TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

579

こんにちは!! BASE株式会社 SRE Group Managerの富塚( @tomy103rider )です。 以前、以下のSREの求人票についての記事を公開してから多くの方とカジュアル面談でお話をさせていただく機会が増え、カジュアル面談の中でも「求人票ブログの記事見ました!」という声をいただき嬉しい限りです。ありがとうございます。 devblog.thebase.in そんなカジュアル面談の中でよくいただく質問があり、今回はその中から最近のSREチームについて、 チームで大事にしていることは? チームの働き方は? チームの業務は? この3つについて紹介したいと思います。 チームで大事にしていることは? 現在SREチームでは、 「信頼性=ユーザの期待値を超え続けること」としてこれを維持し続ける BASEの機能等々の価値を高めるための時間を多く作っていけるようにする が大事であると考えています。 この2つの言葉は、以前チームのミーティングの際に、 「そもそもBASEのSREチームとして今の時点ではどういうチームを目指しているの?」 「BASEのSREは何を大事にしているのか?」 という話題になり、「自分たちが考えるBASEのSRE像とは?」をチーム内で持ち寄って話し合った結果を言語化したものです。ちなみにBASEのSREチームから見たユーザとしては、前者は「オーナーズ(ショップオーナー)とカスタマー(購入者)」の方々、後者は社内でサービスに関わる(SRE含む)人たちを指していて、BASEに関わる全てのユーザ対しSREとしての活動をしていくことが大事であることを表しています。 チームの働き方は? 昨今のコロナ禍ということもありBASEのSREチームにおいても現在は基本的にリモートで業務をしています。そんな中で、日常的なコミュニケーションとしては基本的にはSlackでテキストコミュニケーション、ミーティングはZoomで行っていて、最近はSlackハドルミーティングも活用してより気軽に音声だけでのコミュニケーションを取るようにもしています。 またSREチームの1週間のイベントとしては大きく2つで「朝会」と「SyncSRE」があります。 朝会 - 月〜金 12:00〜12:30 - リリースや大きな作業などのカレンダー確認など - 各自の進捗 - 共有/相談タイム 朝会の様子 朝会はタスクを進めている中での困りごと/相談の時間を特に大事にしていて、より話を掘り下げる必要があるときは30分を超えることもあります。また当然ながら何かあればZoomやハドルミーティングで朝会以外でも都度相談しあっています。 SyncSRE - 隔週金曜 17:00〜 - フリーテーマでチームみんなで会話をする時間 SyncSREは朝会とは異なり、特に議題は決めず主に最近起きたことや感じたことなどをざっくばらんに話してチーム内の認識や意識を合わせる時間にしています。これを始めたのは、リモートワークが始まる前は日常的に自然発生していた会話がリモートワークとなり減ってしまっていることに危機感を感じ、このような時間を意図的に作りました。チーム内での意識/認識をSyncする良い時間になっていると思っていて、実際に1つの事例として最初に書いた「チームで大事にしていることは?」で紹介した2つの言葉を話し合うキッカケになったのはこのSyncSREでの話題からでした。 チームの業務は? ここについては色々ありますが、今回は最近注力している業務や各開発プロジェクトとの関わり方について簡単に紹介したいと思います。 トイル削減/サービスの運用効率化 現在BASEのサービス拡大に伴ってインフラリソースの作成/変更/運用の機会が増え続けており、日常の運用負荷であったり環境の再現性の担保という点において課題があると認識しており、今後の様々な改善を加速していくためにもここを最優先で改善する必要があると考えています。そのため、今更ではありますがInfrastructure as Codeの実践というところでインフラ環境のコード化+断続的な構成レビューのフロー整備を試行錯誤しながら進めているところです。このあたりはある程度形ができた段階でチームのメンバーがブログを書いてくれることを期待しています。 他チームや開発プロジェクトとの関わり 他チームや各開発プロジェクトとの関わりという点では、例えば必要となるAWS上のインフラに関わる業務があります。ただ、単に機能要件を満たすだけの作業をするだけではなく、各開発プロジェクトにおいて機能要件以外に「非機能要件」という観点でもっと事前に考慮しておくべきことはないか?など、リリース後に小さく積み重なっていくと重い課題になりがちな懸念点をSREからも伝えていけるようにしています。 まとめ いかがでしたでしょうか。少しでもBASEのSREチームの雰囲気が伝わればと思います。 さて、引き続きSREチームでは成長するサービスを一緒に支えていっていただける仲間を募集しています! BASEのサービスとSREチームに少し興味が出てきたのでもっと話を聞いてみたい! 似たような試行錯誤をしているのでカジュアルに話してみたい! サービス成長とチームの成長に対して一緒にチャレンジしてみたい! など、少しでも興味を持っていただけましたら、悩み辛みの情報交換でも構いませんし、ここに書ききれなかった話など、ざっくばらんにカジュアル面談でお話ができればと思いますので、お気軽に以下までご連絡いただけると嬉しいです! → カジュアル面談はこちらまで! ← ※参考 open.talentio.com 余談 まとめを書いたあとに余談ですが、最近以下の記事を読みました。 「これでよいのか: SREチームの成熟度評価について考える」 cloud.google.com ここに書かれている「このグループはこの職務をSREと呼ぶために必要な原理と実践の適用において十分なレベルに達しているでしょうか?」という問いに対し、記事内の4つの原理と自分たちがSREとして進めていっている活動を照らし合わせると、方向性としては間違っていないとは思いつつ、実際SREとしての振る舞い/行動ができているかというとまだまだであろうという感想を持ち、この記事については近々SyncSREで改めて話題にしてみようと思います。
アバター
はじめまして、フロントエンドエンジニアの @rry です。 BASE では社内勉強会として「 Frontend Weekly LT 」を毎週開催しています。 今回は Vite 特集で LT をしたので、Frontend Weekly LT の紹介も兼ねて内容を発信していきたいと思います。 Frontend Weekly LT とは BASE はフロントエンドエンジニアが、業務委託の方も合わせて約20名ほどいます(7/16現在)。 普段は別チームに分かれており、プロジェクトもそれぞれ別で開発を進めているのですが、この勉強会ではそんなフロントエンドエンジニアが一堂に集まってわいわい LT するイベントです。 LT イベントは基本的に毎週2名ずつ登壇して、 新しく BASE でリリースされた機能の紹介 いま気になっている&プロダクトで使用している技術について 自己紹介 LT などなど、毎回さまざまな内容で LT しています。 7/16 は Vite 特集の LT 会 Vue.js v-tokyo オンライン Meetup#13 - connpass 最近の v-meetup でも Vite 特集があったりと、最近 Vite がとてもアツいです。 BASE ではまだプロダクションでの利用はありませんが、Vite v2 が Vue や React などライブラリに限らず利用できるというのもあり、期待を寄せているビルドツールです。 今回の LT 登壇者は @rry と @gatchan0807 がそれぞれ発表する流れでした。 次世代のフロントエンドビルドツール Vite 登壇資料はこちらです。 最初は Vite x React で何か作ってみようと思っていたのですが、Vite について調べていくうちにそもそものビルドツールの特徴についてのほうが気になってしまい、結果としてこのような内容の LT になりました。 登壇してみて Vite のドキュメントを改めて全部読んだり、Vite に関係する記事をいくつか読んだりできたので良かったです。 今回参考にした記事: https://speakerdeck.com/kazupon/native-esm-powered-web-dev-build-tool https://speakerdeck.com/kazupon/vue-with-vite https://ics.media/entry/210708/ https://tech.recruit-mp.co.jp/front-end/post-21250/ Frontend Weekly LT はみんなの思い出作り 最近のテックブログで @fshin2000 さんがこんなことを書いていました。 そもそも、オンラインコミュニケーションにおいては何が足りないのだろうか?と思っていたのですが、一言でいうと、 良い思い出を作るイベントが圧倒的に少ない ではないか?という事を考えています。 リモートワークの弊害は職場でのよい思い出が作りにくいこと - BASEプロダクトチームブログ この Frontend Weekly LT は今年の 1/15 から始まった取り組みです。 コロナ禍でコミュニケーションがオンラインになる中、こういったみんなで集まってわいわいできるようなイベントを毎週開催するのは、良い思い出作りになっているのではないかなと考えています。 これからも継続して Frontend Weekly LT を盛り上げていきたいなと思います! 💪 おわりに いつものやつですが、BASE では一緒にフロントエンドを盛り上げていく仲間を募集しています。 Vue や React など新しい技術で EC サービスを支えていきたい!という方のご応募お待ちしております ☺ 1.BASE_フロントエンドエンジニア - BASE株式会社
アバター
Owners Experience Backend Group で Engineering Manager をしています、炭田( @tac_tanden )です。 BASE では決済などの複雑な事業ドメインに立ち向かうために、ドメイン駆動設計(DDD)を使った開発を進めているチームがあります。 そんな中で先日、DDD の有識者として松岡幸一郎( @little_hand_s )さんをお招きし、DDD モデリングデモ会社内で開催しました。 その模様をレポートします。 開催の経緯 BASE では日頃から DDD に関する質問ができるように、松岡さんを Slack チャンネルにお招きし、開発や DDD の勉強会で出てきた困りポイントや疑問を質問させていただいています。 そんな中で「DDD の勉強をしているけれど、モデリングを実際にどうするのか見てみたい」、「自分でモデリングにトライしてみたけれど、このやり方でよいのかいまいちピンとこない」などの声が上がっていました。 なので、実際にモデリングをどうするのかのイメージを掴み、今後 DDD での開発に取り組む際のモデリング作業に活かせるよう、この度 DDD のコーチとして日頃お世話になっている松岡さんをお招きして BASE の決済領域のモデリングデモ会を開催することになりました。 参加者 BASE の決済領域のモデリングということで、エンジニア以外のメンバーにも有意義なのではと思い、Slack の #general にて開催の告知をしました。その甲斐あって、当日はエンジニアメンバー以外にも、プロダクトマネジメントチームやデザイナーチームのメンバーの方たちにも参加していただくことができました! Zoom での開催だったので、気軽に多くの方が参加できたのもあり、夜の遅い時間だったのですが当日は 40 名近くの方に参加いただきました。企画者として嬉しかったですね。ありがとうございました。 当日 デモ会当日は、BASE からは長年決済領域の開発に携わってきた Payment Group のエンジニアメンバーがドメインエキスパートとして参加しました。 ドメインエキスパートの知識をもとに、松岡さんが画面上にモデリングをしていくのですが、複雑だと考えていた決済領域の関係が次々に明らかになっていく過程は壮観でした。 当日のデモによって出来上がったドメインモデル図をブログ上でお見せできなのは残念ですが、各概念の関係性が分かりやすく表現されていて、これがあれば実装に入れそうだなと自然に思えるような完成度で驚きました。 また、モデリングの過程も、具体的にどういう手順でモデリングをしているくのかステップバイステップで説明/デモをしていただいたので、終わったあとには「自分でもできそう」と思えるようになりました。 2 時間という短い時間でしたが、松岡さんやドメインエキスパートの方の協力もあり、とても有意義な時間になりました。ありがとうございました。 参加者の感想 エンジニアメンバーのお二人の感想をお届けします。 ドメインエキスパートとして参加 今回ドメインエキスパートとして参加した Product Dev Division 所属の島田です。 脳内にあった抽象的な全体像をドメインモデリングを行ったことでより具体的にできました。 そのおかげで概念を言語化したり図にすることができ自分以外の人でも共通認識できるようになりました。 共通認識ができるようになるとドメインに対して色々な意見が出るようになりドメインの精度が上がったように思いました。 聴講者として参加 同じくProduct Dev Divisionに所属している 大津 です。 今回のドメインモデリングを見て、「ドメインモデリング自体がドメインを理解する行為である」ということが腹落ちできました! 当日の Slack の一連の流れを見ても今回のテーマである BASE の決済に対して理解を深められた参加者が多く、現状の認識のすり合わせとしてドメインモデリングが有効ではないかと感じました! 参加者で記念撮影 最後に 改めて、モデリングデモ会を通じてモデリングとはこんな風にやるのかと具体的なイメージを持つことができました。講師としてモデリングのデモを行っていただいた松岡さん、本当にありがとうございました! BASE では複雑な事業領域に正面からタックルしてユーザーのためにサービスを一緒に開発してくれる仲間を募集しています! まずはカジュアル面談でお話できることを楽しみにしております!よろしくお願いします! open.talentio.com open.talentio.com
アバター
こんにちは。BASEの藤川です。 緊急事態宣言も続く状況下で、当社もリモートワーク(Work From Home)中心の仕事の進め方をしています。ネット系企業は、幸いにしてVPN、Slack、GitHubやドキュメント管理ツール、その他仕事に必要なSaaSやZOOMがオンライン化しているため仕事の作業そのものは、それほど違和感なく自宅からでもできているのではないかと思います。 でも、仕事というのは作業だけで済むものではありません。業績を上げるための作業を生み出す活動を始めとする考えるタイミングであったり、不確実なものを埋めていくためにお互い議論するタイミングなど、曖昧なプロセスの先に、決定をして作業の的を絞り込んでいくプロセスが不可欠で、ここで複数人のチームワークが不可欠です。 今、一緒に仕事をしている仲間においては、コロナ以前から社内で人間関係を構築済みの人と、コロナ禍においてリモートだけで人間関係を構築している人と二分されています。当社の採用活動でも緊急事態宣言が出てからは、オンラインコミュニケーションのみで内定を出しておりますた、他部署の方などで最短で退職されてしまうと、せっかくそこまで大きくない会社にも関わらず一度も会うことなく去ってしまう人もでてくる可能性があるという状況です。 そのため一日でも長くいただくために、入社される方へのオンボーディングおよび心理的安全性、チームの信頼関係の構築に対するケアは、マネジメントにおいても最重要項目として常に議論をしています。 そもそも、オンラインコミュニケーションにおいては何が足りないのだろうか?と思っていたのですが、一言でいうと、 良い思い出を作るイベントが圧倒的に少ない ではないか?という事を考えています。 オンラインコミュニケーションで起きていたことが職場でも起きる時代 僕は高校一年生の頃からパソコン通信をやっており、オンラインのコミュニケーション経験は長いです。オンラインではパソコン通信でも、その後のインターネットの世界においても、新しいネットワークが大きくなる時に、必ず見かけてきた風景として、 オフ会によって人間関係の距離が変わる オフ会を開催して、誰かと誰かが仲良くなって重鎮化していき、ある種の既得権みたいなのが生まれ、また新しい人が入ってきて新しい関係性が生まれる、常に人間関係のシャッフルが生まれ、コミュニティの歴史が長くなってくると、必ず起きる問題が、 新参者が入れない問題 というのが起きます。 よく組織設計の話で、ダンパー数というものが語られることがあります。 ・5~9人=「社会集団(クリーク)」…最も親しい友人やパートナーの数 ・12~15人=「シンパシー・グループ」…ほぼどのような状況下でも心から信頼できる人の数 ・30~50人=「一団(バンド)」…危険な国を安全に往来できる小さな団体 ・150人=「フレンドシップ・グループ」…共同体の中で一緒に暮らすのに最適な人数 ・500人=「部族・種族(トライブ)」…出会うと会釈する程度の顔見知りの人数 ・1500人=「共同体(コミュニティ)」…人間の長期記憶の情報数の限界、頭の中で名前と顔が一致する人数 出典:チームを成功へ導く魔法の数字たち 「7」「30~50」「150」 https://www.sankeibiz.jp/smp/workstyle/news/190401/wsa1904010700003-s4.htm ダンパー数は組織の人数規模を考慮する時に参考にする数字なのですが、肌感覚としても一致はしていて、小規模のコミュニティにおいては、お互いの距離感は均等に短く、何もしなくても親密になりやすいです。これが人数が30〜50人であるとか、100人を超えてきて、なおかつ参加したタイミングがバラバラともなれば、相互の距離感が不均衡になりがちです、そして、不均衡が生まれれば生まれるほど、既に存在する人間関係や情報の非対称性の中で孤独感を感じる人が出てきます。 特に何十人の規模のオフ会ともなると、人脈を持つものと持たざるものの差は大きく、また、それを切り開くだけのコミュ力や、そのコミュニティで名を売るんだぞ!という根性を持ち合わせていないと、家に帰った後にどっと疲れて、このコミュニティは内輪ばっかりで、疎外感に満ちあふれていたなどの言説が駆け巡ってトラブルになることを、この目で散々見てきました。 こう言ったことが、SlackとZOOM主体のリモートワークの働き方においても適用されるのではないかと考えています。 以前入社した100人ぐらいの会社が、実は1000人以上のグループの一社だったということを入社日に参加したグループ全体の定例会議で知ることになり、なんか大企業に来ちゃったなぁということに疎外感を得たことがあります。自分のアクセス可能なネットワークと、手の届かない全体感の規模感のギャップというのはストレスを感じるという経験をしたことがあります。 そのような会議の後に、心の支えになるのは隣の席の同僚だったり、同期入社した人だったり、即座に小さなコミュニティを構築し、雑談をすることが心のクッションになったりします。 しかし、コロナ禍においてフルリモートのような状態で働くと、自分の部屋でパソコンを閉じても、隣の人と雑談をして共通見解などの意見を交わして、息を抜く所がありません。まして真面目に自粛していて、外にも全く出歩かない独身の方だと、プライベートのネットワークが補完することもできず、どうにも仕事モードを発散できないような人もいるのではないでしょうか。 職場で「何も考えずとも」得られていたもの そういう時に起こりうる疎外感や寂しさと言った無用な感情を埋めてくれる存在は、同じ部署のメンバーやプロジェクトのメンバーなのですが、まだ人間関係ができていないと難しいですし、仕事の中で培われるというよりは、一緒にランチに行く時に路上やエレベーターで会話するシーンや、タバコ部屋の議論とか、仕事の隙間に培われていいた何かというのは確実にあったような気がします。 このようなタイミングで培われるものを「遊び」と定義してみます。 仕事は何かの目的を持って、無駄なく最適化された活動をすることが生産性が高いと表現されます。それが有能な会社員の行動であれば、無駄を許容することはその逆になるので、一人でやるのは難しいです。 Slackで雑談するにしても、相手にしてもらえる人がいればこそ。まして、一緒にタバコを吸いに行くとか、コーヒーを飲みながら雑談するとか、仕事終わりに飲みに行くなど、最適化された活動以外のものは、概ね気の合う数人レベルのマイクロコミュニティとして行われるものではないでしょうか。 ZOOMの活用で、会議室の移動は一瞬でできるようになり、仕事そのものは大変効率化されました。時間ギリギリまで会議していても、10秒後には次の会議室に入ることができ、我々は仕事マシンとして仕事をし続けられるようになりました。その一方で、移動のタイミングで生まれていた、アジェンダのない会話 = 「遊び」は消え去りました。そこで副次的に得られていた相手との心理的安全性や、仕事をする意義みたいなものを学ぶために、実は大切なものではなかったのか?というのが仮設になります。 更に、この会社で働いてよかったこと、自分の成長につながったなぁという手応えは、なんだかんだと新機能のリリースイベントであったり、会議室での議論でのお互いの顔の表情であったり、ホワイトボードに書いたものという手触り感、プロジェクトの打ち上げでの会話、メンバーの笑顔など視覚情報によって脳内に記憶が定着され、それ故に実感を得られていたのはないか?と思うことがあります。 この思い出こそが、10年後20年後に振り返った時に脳の中に残っていることが人生にとって大切なのではないか? 残念ながら、これはZOOM上のバーチャル背景の集合体では得られにくいのではないでしょうか?この1〜2年で働いていたことは人生の記憶やストックとして積み上がるのだろうか。 このような実感を得られるイベントを、オンライン主体の変わりなき日常風景の中で、どうすればみんなが実感できるのだろうなぁというのを常に考えています。何か工夫をされている会社さんやマネージャの方がいたら是非教えてほしいです。 オンラインで「仕事の思い出」は生み出せるのか コロナ以前であれば、合宿、社内イベント、飲み会などは、それらを補完し、思い出を業績に変えていく装置だったように思えます。打算的に言うと、あえて遊びを作ることで、仕事の生産性を生み出す装置であったという見方ができます。 組織を語る表現において、歯車というのはネガティブな表現とされますが、歯車と歯車の間には遊びは必要だし、それらが適切に回って全体が駆動されなければ、そもそも良いプロダクトは作れないのです。 こう言った遊びの構築を、それをオンラインでは補完できるのだろうか? 間違っても、その処方箋はZOOM飲みではないなとは個人的な感覚としてはあるのですが、無駄や行間をあえて作って、そこから何かを生み出していくというのは、全員に高度なネットリテラシーが問われるようにも思えます。 もしかしたらヒントは、ツイキャスや17LIVEのようなライブ配信の関係性にあるのかもと思わなくもないのですが、とりあえず今回は、Youtubeの動画を作ってみて、こういう考えを深めるきっかけを作ってみました。 こちらの動画は、採用候補者の方にBASEを検討する際に見ていただきたい動画として、BASE社におけるPHPの現在・過去・未来というタイトルで作っています。今回は、当社の社員として活躍いただいているyakkunへのインタビューという形で収録したのですが、更に、最後のサイドトークに今回のリモートワークについての話にも言及しています。 www.youtube.com 今後も当社のメンバーとこのような会話を公開用のコンテンツとして作っていくことで、1on1とはまた違った思い出が作れないかと思って作っています。全社員できるかな?! 今回の内容は「最近は他の言語がメインにしてきた人がBASE社でPHPを使う気持ち」という長く続くことに成功したサービスに携わる人達であれば共通の経験がある話をしています。 ラジオ感覚で聞ける話ですので、もしよかったら是非見てみてください。
アバター
こんにちは。Product Dev Divisionに所属している 大津 です。 PHPカンファレンス2021 のトーク募集が始まりましたね。 僭越ながら、私は過去にPHPカンファレンス 2019 と 2020 に2回登壇したことがあり、今年もトーク応募をしてカンファレンスを盛り上げていこうと思っています! そこで今後登壇する人のお力に少しでもなればと思い、トークするまでの流れを私なりにまとめてみました。 ぜひ参考にしてみてください! 話したい内容を軽く決める まずは話したい内容をなんとなく決めます。 これは普段から思っていることでも良いですし、社内向けドキュメントとして書いた内容でも良いです。 はたまた、誰も話してなさそうなテーマだから自分が話してみよう!みたいな動機でも良いです。 大事なのは自分がトーク準備の過程を楽しめそうなテーマであるか?ということです。 ターゲットを決める どんな人に向けてトークをするのか具体化しましょう。 例えばWebエンジニアがターゲットだとしても、初心者〜上級者、フロントエンド〜バックエンドなど色んなセグメントがあります。 私はトークを聴く人のペルソナのようなものを、なんとなく作っています。 どのくらいの知識を持っているか どんな課題感を持っているか 自分が話したいことを知ることでどんな状態になるか 例えば私が過去に話した PHPerのためのテストコード入門 では、以下のようなペルソナを考えていました。 参考にしてみてください。 ex.「PHPerのためのテストコード入門」ターゲット1 職業プログラマなりたての新人エンジニア テストコードを言われるがままに描き始めているが、テストコードを書くモチベーションが湧かない 発表を聞いてテストコードを書く理由を知ることで、意味があることをしているんだとモチベーションを高めたい ex.「PHPerのためのテストコード入門」ターゲット2 そこそこコードを書いているエンジニア とりあえずテストコードを書け!と言っているが、何故テストコードを書かなければいけないんですか?と言われると言葉に詰まる 発表を聞いてテストコードを書く意義を伝えられるようになりたい ターゲットに合わせた登壇内容を決める ターゲットが決まったら、ターゲットの達成したい目的を果たせるような登壇内容にしてみましょう。 例えばターゲットが職業プログラマなりたての新人エンジニアであれば、一部のマニアしか分からない内容を書く必要ありません。 逆にベテラン向けの話であれば、初歩的なことはある程度省いても良いでしょう。 この段階で、話すこと話さないことが決まるので、トークの最初に話すこと話さないことをスライドに載せても良いかもしれません。 プロポーザルを出す トーク内容が決まったらプロポーザルを出しましょう。 プロポーザルとは、自分が話すトークの概要です。下記のようなトークの募集が始まった際に、プロポーザルを出します。 PHPerKaigi 2021 PHPカンファレンス 2021 例として僕が今までに出したプロポーザルのリンクをいくつか貼ります。 拙筆ではございますが、どれも採択されたプロポーザルです。参考にしてみてください。 テストピラミッドを意識したテストコード実装戦略 PHPerのためのテストコード入門 リーダブルコミットのすゝめ よくある採択されないプロポーザルについては、こちらのスライドが良くまとまっているので読むと良いでしょう。 プロポーザル・アンチパターン - Speaker Deck プロポーザルの構成についてはこちらのスライドが参考になります。 登壇の可能性をあげる!カンファレンスプロポーザルの書き方のススメ - builderscon::blog また、過去に採択されたトークを見て分析するのも良いでしょう。 トークの採択をするのはカンファレンススタッフですが、スタッフも人間である以上カンファレンスが盛り上がるかということやそれぞれの好みを軸に選ぶはずです。 例えば、10月に開催されるPHPカンファレンス 2021では、採択に関するルールで 「採択者はPHPプログラマー歴が長い人から2年程度の短い人まで、過去登壇してくれた方を中心にお願いしております。」 と書いてあります。 余裕があればこの辺りも見てみましょう。 トーク内容と構成を練る 無事採択されましたら、いよいよトークの内容を考えていきます。 私からトーク内容を練る上で、重要な点をいくつかまとめます。 十分に時間を割こう 目安ですが、私はスライド作成や発表練習などトークの準備時間を最低でもトークする時間 × 120分用意しています。 例えば、25分の発表であれば、25分 × 120分で50時間準備時間に費やしています。 ※LTはこの限りではありません 気をつけて欲しいのは、ここにはデモなどの環境を用意する時間は含まれていません。 作成する内容に合わせて、追加で時間を設けると良いでしょう。 想定外を考慮する 資料を作っていると、以下のようなことに陥ることがあります。 プロポーザルに書いた(想定していた)内容と違う点を見つけてしまった 調べれば調べるほどよく分からなくなり、何もわからんという状態になる 今伝えたいことが、今回のトークの時間だけでは足りないことに気づいた この辺りはプロポーザルを書いた時点ではわからない不確定要素です。 できるだけ不確定要素は取り除いた方が良いものの、想定外はあるという前提で資料作成のスケジューリングをした方があとあと自分が楽になるかと思います。 1回トークのレビューをしてもらう 資料がある程度完成したら、一度他の人の前でトークしてFBをもらいましょう。 誤字脱字などがないか スライドに書いてある内容が本当に正しいか 聞いている人が理解しやすいような構成、流れになっているか トークの時間に間に合う分量か 喋りのスピードは適切か 一番良いのは、トークのターゲットにあたる人と今回のトークテーマについて詳しい人へレビューをお願いすることです。 企業に所属している人は、スライドのレビューもしてもらう スライドは、トーク中だけでなくトーク後のことも考えて作成しなければなりません。 基本的にトークで使ったスライドは、発表後も多くの人の目に触れます。 カンファレンスがトークの録画をしていて、後日カンファレンスに参加していない人や別のトークを聴講していて見れなかった人が動画を見る 公開したスライドのURLが拡散されて、カンファレンスに参加していない人がスライドを見る 内容が企業にとって不都合がないか、広報へ確認の依頼を出しましょう。 また企業によっては、指定したスライドテーマを使うよう指示されることもあるかと思います。 その際には、作成したスライドが企業のブランドポリシーに反していないかのチェックをお願いしましょう。 カンファレンス当日にトークをする いよいよカンファレンス当日です。 トークをするにあたって、考えておきたいのは質疑応答です。 事前に「こんな質問が出そうだから、その時はこう回答しよう」とかある程度考えておくと良いでしょう。 また、答えられない質問やいわゆるマサカリが来たときは、素直にわからないということを伝えましょう。 この辺りの話は、以下のスライドへ譲ります。 LT・登壇でマサカリに備えるたった一つの心得 トーク終了後 登壇お疲れ様でした!トーク後にぜひやってほしいことが2点あります! スライド共有サービスにアップロードしよう トークで使ったスライドを、slideshareやspeakerdeckなどでアップロードしましょう。 スライドのURLをツイート等すると、トークを聞いてくれた人だけでなくトークを聞いていない人の目にも触れるチャンスになります。 参考:大津のspeakerdeck また、自分が登壇した内容のスライド一覧は、自分のポートフォリオの一部として活用できます。 ぜひ自身のブランディングのためにもアップロードしていきましょう。 懇親会にはできるだけ参加しましょう 懇親会では、スピーカーは比較的声をかけてもらえやすい立場にあります。 話す内容はトークのFBだったり、質疑応答だったり…トークした内容について貴重なご意見をもらえるチャンスでもありますので積極的に参加しましょう。 ただし体調不良だったり先約がある場合は、そちらを優先しましょう!(無理せず!) 前日まで徹夜でスライドを作っていたりすると、この懇親会がしんどくなり良いチャンスを逃してしまうかもしれません。 懇親会の参加も含めて、余裕を持ったスライド作りのスケジューリングをしましょう。 最後に トークするまでの流れについてご紹介しました! ちなみに弊社のSlackでは、外部カンファレンスへの登壇についてワイワイする #iikanji-conference-toudan というチャンネルがあります。 今後も外部カンファレンスへの登壇に向けてワイワイしていきます!
アバター
この3ヶ月で行ったBDIの内容を紹介します こんにちは、デザイナーの河越です。 BASEのデザインチームが月2回行っている社内勉強会「BDI」。リモートワークになってからも継続的に開催しています♪ 今回は4月~6月に開催したBDIの内容をご紹介したいと思います! BDIとは? 『BDI』は「BASE Design Inspiration」の略。 2018年の秋頃から活動している、デザイナーがやりたいことを持ち寄って、 デザインに関する幅広い知見をみんなで楽しく学ぶことを目的とした任意参加の社内勉強会です。 BASEのデザイナーであれば、デザイナーだけでなく誰でも参加することができます。 Inspirationの名の通り新たなひらめきにつながる新しいトピックを取り上げることも多くあります。 1月~3月の振り返りはこちら devblog.thebase.in BASEのデザイナーがどんな活動をしているのか気になっている方に読んでいただきたいのはもちろん、 リモート環境での社内勉強会のネタ探しにもぜひご活用くださいね! 4月 発想メソッド「マンダラート」でアイディアを湧き出させる会 4月最初のBDIは、アイディア出しのフレームワーク「マンダラート」を実践で使う企画からスタートしました。 マンダラートとは、一つのキーワードに対して8つのマス目を作り、そのマス目一つ一つにアイデアを書き込むことで目標を達成するためのアイディアを見つける手法。 あの大谷翔平選手も目標設定のために使っていて、現在注目が集まっているフレームワークの一つだそうです⚾️(私は初めて聞きました!) BDI用にアレンジしたマンダラートでは、デザインリサーチPJメンバーのショップオーナーインタビューの報告をもとに、マンダラートを使って『オーナーズが持っている課題に対してどのような解決策があるのか』をアイディア出ししました! デザイナー以外も参加して楽しめるように、Miroを使ってワイワイと。 最終的には出てきたアイディアを発表し合うことで、オーナーズへの知見が溜まるだけでなくメンバーへの理解も深まった会でした。 社内で「ものづくり」をしている人のお話を聞くLT会 「今年のGWもおうちにいる時間が長そうだね」ということで、ゴールデンウィーク直前に実施した社内LT会。 おうち時間を楽しく過ごすヒントをもらうべく、社内でものづくりをしている3名の方にお話してもらいました! 3名はそれぞれ、ボードゲーム・ペーパーアイテム・家具を作っていて、 なぜものづくりをしているのか どうやって作っているのか 今後の展望は? と、いう角度からのお話を聞きました。 ものづくりへの熱い思いのこもったLTで「売上をやみくもに上げるより、自分の作品を好きになってもらいたい」「角度にこだわり抜いた座椅子を作って自分の生活を豊かにしたい」といった色々な価値観を知ることができました。 今回の3名だけでなく、オーナーさんが持っている考えとも通じるところがありそうで、ユーザーへの理解が深まる会となりました。 何よりLTをしてくれた3名のこだわりを知れて、作品のストーリーをより深く知ることができ、それぞれの作品のファンになりました...! GWでものづくりをしてみようかな、と考える方が増えたBDIになったかなと思います! 5月 Ownersになってみよう第2弾! ブランドのロゴを作ってみよう🎨 3月に開催した『Ownersになってみよう企画』をシリーズ化し、第1段で制作したブランドのコンセプトを元に、ロゴ作りのワークショップをやってみました。 ロゴはどんなプロセスで作られて、どんな所で使われるかのLT ロゴが使われるシーンをブレストしながら、コンセプトのブラッシュアップ ロゴのラフ案をたくさん作成するモクモク会 2回分のBDIの時間を使って、架空のブランドのロゴやシンボルマークを作成しました。 丁寧なLTがロゴ作成未経験者にもわかりやすい内容で、ブランディングの大切さ/大変さを実感できる回となりました。 フォント決めやロゴタイプのモチーフ決めには議論が活発化して2時間で納めるのは大変でしたが、複数人で集まって一つのMiroボードにモクモク作業する工程で「みんなでデザインするって楽しいな」と改めて感じました。 次回以降の『オーナーズになってみよう企画』ではパッケージや商品のデザインを進めていきます! 6月 「ページ追加 App」リリースまでのあれこれを聞く座談会 5月末にリリースされた「ページ追加 App」について、PJメンバーにリリースまでの道のりを語ってもらう座談会を開催しました。 https://baseu.jp/20203 PJを担当したPMやデザイナーからリリース前に悩んだことの振り返りや、リリース後のユーザーからの反応を聞きました。 機能を利用しているショップの事例を紹介するパートでは、想像以上に工夫して使ってくださるショップを知れてみんなで盛り上がりました! 事前に全社からPJメンバーに聞きたいことを募集して質問を選んでおいたので、より内容の濃いウラバナシを聞くことができたし、発表するメンバーの負担も少なくできたのではないかと思います! デザインツール「Figma」を使いこなそう!!!〜基礎編〜 BASEのデザインチームでも利用しているデザインツール「Figma」。 デザイナー以外のメンバーに向けて、Figmaの基本的な使い方や、Auto Layoutの組み方を学ぶ実践型のワークショップを開催しました。 Figmaマスターの渡邊さんがFigma特有の「Frame」という概念や、「Auto Layout」の組み方を丁寧に説明してくださいました。エンジニアからは「Flexboxに似ていてわかりやすい!」という声も。 資料作成にFigmaを使っているPMや、Figmaでデータを受け取るエンジニア陣から大好評の企画となりました! お互いの仕事についての理解が深まることで、よりスムーズなコミュニケーションが取れるようになりそうです◎ まとめ 月2回ペースで開催しているBDI。4~6月はLTや座談会を多く取り入れてみました。 結果としてデザイナー以外のメンバーもROM専で参加しやすくなり、参加者が徐々に増えて大盛況となりました。 PJやチームが違っても、こういった機会でコミュニケーションが取れる機会があるのは良いなと感じています。また、実況コメントを流しながらのLTなど、オンラインでもLT会を盛り上げる知見も溜まってきたので、参考になったら嬉しいです。 これからも楽しくてためになる勉強会を開催していきます。7月以降もお楽しみに!
アバター
こんにちは!! BASE BANK 株式会社 Dev Division にてSoftware Developerをしている永野( @glassmonkey )です。 普段はGo/Python/PHPを主に生業に開発・運用から何でもござれの精神でフルサイクルエンジニアをしています。 現在、自分たちのプロダクトである YELL BANK の分析基盤を構築しています。 その際に、BigQueryで扱っているデータをGoogle App Script(以下GAS)、Googleスプレッドシートとデータポータルで簡易CRMをビジネスサイドのメンバーである猪瀬 ( @Masahiro_Inose )と協力して作ったのでそのご紹介です。 いざ実施してみるとハマってる点もそこそこ多く、意外とGASやBigQueryの連携している情報が少なかったので、誰かの助けになれば幸いです。 thebase.in 簡易CRMツールをスプレッドシートベースで作成した背景 弊社で開発している「YELL BANK」では、プロダクト改善のためにユーザーインタビューなど定性情報を顧客に直接聞く形で得ていくスタイルを積極的に実践しています。 その記録を一元管理する場所がなかったこともあり、CRMツールが必要な状況でした。 CRMツールとしてはSalesforceなどが有名ではありますが、様々なデータがBigQueryに集約をする状況でありました。 今回は以下の2点で慣れているスプレッドシートベースのシステムの採用をしました。 プロダクトとして仮説検証フェーズなので見たい内容は定まってないこと データ分析を主として実施しているビジネスサイドのメンバーが自身でカスタマイズ容易なこと 謝辞 一部改変をしておりますが、コードなどは 【GAS/BigQuery】日付に応じて異なるクエリを実行 から大部分を引用させていただきました。 この場を借りてお礼を申し上げます。 構成 GAS周りの基盤を私が作成して、集計のスプレッドシート構成からダッシュボードの構成まではビジネスサイドのメンバーで分担して作成しました。 簡易CRMの構成図 準備 GASのメンテは基本的には普段コードを書かないビジネスサイドのメンバーが扱うので、都度追加するコードはシンプルである必要がありました。 そこで、OAuth認証に関連するところやAPI経由でBigQueryを実行する処理は共通処理として切り出すことにしました。 GCPプロジェクトの準備 GASからBigQueryをOAuth経由で実行するので、その設定が必要です。 今回は内部的に利用するので、UserTypeは内部を選びます。 0auth同意画面 アプリ名とサポートメールとデベロッパーの連絡先情報を入力します。内部用なので適当で良いです。 Oauth設定画面(メールアドレスなど) BigQueryのAPIが有効になっているか確認します。 このあとAPIを利用するので、もし無効になっていたら有効にしておきましょう。 BigQuery API確認画面 GASの共通プロジェクトの設定 まず最初に共通GASプロジェクト設定します。 GASのメンテは基本的には普段コードを書かないビジネスサイドのメンバーが扱うので、都度追加するコードはシンプルである必要がありました。 API経由でBigQueryを実行する処理は共通処理として切り出すことにしました。 認証用のOAuth設定を予め共通プロジェクトとの連携で行っておくことで、ビジネスサイドのメンバーが権限周りを考慮せずにすむようにもしました。 各種画面のスクリーンショットは2021年6月時点のものになります。 1. 新しいプロジェクトから共通プロジェクトを作成する 新しいプロジェクトを作成します。 新しいプロジェクトその すると下記のように無題のGASのエディターが開きます。 GAS エディター 2. ライブラリを追加する GASからスプレッドシートのアクセスを簡易的にしてくれる、gas-underscoreを入れます。 詳細は simula-innovation/gas-underscore を参照ください。 READMEから"プロジェクトキー"である M3i7wmUA_5n0NSEaa6NnNqOBao7QLBR4j を使うと良いとありますが、新エディターの場合使えませんので注意ください。 github.com 旧エディターを利用する場合 (非推奨) Add a libraryから"プロジェクトキー"である M3i7wmUA_5n0NSEaa6NnNqOBao7QLBR4j を入力します。 バージョンは最新の2を選びます。 なお、この方法はいずれ廃止になる可能性があるのでおすすめはしません。 新エディターを使う場合 左カラムのライブラリの + をクリック gas-underscoreの"スクリプトID"は 1PcEHcGVC1njZd8SfXtmgQk19djwVd2GrrW1gd7U5hNk033tzi6IUvIAV なのでそれを入力する。調べ方は後述します。 ] 調べ方としては、旧エディターで得た情報をベースにはなりますが、インストールするとGASのマニフェストファイルからスクリプトIDがわかるのでそれを利用します。 旧エディタの場合は 表示 > マニフェストファイルを表示 を選択すると見ることができます。 他にもプロジェクトキーしかわからないライブラリがある場合はこれを使うとわかるので便利です。 GASマニフェストファイルの一部 余談ですが、公式のリポのREADMEには一応 Pull Request を出しました。古いリポジトリなのでメンテされてない可能性が高いので、forkした 私のリポジトリ も記載しておきます。 github.com 3. サービスにBigQueryを追加する 今回はBigQueryのAPIを利用するので追加します。 1. 左カラムのサービスの + をクリック 2. BigQueryを選びます。バージョンは2021年6月時点で最新のv2を選択しています。 4. GCPプロジェクトと連携させる 歯車マークのアイコンからスクリプトの設定に遷移します。 プロジェクトを変更からGCPプロジェクトの連携設定を追加します。 ここでうまくいかない場合は、連携先のGCPプロジェクトのOAuth認証が正しく設定できていない可能性があります。 ここのプロジェクト番号とはGCPのダッシュボードのプロジェクト情報で確認できます。 プロジェクト情報 5. GASの共通コードを用意する。 集約したいスプレッドシートのID、シートの名前、実行したいクエリは都度変わるとのでそこを外部から渡せるように共通処理を書いておきます。 共通処理内部でBigQueryへのAPIリクエストを秘匿するようにしていので、ここはビジネスサイドのメンバーは意識しなくて済むようしておきます。 プロジェクト番号のところは、前述したOAuth設定したBigQueryを実行するgcpプロジェクト番号に読み替えください。 function run ( spreadsheetId , sheetName , query ) { /** * コンフィグ設定 **/ const projectId = 'プロジェクト番号' ; // 出力先シートSpreadsheet const sheet = SpreadsheetApp . openById ( spreadsheetId ) ; //出力するシート名 const workSpace = sheet . getSheetByName ( sheetName ) ; const request = { query : query , useLegacySql : false } ; execBigQuery ( projectId , workSpace , request ) ; } ; function execBigQuery ( projectId , ws , request ) { let queryResults = BigQuery . Jobs . query ( request , projectId ) ; const jobId = queryResults . jobReference . jobId ; //BigQuery実行 queryResults = checkOnQueryJobStatus ( projectId , queryResults , jobId ) ; //結果を取り出す const rows = getAllResults ( projectId , queryResults , jobId ) ; //結果を書き出す outputToSpreadsheet ( rows , ws , queryResults ) ; } function checkOnQueryJobStatus ( projectId , queryResults , jobId ) { var sleepTimeMs = 500 ; while ( ! queryResults . jobComplete ) { Utilities . sleep ( sleepTimeMs ) ; sleepTimeMs *= 2 ; queryResults = BigQuery . Jobs . getQueryResults ( projectId , jobId ) ; } return queryResults ; } // Get all the rows of results. function getAllResults ( projectId , queryResults , jobId ) { var rows = queryResults . rows ; while ( queryResults . pageToken ) { queryResults = BigQuery . Jobs . getQueryResults ( projectId , jobId , { pageToken : queryResults . pageToken }) ; rows = rows . concat ( queryResults . rows ) ; } return rows ; } // Clear sheet data and output BigQuery results to sheet. function outputToSpreadsheet ( rows , ws , queryResults ) { if ( rows ) { ws . clear () ; // Append the headers and return header cols. const headers = appendHeader ( ws , queryResults ) ; // Append the results and return all spreadsheet data. const data = appendResults ( rows , headers ) ; ws . getRange ( 2 , 1 , rows . length , headers . length ) . setValues ( data ) ; return data ; } else { Logger . log ( 'No rows returned.' ) ; } } function appendHeader ( ws , queryResults ) { const headers = queryResults . schema . fields . map ( function ( field ) { return field . name ; }) ; ws . appendRow ( headers ) ; return headers ; } function appendResults ( rows , headers ) { const data = new Array ( rows . length ) ; for ( var i = 0 ; i < rows . length ; i ++ ) { var cols = rows [ i ] . f ; data [ i ] = new Array ( cols . length ) ; for ( var j = 0 ; j < cols . length ; j ++ ) { data [ i ][ j ] = cols [ j ] . v ; } } return data ; } 詳細は後ほど記載しますが、各種呼び出しは以下のような形になります。 実際にシートに集計クエリを書くビジネスサイドのメンバーとしては、集計クエリをどこのシートに出力するかのみ考慮すれば良いようにしました。 function example (){ //シートID, シート名, クエリを指定する 共通プロジェクトの名前 . run ( SpreadsheetApp . getActiveSpreadsheet () . getId () , "集計結果シート" , `何か集計用SQL` ) } シートごとの設定編 共通プロジェクトのスクリプトIDをメモ 集計シートからBigQueryを実行できるように共通プロジェクト呼び出しをします。 共通プロジェクトのスクリプトIDをメモします。 スプレッドシートに紐づくGASを開く 集約用のデータを扱うスプレッドシートを用意します。 スプレッドシートの ツール > スクリプトエディタ からシートと紐づくGASを用意します。 スプレッドシートと紐付いているGASはコンテナにシート名の記載があるので、念の為確認するといいでしょう。 ライブラリの+から共通プロジェクトを呼び出しをします。 ここで呼び出しをすることで 共通プロジェクトの名前.run で共通処理を呼び出しできるようになります。 BigQueryを共通処理と同様にサービス設定に記載します。 呼び出し関数を用意する 何かの集計結果のSQLを集計結果シートに出力する場合は下記のように書くことで実行できます。 基本的にはここの実装はビジネスサイドのメンバーが書くことになるので、 集計したいクエリを書くことに専念できるような呼び出し方としました。 また、関数として切り出すとボタン配置などやデバッグがしやすいのでおすすめです。 function example (){ //シートID, シート名, クエリを指定する 共通プロジェクトの名前 . run ( SpreadsheetApp . getActiveSpreadsheet () . getId () , "集計結果シート" , `何か集計用SQL` ) } 呼び出し用のボタンを用意する gasの実行には色々方法がありますが、今回は不要な集計処理を走らせたくなかったのと必要なときに実行できるようにボタンを用意する方式にしました。 適当にボタンとなる図形を 挿入 > 図形描画 から配置します。 図形右上の︙からスクリプトの割当を選びます。 用意した関数名を入力します。 これが完了すると用意した図形を押下すると集計クエリが出力されるようになります。 結果と使用感 Google Data Studioの連携部分に関してはGoogle Data Studioで何を出すかで変わってくるので割愛します。 結果としてビジネスサイドのメンバーが作ってくれたダッシュボードの一部ですがご紹介します。 架空の数字ですが、イメージは伝わると思います。 簡易CRMのイメージ図 当初の目的通り、ユーザーインタビューなどの定性情報を得にいく時にも、顧客とのコミュニケーション記録を有効に活用できるようになったとのことでした。 過去にコミュニケーションしたショップがファネルの上位のフェーズになったといった変化を発見できるようになったとのことです。 感想 データ分析もより専門性のあるビジネスサイドのメンバーに委譲する流れができた点も良かったと考えています。このおかげで全体としては2週間ほどでCRMのシステムを構築できました。 BigQueryをGAS経由で呼び出せるといろいろ楽なので、誰かの助けになれば幸いです。 そのようなスピード感を持って一緒に開発するアプリケーションエンジニアやカスタマーサクセスのメンバーを募集中です。 open.talentio.com open.talentio.com
アバター
Owners Experience Backend Group で Engineering Manager をしています、炭田( @tac_tanden )です。2021 年 4 月末に『ユニコーン企業のひみつ ―Spotify で学んだソフトウェアづくりと働き方』という本が発売されました。 自分含め、多くのメンバーが「読んでみたい!」と話題になっていた中で、翻訳者の角谷さまのご厚意で献本いただいたので、遅ればせながらレビュー記事を書かせていただきます。 🦄 テック企業のみなさま、テックブログの記事の候補に一冊いかがでしょうか!!!q 🙏 » 🦄 書籍『ユニコーン企業のひみつ』を貴社テックブログでレビューしていただける企業さまを募集します https://t.co/TdfL58TwpS — Kakutani Shintaro (@kakutani) April 7, 2021 著者について この本の著者を見たとき驚きました。 "Jonathan Rasmusson(ジョナサン・ラスマセン)"。自分が好きで何度も読んだ『アジャイルサムライ−達人開発者への道−』の著者の、あのジョナサン・ラスマセンさんが書いた、Spotify での経験をまとめた本ということで否が応でも期待が高まりました。 実際、読み終わった後はジョナサン・ラスマセンさんがつぶさに観察した Spotify での開発チームや文化が完結にまとめられていて、読んでいてすごくワクワクしました。 この本のテーマ 冒頭の『日本の読者の皆さんへ』の章でジョナサン・ラスマセンさんは以下のように書かれています。 『アジャイルは今や「ふつう」になりました。(中略)本書を『ユニコーン企業のひみつ』と題したのは、ものすごく成功しているソフトウェア企業(Spotify、Amazon、Googleなど)は「アジャイルでいつもやっていること」を超えた先のやり方を見つけ出しているからです。』 (vii 日本の読者の皆さんへ) ものすごく成功しているソフトウェア企業(Spotify、Amazon、Google など)はアジャイルを超えたさらに一歩先を進んでいる。その一歩先を Spotify はどう歩んでいるのか。知りたくないかい?という風に自分は理解しました。 これだけでなんだかワクワクしてきますよね。自分は Web アプリケーションエンジニアとしてずっとサービス開発に携わってきた中で、アジャイルでの開発の有効性について理解しているつもりです(スクラムマスターの資格を取りに行ったくらいアジャイルやスクラムの考え方が好きです)。 アジャイルのその先の一旦を垣間見れるということで、冒頭からフツフツとテンションが上がっていき、ページ数がそれほど多くないコンパクトな書籍ということも相まって、一気に 2 時間ほどで読み終えることができました。 アジャイルのその先 〜 Spotifyモデル 〜 Spotify モデルは自分自身はこの本を読むまで恥ずかしながら知りませんでした。2012 年(約 10 年も前なのですね...)に、Henrik Kniberg と Anders Ivarsson によって "Scaling Agile @ Spotify with Tribes, Squads, Chapters & Guilds" として発表されたのが初出のようです。 https://blog.crisp.se/wp-content/uploads/2012/11/SpotifyScaling.pdf Spotify モデルについて他に詳しく解説されている記事がいくつもあるので、ここではこれくらいにとどめ、自分はこの本の中でいくつか気になった Spotify の開発組織やスタイルについて取り上げたいと思います。 スクワッド 日本語で分隊と訳されることが多い squad ですが、もともとの語源は square(四角)です。昔、銃などの武器がなかった時代に、軍隊では四角い陣形を組んで戦ったことが多かったことに由来するそうです。自分はここから、一致団結して集団としてフォーメーションを組んで開発に立ち向かう姿をイメージしました。 また、この本からスクワッドはチームというよりは小さなスタートアップに近いように感じられました。小さなスタートアップ企業として、与えられた領域の全てに責任と権限を持ち、与えられたミッションを達成するために高速に開発イテレーション(リリースと検証)を回していく。理想のチーム形態の 1 つだと思います。 Spotify ではこのスクワッドが何をするにしても基本となり開発が進むようです。色々なスキルや経験をもった良き仲間とスクワッドを作っていく過程は楽しそうですよね。 ちなみに BASE でも企画・機能の策定から動作確認、リリースまで責任をもって行う複数の役割のメンバーで構成されたチーム単位で機能の開発をしているので、近い部分もありつつ違いも多かったので、スクワッドについて解説している第3章は特に興味深く読ませていただきました。 devblog.thebase.in カンパニーベット カンパニーベットは会社で取り組みたい最重要事項を並べた ToDo リストです。 会社で最もフォーカスしたいものを、全社向けの OKR のような形で表明する場合もありますが、「ベット」(賭ける)のほうが重要度に対して生々しさを感じてネーミングとして、素敵だなと思いました。 また、この章で紹介されている DIBB(Data, Insight, Belief, Bet)も、いわゆる OODA ループ(Observe, Orient, Decide, Act)をより具体化したものになっていて、過程がはっきりイメージしやすいので、チームに取り入れやすいのではないかと感じました。 会社やチームに限らず、時間は有限ですべてのことを完了させることはできないので、どこかでやることやらないことを分ける必要がありますが、カンパニーベットと DIBB は得られる結果がとてもシンプルになるはずなので、面白い仕組みだなと率直に思います。 会社やチームだけでなく、個人でも何かの目標ややりたいことに対して試してみるのもありなのではないでしょうか? まとめ 以上が、簡単ではありますが自分が『ユニコーン企業のひみつ』で紹介されている Spotify モデルの一部です。この本では他にもスタートアップとは何かや、開発組織をスクワッドを使ってスケールさせる方法、Google や Apple などとの文化の比較も掲載されていて、単純に読み物として面白かったです。 また、説明も具体的にかかれているので、気になった部分や仕組みを一部取り入れてみることもしやすいのではないかなと感じました。 注意点 この本の英語版が世に出たのが 2020 年 3 月です。また、ジョナサン・ラスマセンさん自身はすでに Spotify を退職されているようで、この本の原型といえるようなブログポストが 2017 年 11 月投稿されていました。 The Spotify Playbook /* */ agilewarrior.wordpress.com つまり、この本で描かれている Spotify の文化や開発スタイルは少なくとも 4-5 年前のもので、2021 年の今では Spotify は別の開発スタイルになっていてもおかしくないですね。 実際、冒頭で紹介した Spotify モデルを初めてまとめた "Scaling Agile @ Spotify" にはこう書かれていました。 Disclaimer: We didn’t invent this model. Spotify is (like any good agile company) evolving fast. This article is only a snapshot of our current way of working - a journey in progress, not a journey completed. By the time you read this, things have already changed. Scaling Agile @ Spotifywith Tribes, Squads, Chapters & Guilds p.1 この記事を読んでいるときにはすでにやり方は変わっているはず。終わりのない旅なんだ。 進化のスピードへの危機感を持つとともに更に刺激を受けました。また、Spotify でもやはり試行錯誤の連続なんだなと再確認できてよかったです。 最後に 今回、翻訳者の角谷さまのご厚意で献本いただき、拝読させていただきました。 この本がきっかけとなり社内でも開発スタイルや組織についての議論が活発になり、学ぶことが多かったです。改めて感謝申し上げます。 本当にありがとうございました。 お知らせ BASE 株式会社では、「BASE」の開発を一緒に盛り上げてくれるエンジニアを募集しています。一緒にかっこいい開発チームを作りましょう! ぜひよろしくお願いします! open.talentio.com open.talentio.com
アバター
こんにちは!アプリチームのEMをしている竜口です! 今回はリモート下でチームのコミュニケーションに課題があったので、それをどう改善していったかを紹介していきたいと思います。 初手、どうありたいかを決める やったこと いきなりxxxを始めます/やってみます!!と言ってもチームメンバーとしては意図、どうありたいかがわからないと、どう行動するべきかわからないと思います。 なので、何の為に改善して、どうありたいんだっけ?って部分を明文化しメンバーに伝えました。 実際はもっと肉付けしたものですが、下記がメンバーに伝えたものです。 1. 改善、課題解決していく 2. 開発をより早く、より確実にしていく 3. チームで開発していく 正直内容としては、特に真新しいものでもなく当たり前なものだと思いますが、今どのようなチームを目指しているのかというのを、メンバーと認識合わせました。 効果 個人的には、これが一番良かったかなと思っていて、この共通認識をもつことでメンバーも意識的に改善に向かっていってもらえたかなと思っています。 ツールに頼ってみる、tandem導入 やったこと tandem を簡単に説明すると A virtual office for remote teams を実現させるサービスで、 リモートで一緒に働く上でチーム感での会話のしやすさの実現、お互いの状態を緩く把握することで一緒のオフィスで働いてる感を醸成してくれます。 youtu.be 最初はアプリチームで導入し、その後アプリ開発に関わる他のプロジェクトメンバーも含めて導入しました。 効果 Slack or Zoomでコミュニケーションとってる時と比べ、ツールとしての会話のしやすさであったり会話できそうな状態か否かがわかりやすいのもあってコミュニケーションは増えていきました。 参考までに実際のtandemの様子ですが、各部屋に誰がいるのか分かるとこや、特定の人が緑色の場合はactive/オレンジだとinactiveという風に話しかけて良さそうな雰囲気も醸成できて話しかけるハードルや他の人の話してる雰囲気が伝わったりします。 朝会の後に意味のない時間を作る やったこと 毎日、朝会と言ってやること/困りごとを共有する場を15分程設けています。 その中で朝会後にすぐ抜ける必要のない運用にしてみました。朝会が終わってすぐ抜けてもいいし、話題がなくてもただそこにいてもいいし、何か特定の人に聞きたいことあれば聞いていいし、雑談してもいい。 効果 雑談が増えたのと、朝会でみんなの時間取るほどではないしSlackで聞くほどではない疑問とかが自然と出てきてよいです。 このルールだとtandemからの抜けにくさが出るかなと思ったのですが、お昼ご飯時なのもあり抜けやすさがあり、そこもメンバーのお気持ち的にも良かったのかなと。 作業タイムを作る やったこと ただただtandemつなぎっぱなしで作業する時間を2時間/1週間入れてみました。 ただ同じ空間で働いてる感を作りたかったので、ルールとしては強制参加だけど話したりカメラ繋いだりは強制しない形でやってみました。 効果 雑談や軽い相談も出てきてよかったです。ただずっとオンラインで繋げることによる集中力下がるというフィードバックがあったり時間や運用の仕方は、まだまだ調整が必要な部分もあります。 またtandemに Crosstalk という機能があり、同じ部屋にいる特定の人と話す時、会話に関係ない人にはその会話が小さくしか聞こえない機能があり、会話の指向性がでて話題に関係ない人のストレスにもなりにくくよかったです。 まとめ 今回は比較的導入が簡単なものをやってみて、以前よりチーム内での会話が増えて、一人で問題を抱え込むような形は改善されたかなとは思います。 以下メンバーの生の声です! - 朝会の後、いま取り組んでる課題とかに自然と移行できていい - Tandemは、いまみてるGitHubのURLとかを自動で取ってきて表示してくれる機能があって、IssueやPRの話などをしやすい - 朝回の前はあえて話すことないかなーと思っていたりするけど、意味のない時間に入ると敷居が下がって話せたりする - Tandemは、いざってとき会話するハードルが低い。ちょっと確認したいけど込み入った問題で確認したいぐらいの温度感のときに会話しやすいのが助かる ただまだ改善の余地はあって、チームとしてプロダクトを成長させる為に勉強会等を今後やっていきたいと思います。 そして!今アプリチームでは一緒にプロダクトを成長させていける方を募集しています! iOS 採用情報 / カジュアル面談 Android 採用情報 / カジュアル面談
アバター
BASE株式会社 Owners Experience Frontend チームのパンダ( @Panda_Program )です。 BASE では BASE の UI を構築するための社内コンポーネントライブラリ「BBQ」を使ってフロントエンドの開発をしています。 BBQ は Vue2 + Storybook v5 で作成されています。現在、フロントエンドの有志たちで Storybook のバージョンを最新の v6.2 にする対応をしています。 この記事では、Vue2 + Storybook v5 のコンポーネントを v6 向けに書き換える方法を紹介します。 なお、本記事ではStorybook v6 自体の機能の説明や、 main.js や preview.js の書き方といった Storybook の環境構築の方法には触れません。 Storybook コンポーネントを v5 から v6 に書き換える ここでは button-group を v5 から v6 に書き換えた例を紹介します。 まずは v5、v6 の書き方をそれぞれご覧ください。その後、変更点をそれぞれ解説をしていきます。 v5 の button-group.stories.js // bbq/stories/elements/button-group.stories.js import { action } from '@storybook/addon-actions' import { number, select, text, withKnobs } from '@storybook/addon-knobs' import { storiesOf } from '@storybook/vue' import { withInfo } from 'storybook-addon-vue-info' import { ButtonGroup } from '../../elements/buttonGroup/button-group.vue' import README from '../../elements/buttonGroup/README.md' import { devices } from '../../values/Devices' // Storybook コンポーネント名 const buttonStories = storiesOf( 'Elements/ButtonGroup' , module) buttonStories // addon-knob .addDecorator(withKnobs) // addon-info .addDecorator(withInfo) .add( 'ButtonGroup' , () => { return { components: { ButtonGroup } , // Vue Template template: ` <div : class = "'theme-'+device" > <p> <h2>デフォルト</h2> <div>アイコン+ラベル</div> <bbq-button-group :tag= "tag" :items= "iconsAndLabels" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> <div>アイコン</div> <bbq-button-group :tag= "tag" :items= "icons" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> <div>ラベル</div> <bbq-button-group :tag= "tag" :items= "labels" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> </p> <p> <h2>カスタムUI</h2> <bbq-button-group :tag= "tag" :items= "['a','b', 'c', 'd']" @change= "({index}) => change(index)" :selected= "selected" :width= "width" > <template v-slot= "{items, change}" > <button v- for = "(item, index) in items" @click= "change(index)" > {{ item }} : {{ index }} </button> </template> </bbq-button-group> </p> </div> `, // Data data() { return { device: select(`device`, devices, 'pc' ), selected: number( 'selected' , 0), width: select( 'width' , [ '' , 'full' ] ), } } , // Props props: { tag: { default : text( 'tag' , 'ul' ) } , } , // Computed computed: { icons() { return [{ icon: 'list' } , { icon: 'grid' }] } , labels() { return [{ label: 'ドラッグで並び替え' } , { label: '数値で並び替え' }] } , iconsAndLabels() { return [ { icon: 'list' , label: 'リストで並び替え' } , { icon: 'grid' , label: 'グリッドで並び替え' } , { icon: 'attentionCircle' , label: '念で並び替え' } , ] } , } , // methods methods: { change: action( 'change' ), } , } } , // Parameters { notes: README, } ) v6 の button-group.stories.js // bbq/elements/buttonGroup/button-group.stories.js import { devices } from '../../values/Devices' import ButtonGroup from './ButtonGroup' import README from './README.md' export default { // Storybook コンポーネント名 title: "V6/Elements/ButtonGroup/Vue" , // import したコンポーネントを指定 component: ButtonGroup, // parameters parameters: { notes: { README } , docs: { extractComponentDescription: ((_, { notes } ) => notes?.README) } } , argTypes: { // addon-knob の select で定義していた変数 device: { options: devices, defaultValue: devices [ 0 ] , control: { type: "select" } } , width: { options: [ "" , "full" ] , control: { type: "select" } } , // addon-action で定義していた関数 change: { action: 'changed' } } } ; const Template = (args, { argTypes } ) => ( { components: { ButtonGroup } , props: Object .keys(argTypes), template: ` <div : class = "'theme-'+device" > <div> <h2>デフォルト</h2> <div>アイコン+ラベル</div> <bbq-button-group :tag= "tag" :items= "iconsAndLabels" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> <div>アイコン</div> <bbq-button-group :tag= "tag" :items= "icons" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> <div>ラベル</div> <bbq-button-group :tag= "tag" :items= "labels" @change= "({index}) => {this.selected = index; change(index)}" :selected= "selected" :width= "width" /> </div> <div> <h2>カスタムUI</h2> <bbq-button-group :tag= "tag" :items= "['a','b', 'c', 'd']" @change= "({index}) => change(index)" :selected= "selected" :width= "width" > <template v-slot= "{items, change}" > <button v- for = "(item, index) in items" @click= "change(index)" > {{ item }} : {{ index }} </button> </template> </bbq-button-group> </div> </div> ` } ); export const Default = Template.bind( {} ) Default.args = { // Default コンポーネントに与える Props selected: 0, tag: "ul" , icons: [{ icon: 'list' } , { icon: 'grid' }] , labels: [{ label: 'ドラッグで並び替え' } , { label: '数値で並び替え' }] , iconsAndLabels: [ { icon: 'list' , label: 'リストで並び替え' } , { icon: 'grid' , label: 'グリッドで並び替え' } , { icon: 'attentionCircle' , label: '念で並び替え' } , ] , } ; なお、 devices の定義は const devices = ['pc', 'sp'] です。 Storybookv6の変更点 上記、新旧ファイルの変更点を抜粋してコードを比較します。 Storybook 上のコンポーネント名 Storybook で表示されるコンポーネント名の定義方法の変更点です。 // v5 import { storiesOf } from '@storybook/vue' const buttonStories = storiesOf( 'Elements/ButtonGroup' , module) // v6 export default { title: "Elements/ButtonGroup" , // ... } v6 では @storybook/vue を import する必要がなくなりました。その代わりに、default export するオブジェクト内にコンポーネントのメタ情報を記述します。 表示するコンポーネントを指定 コンポーネントを指定する箇所も変更になっています。 // v5 import { storiesOf } from '@storybook/vue' import { ButtonGroup } from '../../elements/buttonGroup/button-group.vue' const buttonStories = storiesOf( 'Elements/ButtonGroup' , module) buttonStories.add( 'ButtonGroup' , () => { return { components: { ButtonGroup } , // ... } } ) // v6 import ButtonGroup from './ButtonGroup' export default { // ... component: ButtonGroup, } v6 ではコンポーネントを default export するオブジェクトのプロパティに追加します。 parametersを定義する箇所の変更 v5 では add メソッドの第三引数だった parameters の定義箇所が、v6 では default export するオブジェクトに変更になりました。ここではマークダウンファイル README.md を v6 で読み込める書き方を紹介します。 // v5 import { storiesOf } from '@storybook/vue' import { withInfo } from 'storybook-addon-vue-info' import README from '../../elements/buttonGroup/README.md' const buttonStories = storiesOf( 'Elements/ButtonGroup' , module) buttonStories .addDecorator(withInfo) // decorator で addon-vue-info を活用している .add( 'ButtonGroup' , () => { ... } , { notes: README } ) // v6 import README from './README.md' export default { // ... parameters: { notes: { README } , docs: { extractComponentDescription: ((_, { notes } ) => notes?.README) } } , } v6 では Storybook 上の Docs タブで README.md を表示できます。このため、 @storybook/addon-notes 、 storybook-addon-vue-info は不要になります。 今回は v6 のコンポーネント内で定義しましたが、 preview.js に以下のように記述すると Storybook の全コンポーネントに parameters が追加されるため、 docs を各ファイルで記述すること避けられます( 「Migrating from notes/info addons」 )。 // preview.js import { addParameters } from '@storybook/client-api' ; addParameters( { docs: { extractComponentDescription: ((_, { notes } ) => notes?.README) } , } ); なお、今回は既存資産を活かすためにマークダウンファイルをそのまま使いましたが、 MDXを用いる方法も公式で紹介されています。 addon-knob の書き換え v5 では addon-knob を使うと Storybook 上でコンポーネントに与える値を画面上で変更できました。 v6 では addon-essentials に含まれている controls を使えば同様のことができます。 以下では knob の number 、 select 、 text 関数を書き換えています。 // v5 import { number, select, text, withKnobs } from '@storybook/addon-knobs' import { devices } from '../../values/Devices' buttonStories. add( // ... data() { return { device: select(`device`, devices, 'pc' ), selected: number( 'selected' , 0), width: select( 'width' , [ '' , 'full' ] ), } } , props: { tag: { default : text( 'tag' , 'ul' ) } , } , } ) // v6 import { devices } from '../../values/Devices' export default { // ... argTypes: { // select 関数で作成していた値 device: { options: devices, defaultValue: devices [ 0 ] , // 初期値の設定 control: { type: "select" } // この行は省略可能 } , width: { options: [ "" , "full" ] , control: { type: "select" } // この行は省略可能 } , } ; const Template = (args, { argTypes } ) => ( { ... } ); export const Default = Template.bind( {} ) Default.args = { selected: 0, // number 関数で作成していた値 tag: "ul" , // text 関数で作成していた値 } ; select 関数の代わりになる control: { type: "select" } で定義した値は、 defaultValue で初期値を設定できます。 なお、 export default の中で定義している device 、 width は以下のように Default.args で定義することも可能です。 // v6 Default.args = { device: devices, width: [ "" , "full" ] , selected: 0, tag: "ul" , } ; (参考: Dealing with complex values ) addon-actions の action 関数の書き換え addon-actions を使ったダミーのコールバック関数の定義方法も変更になりました。 // v5 import { action } from '@storybook/addon-actions' buttonStories .add( // ... () => { // ... methods: { change: action( 'changed' ), } , } } ) // v6 export default { argTypes: { change: { action: 'changed' } } } ; (参考: addon-actions ) ただし、以前のように action 関数を用いても問題なく動作するため、書き換えは必須ではありません。 コンポーネントに渡すデータの定義の変更 v5 で記述していた data, props, computed で定義していた値を Storybook の画面上で自由に変更したい場合は、 argTypes や args に集約可能です。 // v5 buttonStories .add( // ... () => { // ... // Data data() { return { device: select(`device`, devices, 'pc' ), selected: number( 'selected' , 0), width: select( 'width' , [ '' , 'full' ] ), } } , // Props props: { tag: { default : text( 'tag' , 'ul' ) } , } , // Computed computed: { icons() { return [{ icon: 'list' } , { icon: 'grid' }] } , labels() { return [{ label: 'ドラッグで並び替え' } , { label: '数値で並び替え' }] } , iconsAndLabels() { return [ { icon: 'list' , label: 'リストで並び替え' } , { icon: 'grid' , label: 'グリッドで並び替え' } , { icon: 'attentionCircle' , label: '念で並び替え' } , ] } , } , // ... // v6 export default { // ... argTypes: { // addon-knob の select で定義していた変数 device: { options: devices, control: { type: "select" } } , width: { options: [ "" , "full" ] , control: { type: "select" } } , } ; // ... export const Default = Template.bind( {} ) Default.args = { selected: 0, tag: "ul" , icons: [{ icon: 'list' } , { icon: 'grid' }] , labels: [{ label: 'ドラッグで並び替え' } , { label: '数値で並び替え' }] , iconsAndLabels: [ { icon: 'list' , label: 'リストで並び替え' } , { icon: 'grid' , label: 'グリッドで並び替え' } , { icon: 'attentionCircle' , label: '念で並び替え' } , ] , } ; ただし、data や props、computed をそのまま残すことも可能です。その場合、 props 以外は GUI 上で値を変更できません。Storybook の GUI 上で変更したい値であれば、args で記述すれば良いと思います。 BASE BBQ の.Storybook では様々なパターンがあるため、まずは v6 の書き換えを優先しています。このため、data 等で定義している値は一旦 args に集約し、コンポーネントごとの細かい調整は個別に対応する予定です。 以上のパターンで BASE の BBQ で作成された Storybook コンポーネントの大抵のケースを網羅しています。 その他、より詳しい変更点は、 Storybook 6 Migration Guide をご覧ください。 addon について v6 で利用可能な Essential addons には、今までの主要な.addon の機能がまとめられています。 Docs Controls Actions Viewport Backgrounds Toolbars & globals Storybook で開発するにあたり、 @storybook/addon-essentials は開発体験を向上させてくれるため、v6 からは必須といっても過言ではないでしょう。 ここでは、先程紹介した例で使用している addon のみ取り上げます。 addon-knob v6 では deprecated addon-essentials の controls を代わりに使う addon-info knob と同様に v6 では deprecated addon-essentials の docs を代わりに使う addon-action v7 で deprecated になる予定 v6 ではまだ使えるが、可能なら control に置き換えると次のバージョンアップがスムーズになる v6 で addon-action を使いたい場合は、以下のように記述すれば OK です。 const Template = (args, { argTypes } ) => ( { // ... template: `...`, methods: { change: action( "changed" ), } } ); 表示を確認する v6 の書き方に変更したコンポーネントを実際にStorybook で表示すると以下のようになります。 StorybookのButtonGroup ンポーネント control で値を変更して様々な props の表示ケースを確認できるようになりました。 また、「Docs」というタブをクリックすると README が表示されています。 StorybookのButtonGroupコンポーネントのDocs これで v6 への書き換えが完了しました。 Storybook v6 で向上した開発体験 v5 と異なり、v6 では以下の点で開発体験が向上しました。 コンポーネントに与えるデータを Vue の外(args, argTypes)で定義できる args として定義した値は addon-knob を使わなくても Storybook 上で値を書き換えられる 上記の例では取り上げていませんが、args を変えることでコンポーネントのバリエーションを容易に作成できる( using-args ) インストールする addon の数や、stories ファイルのボイラープレートが減った おわりに 今回は Vue2 + Storybook v5 の環境で Storybook を v6 にアップデートする詳細な方法を紹介しました。 Storybook のバージョンアップにあたり本記事の内容が参考になれば幸いです。 多くの方はお気づきだと思いますが、v5 から v6 への書き換えといってもパターンが決まっています。 このため、手順さえ分かってしまえばプログラムで機械的に置換するだけで対応できます。 この考え方をもとに、TypeScript Compiler API を使ってメタプログラミングで v5 のコンポーネントを v6 に書き換えたので、次回の記事でその方法をご紹介しようと思います。
アバター
この度は、5/29(土)にオンラインで開催された PHP カンファレンス沖縄 2021 にゴールドスポンサーとして協賛し、また 4 名のメンバーが登壇しました。 登壇者 4 名から発表内容の補足など、PHP カンファレンス沖縄 の参加レポートをお届けします! phpcon.okinawa.jp 発表内容と補足 杉浦のセッション内容について こんにちは!BASE 株式会社でバックエンドの開発をしている杉浦( yutakasugiura )です。この度、PHP カンファレンス沖縄 で、BASE のスポンサートークとして「変化する時代のエンジニアリング」について発表させていただきました。 発表内容の意図 PHP に関するカンファレンスでしたが、スポンサーセッションの発表内容は PHP 以外のことでも良いとのことでしたので、広い意味でエンジニアリングについてお話ししました。普段の業務におけるエンジニアリングとは PHP のような言語が話題の中心ですが、せっかく頂いた機会なので、より大きな視点でエンジニアリングを語ることにしました。 登壇資料 発表内容の要旨 企業活動におけるエンジニアリングはマーケットニーズに従属的です。2010 年代に CakePHP がフレームワークとして支持されたのも「便利な web サービスを早く使いたい!」というニーズが背景にありますし、これほど AWS が広まった理由も「いつアクセスが爆発的に増加するかわからない!」という課題への対処として有効だったからです。スマホの普及によって、web サービスへのニーズが 2010 年代を通じて爆発的に増加したからこそ、これらのエンジニアリングが時代の潮流となったと言えます。 一方で、注意しなければならないことは、エンジニアリング単体で物事を考えると、ニーズにそっぽを向いてしまう可能性があると言うことです。純粋な研究開発は別として、企業活動の最終目的は、顧客にサービスを提供することにあり、その手段としてエンジニアリングがあります。考えてみれば当たり前の事実ですが、当たり前だからこそ、この単純な事実を忘れやすいというのも、また真理です。 web 業界は誕生してまもないこともあり、ニーズと技術が乖離してしまうという経験値が少ない業界です。ですが、歴史を広く見渡せば、時代の変化によって、顧客ニーズとエンジニアリングが乖離してしまい、ビジネスとしては幸せにならなかった例は枚挙にいとまがありません。 今回のセッションでは、1980 年代の DRAM(ダイナミック・ランダム・アクセス・メモリー)におけるエンジニアリングの失敗例を挙げましたが、1 つの技術に精通するエンジニアほど、ニーズに乖離してしまうというのは往々にしてあります。科学技術の世界ではパラダイムシフト、ビジネスの世界ではイノベーションのジレンマがよく知られますが、専門家であっても、常識の変化を機敏に感じ取ることは容易ではないのです。 2021 年の時点で web 業界は成長産業ですし、2040 年ぐらいまで急成長が続くことは、ほぼ確実な未来です。ですが、この疑いない状況が未来永劫、永遠に続くのか?と問われれば、その回答は「その限りではない」と断言します。 だからこそ、エンジニアは細心の注意を払って「このエンジニアリングによってどんなニーズが満たされるのか?」「顧客ニーズはどのように変化していくのか?」を、常日頃から考え続けなければならないのでしょう。 感謝 技術に関するカンファレンスの登壇は初めての経験でした。貴重な体験をさせていただき、関係者の皆様に御礼申し上げます!ありがとうございました! 炭田のセッション内容について こんにちは!BASE 株式会社の炭田( @tac_tanden )です。今回の PHP カンファレンス沖縄 2021 にて「PHP で throw しない例外ハンドリング」をテーマに 30 分間発表させていただきました。 セッション内容について PHP で throw せずに例外ハンドリングを行う事例をご紹介する前に、そもそも例外とは何かという部分を整理したり、PHP 以外の言語でどのように throw しないで例外ハンドリングを行っているのかを説明させていただいました。 特に、前半の例外の概念を説明するのに苦労し、発表でもわかりにくい部分があったかと存じます。例外については様々な意見や見解があるかと思いますが、みなさまの議論や考えの整理にこの発表が少しでもお役立てれば幸いです。 カンファレンスでの発表について 今回自分は初めてのカンファレンスの発表だったのですが、発表を通して色々な方と関わることができ、とても楽しいカンファレンスでの発表体験になりました! 発表中に twitter で様々なコメントをいただいたのも、すごく嬉しかったです。今後も機会があればぜひ発表したいと思いました。この場をお借りして、運用の皆様、参加者の皆様にお礼申し上げます。ありがとうございました! セッション動画はこちらになりますので、興味のある方ぜひご覧いただけると嬉しいです! https://www.youtube.com/watch?v=kOhsJCW9YIE&t=14093s 大津のセッション内容について 2021 年 2 月から BASE 株式会社に入社しました大津( @cocoeyes02 )です。PHP カンファレンス沖縄 2021 では Git のコミットにまつわるトークをさせていただきました。 また、セッションの動画はこちらになります。 コミットへのFBをもらうとGood 最後のスライドでこのセッションは自戒が含まれていると書いていますが、それは私自身過去にコミットによる FB をたくさん受けたことがあるからです。 例えば NG 例として挙げたコミットメッセージ「一旦コミット」「PR で指摘したので反映」「バグを直した」は、全て過去に僕が書いたコミットメッセージだったりします。このコミットメッセージが何故ダメなのか FB をもらって、ようやく理解できるようになりました。 コミットを見るのは他人と言う話をしましたが、リーダブルなコミットを書くことによるメリットを受け取れたか判断するのも他人です。今回のトークの tips を参考にしつつ他人から FB をもらうと、リーダブルなコミットを書けているのか判断できるので良い思います! スタッフの皆様ありがとうございました! 最初のオープニングから、最後の懇親会まで楽しく過ごすことができました! 今年はコロナ渦ということもあって、直接沖縄の会場へ向かうことは叶いませんでしたが次回は是非現地にて登壇したいと思っています! 東口のセッション内容について はいさい!BASE BANK 株式会社の東口 ( @hgsgtk )です。当カンファレンスでは、プロダクト開発のため E2E テスト環境を整備してきた中で、泥臭くなりがちなテスト環境・テストデータの考え方と工夫について発表させていただきました。 懇親会等、PHP コミュニティの方とも楽しくコミュニケーションさせていただきました。前回の PHP カンファレンス沖縄では直接沖縄現地に伺い、PHP コミュニティの方と直接話したりソーキそばなどを堪能したり楽しい時間でしたので、また来年・再来年とコロナが落ち着いてきた際に、直接沖縄の会場に伺えればいいなと思います。 最後に 今回弊社は計 4 名のメンバーが登壇して発表する機会をいただき、とても充実した時間を過ごすことができました! また自身の発表だけでなく、多くの方々の発表を通して様々な知識にふれることができ、各々が新たな知見や視点を持ち帰って来れたと考えております。 それもひとえに PHP カンファレンス沖縄実行委員会の皆様のおかげです。心より感謝申し上げます。 それでは、来年もまた皆様にお会いできることを楽しみにしております!
アバター
BASE株式会社Data Strategyチーム兼 Data Platformチームの楊(@wyang)です。 ショッピングアプリ「BASE」では、 前回公開した記事 の通り、商品検索基盤をCloudSearchからAWS Elasticsearch Serviceへ移行しました。 この記事では、レスポンス速度改善と検索精度改善をメインにご紹介します。 1. レスポンス速度改善について 1-1. ElasticsearchのProfile API この Profile API を利用すると検索クエリを分析し、どんなクエリが発行されたのか、どのくらいの処理時間がかかっているのかなどを簡単に知ることが出来ます。 GET items/_search { "query": { "bool": { "filter": [ { "range": { "date_field": { "gte": "now-180d" } } } ] } }, "profile": "true", // profile: trueで有効になる "size": 0 } BASEではAWS Elasticsearch Serviceを使っている都合上、X-Packの Search Profiler を利用することはできませんが、こちらではより手軽にパフォーマンス分析をすることができます。 1-2. 改善された変更点 1-2-1. 時間Rangeクエリの丸め込み BASEでは長期間でログインされていないショップの制御など、検索の要所要所でdate型データに対する絞り込みをしています。 GET items/_search { "query": { "bool": { "filter": [ { "range": { "date_field": { "gte": "now-180d" } } } ] } } } しかし、 Elasticsearch公式のドキュメント )にもある通り、時刻によるフィルタリングはfilter cacheに載らないため、毎回絞り込みが行われレスポンス速度が低速になる傾向がありました。 ドキュメントのガイドのように時刻を丸め込むことにより、filterクエリがfilter cacheの対象となり、レスポンス速度が大幅に改善されました。 GET items/_search { "query": { "bool": { "filter": { "range": { "date_field": { "gte": "1600819200000" } } } } } } 1-2-2. 必要最低限のフィルターを利用、不要なドキュメントの削除 Profile APIで時間がかかるフィルターにあたりをつけた後、検索インデックスで管理する必要がないフィールドとフィルターを削除しました。この部分は、インデックス更新バッチ側で制御しており、不要なドキュメントをそもそも登録しないなどの対応で、インデックスサイズを軽量化させました。結果として、クエリの軽量化にも繋がり、レスポンス速度が改善されました。 1-2-3. search_analyzerの使用 シノニム辞書を利用した検索を実現するために、インデックス時と検索時でanalyzerを分けて管理しました。 詳しい内容は Elastic社公式の記事 にもありますが、 インデックスサイズに影響が出ない。 用語の統計全体は同じに保たれる。 同義語ルールを変更するにあたり、ドキュメントの再インデックスは必要ない。 など、大きなデメリットはなく、こちらの設計を採用しています。 2. 精度改善について 2.1. A/Bテスト運用 Firebase Remote Config による検索の A/Bテストを実施しています。アプリのアップデートを配布せずとも、Remote Configのコンソール上で設定を修正するだけで、指定の比率でテストサイズを設定できるようになりました。 2-2. A/Bテストの評価指標 実際に改善に繋がったかどうかを以下の指標で判断しています。 商品閲覧につながった検索率 商品閲覧につながった検索のうち、商品閲覧位置(表示順位)の最小値の平均値 検索結果を1件も返せなかった率 検索結果のうち上位k件の商品閲覧率(SERP@k) 商品お気に入りにつながった率 検索実行からレスポンスを返すまでの時間 閲覧率だけでなく他の評価項目(商品の多様性など)も含めました。 2-3. これまでに効果があった変更点 2-3-1. 複数語検索時の絞り込み条件の緩和 ユーザーが複数語で検索した場合、語順を考慮する検索(type: phrase)と、語順を考慮しない検索(type: best_fields)では、語順を考慮した検索のほうが成績が良いことがわかりました。ただし、一部のキーワードの結果に対してはヒット件数が0件になってしまうため、ヒット件数が一定件数以下になった場合に、条件を緩和して語順を考えないクエリで再検索をするようにしています。結果、CTR改善に繋げることができました。 2-3-2. function_score BASEのおすすめ順では、ユーザが検索したキーワードに該当するドキュメントをスコアリングする際にfunction_scoreを利用しています。function_scoreにより、ショップ情報や、商品情報の複数の要素に対して、それぞれ重みづけをしたオリジナルのスコアを設定することができます。 GET items/_search { ... "function_score": { "functions": [ { "field_value_factor": { "field": "score_field_A" // スコアに関するfield } }, { "gauss": { "date_field_B": { // 日付に関するfield "origin": "now", "scale": "180d", "decay": "0.5" } } } ], "score_mode": "multiply", "boost_mode": "multiply" } } 上記のクエリでは、score_field_Aとdate_field_Bのスコアを乗算してスコアリングをしています。 gaussを利用する場合では、origin(now:現在時刻)を基準として、scale(180d: 180日)の日付差の地点に対して、decay(0.5)の減衰をする正規分布のスコアを設定することができます。 その他 日々登録される商品データのサイズや、更新反映までに求められる時間を考慮し、 Bulkで投入するリクエストデータのサイズ refresh_interval に対する調整をそれぞれ行い、効率的にドキュメントを更新できる設定にしています。 おわり 今回は移行後におけるレスポンス速度改善と検索精度改善についてご紹介しました。AWS Elasticsearch Serviceへの移行を行うことで継続的な検索性能の改修、改善をしやすい環境を作ることができました。これからさらなる改善を行なっていきたいと思います。
アバター
こんにちは、BASE株式会社Data Strategyチームの杉です。 ショッピングアプリ「BASE」では、検索にAmazon Cloudsearchを使用していました。今回、検索基盤をAmazon Elasticsearch Service(以下、ES)に移行し、Data Strategyチームで管理をする方針にしました。 この記事では商品が更新された際などにどのように検知し、データをESにいれるようにしたかなど、基盤の部分をメインにご紹介をします。 1. 背景 検索は新しいショップに出会うきっかけを作ってくれたり、探していた商品をいち早く見つけることができることができます。 そのため、検索機能はどのECサイトなどでも見かける存在であり、活用している人も多いのではないでしょうか。 例えばショッピングアプリ「BASE」の検索はこのような画面になっています。 このようにさまざまな便利さをもっている検索機能ですが、ショッピングアプリ「BASE」では継続的な検索性能の改修、改善ができていないという問題がありました。 これらの課題に対し、今回検索基盤の移行を行うことで検索性能改善への第一歩を進めました。 2. 新基盤の移行について 今回の移行はAmazon CloudsearchからESへデータを移行するだけに見えます。 しかし、内部的には管理をData Strategyチームに移行するため、商品が更新された際に検知、データの取得や同期など全体的に新しく作り直す必要がありました。 また、実際に移行計画を進めていくと、様々な問題の壁に当たりました。 例えば Data Strategyチームの管理に変えるために参照するデータベースを変える必要がある データを全てロードし直す必要がある 商品更新時にできる限りリアルタイムに更新をしたいがどう実装するべきか 商品情報に関するテーブルがたくさんある などが挙げられます。 これらの課題が存在したため、手探りでlogstashを試したりembulkでデータ同期を試みたりもしながら、現在の基盤を作りました。 2-1. システム構成 今回は商品検索のみの移行をしましたが、検索を行う上で商品情報に関するテーブルは多くあります。 また、これらのテーブルがそれぞれ更新が起きた際にESにもデータを同期させる必要があります。 これらを考慮し、最終的に以下のシステム構成で実装を行いました。 商品情報の更新 定期的に動いているbatchがS3から前回起動情報を取得 データ更新の有無の確認 更新分のデータを取得 ESのデータを更新 S3へ更新後の情報を記録 という流れで動いています。これを短い時間で繰り返すことでリアルタイムに近い間隔でデータを更新することが実現できました。この「短い時間」は定期実行時間を何分毎と設定しているわけではなく、可能な最短時間で動かしています。 S3に入っている前回起動情報はデータ同期をしているテーブルのそれぞれの実行情報を記録しています。このような記録をし、都度取得をすることで急にDBの同期が上手くいかなくなった場合にも、自動で復旧するような仕組みになっています。 さらに、batchはデータの取得からESのデータ挿入まで全てPythonで作りました。このPythonでの実装時にいくつかの細かい設定をしました。 こちらはinsert時のコード例です。 es = Elasticsearch( ..., timeout=timeout # (1) ) retry_count = 0 while retry_count < retry_max: # (2) try : ... es.bulk(body) time.sleep( 1 ) # (3) break except BaseException : ... time.sleep( 1 ) retry_count += 1 (1) timeoutを明示的に書く timeoutを書かない場合、デフォルトの秒数となります。しかし、実際に動かしてみると稀にtimeoutになることがありました。 bulk時にはサイズで区切っていたのですが、商品情報は商品説明文などが長いことがあり、想定よりサイズが大きくなってしまうことがありました。この際にtimeoutが発生してしまっていたのですが、伸ばすことでtimeoutでのエラーはなくなりました。 (2) retry処理をいれる こちらも同様にbulk時の処理で稀に失敗することがありました。 しかし同じデータをもう一度試すと成功することも多かったため、retry処理をいれることで対応をしました。 (3) sleepをいれる 今回、商品情報が更新されたら更新分を全て更新する仕様です。こちらも頻繁に起こることではありませんが、ある時間に大量の商品情報更新がかかることもあります。 その際に短時間に何度もbulkを行うと、内部キューが溜まりすぎてしまうことがありました。上限値を超えた場合、破棄されてしまうため、データが欠損してしまう恐れがあります。 ESの設定で上限値を変更するという手段もありますが、上限値を変えても送る量が上限値を超えないという保証はなかったため、スピードを緩和させることで対応をしました。 このように、とても細かい部分の設定ではありますが、これらの処理をいれることでデータの欠損もなくスムーズにESにデータをいれるためのシステムを実装することができました。 2-2. 初期ロード 通常の商品が追加や更新された際のデータ更新はシステム構成で書いたような内容で行われています。 しかし、今回ESには商品情報がゼロの状態から始めるため、今までのデータを入れ直す必要がありました。 対応策としては、別途batchを作り初期ロード専用の作業を行いました。 初期ロードのbatchは以下のような流れでデータをいれるようにしました。 メインの商品テーブル以外は一気に全部取得 user_idもしくはitem_idごとにデータをまとめる 商品テーブルをID区切りでデータを取得し、2のデータとjoin これをIDを変えながら繰り返すことで今までのデータを全部入れました。 通常時のESのinsertはupdateもしくはdeleteを使用していますが、初期ロードではindexを使い少しでも速くなるようにしました。 2-3. 例外テーブル システム構成でも書いたように、今回商品に関するテーブルは多くあります。 テーブルに何かしらの変更に起きた際に更新をかけるという方法でうまくいかないテーブルも存在しました。 具体的には batchで計算した結果を格納するため同時刻に何十万、何百万のレコードがinsertされるテーブル 更新頻度がとても高く、約1分間に紐づくレコードが何十万、何百万存在するテーブル があります。 これらのテーブルに関しては、上の処理とは別の処理も加えてテーブル内容の同期を行なっています。 2-3-1. batch計算結果を格納しているテーブル こちらに関しては、常にinsertが走っているわけではなく、dailyやweeklyといった頻度であったことから、insert時に全てのデータを更新するのではなく、少しずつ更新をする方針にしました。 batchでの計算結果の追加 定期的に動いているbatchがS3から前回データを取得 ID区切りでデータを確認 データを取得 前回データとの差分を確認 差分発生データのみESを更新 S3へ更新後の情報を記録 大まかな流れは通常動いているシステムと同様ですが、S3に前回のデータを保存している点と更新分全てを取得するのではなく、ID区切りで他の更新の妨げにならない量に抑えている点が異なるポイントとなります。 このような工夫をすることで、他のテーブルの更新にも影響がでず、スムーズに更新をすることが可能になりました。 2-3-2. 更新頻度が高いテーブル 更新頻度が高いテーブルの問題点としては、同時刻に更新しなくてはいけないレコードが多く、best effortでの更新を行なっているとどんどん詰まっていき、全体の更新が遅くなるということがありました。 更新レコードが多く、更新も常に起こっているためbatch計算結果テーブルのようにIDで区切って少しずついれるようなこともできませんでした。 検討の対象となった更新頻度が高いテーブルでは、多くの情報が入っており、少しの更新でもレコード全体に更新がかかってしまっていました。 そのため、必要な情報のみをS3へ保存し、更新が起きた際にS3の情報と比較をし、必要な情報に更新が起きた際のみ、ESのデータも更新をする方法にしました。 S3に前回情報を保存し、毎回取得することはデータ更新の速度に影響してしまう可能性もありましたが、幸いにも本当に更新すべきデータがかなり減ったため、速度アップにつながりました。 おわり 今回は移行後の基盤についてをメインにご紹介しました。 このように実装を行い、現在のショッピングアプリ「BASE」では移行後のシステムで稼働をしています。 実際には基盤完成後にAPIのresponse timeが遅い問題の対応や、CTRの改善などを行なっています。こちらの詳細については来週ご紹介させていただきます。 移行を行うことで継続的な検索性能の改修、改善をしやすい環境を作ることができました。これからさらなる改善を行なっていきたいと思います。
アバター
はじめに こんにちは。BASEのCSEチームの秋谷です。 CSEチームは社内業務の効率化と財務の信頼性担保することを専門とするチームとして開発や社内の整備を行なっています。そんなCSEの取り組みを紹介できればと思います。 CSEについて詳しくはこちらをご覧ください devblog.thebase.in BASEショップの売上金の担保とJ-SOX対応 BASEではショップの売上を一時的にプラットフォーム側が預かっており、申請があった段階で売上金を引き出せるようになっています。 そのため、ECプラットフォームはショップに対して、以下のことを担保することが大前提です。 データが適切であること 発注、決済データの適切な記録がされていること 上記データから作成される、売上計上のデータの作成が適切であること 集計範囲、抽出がただしくおこなわれていること BASEは2019年12月に上場したこともあり、J-SOXへの対応を求められるようになりました。これにより、より厳格にショップの売上金に対してデータの透明性を担保し、またそれに対して監査を受ける義務が発生しました。 J-SOXとは 財務報告に係る内部統制報告制度の概要 J-SOXにおいては、経営者は財務報告に係る内部統制を構築する責任を有しており、その有効性を自ら評価し、外部に対してその結果を報告することが求められます。また、財務報告に係る内部統制の有効性に関する経営者の評価を外部監査人が監査することによって、その評価の適正性を確保する制度となっています。 監査人による内部統制報告書の監査 J-SOXでは、財務報告に係る内部統制の有効性に関する経営者の評価を監査人が監査することによりその適正性を確保することとされています。また、内部統制監査は、原則として財務諸表監査と同一の監査人が実施することとされ、内部統制監査報告書は財務諸表監査報告書と合わせて作成することが原則とされています。 参照: https://www.pwc.com/jp/ja/knowledge/ipo-guideline/j-sox.html ショップの売上金の検証と経理業務の負担 店舗預かり金について BASEではショップの売上金を店舗預かり金と称しています。この店舗預かり金に対して経理チームがデータを検証し、妥当性を担保しています。 当初は売上データを取得するのに時間がかかってしまったり、日々動いているトランザクションに対して常にSQLを実行しているためキャンセルなどが発生した場合に再集計すると過去の結果が変わってしまったり、Excelでデータの確認作業を行なっていたためExcelが重くなり開けなくなるなどの物理的な問題もありました。 また、売上金を担保するためにBASEシステム上のデータと各決済データとの突合しなければなりませんが、これにもかなりの時間がかかっていました。 reportシステムの構築 そこでCSEでは上記の問題を解決するために、BASEのデータ集計やレポーティングのためのシステム(以下reportシステム)を作成しました。 先述の通り、BASEでは各ショップの売り上げを一時的に預かっているため、特に以下の点について詳しくチェックをしています。 店舗預り金のBASEシステムのデータが適切に作成されているか 例:商品の代金、送料、各種手数料が正しく計算されている 店舗預かり金の構成要素に変更はないか 例:新規決済手段の追加(AmazonPayなど) 決済データの突合 例:BASEシステム上のデータとクレジットカードの決済情報が正しいか この売上データの取得やデータの突合をしやすくしたり、構成要素の変更をわかりやすくするために、日次でBASEシステムの本番DBから必要なデータをAmazon Athenaにインポートしています。 reportシステムの構成について次に詳しく説明します。 構築 reportシステム概要 reportシステムの概要はざっくりこんなかんじです。 1. BASEシステムの本番DB(Amazon Aurora)から、必要なデータをAmazon Athenaに日次で取り込む 2. Amazon Athenaに対してSQLを実行して、レポートとなるCSVを出力 reportシステムの構成 report-dataloader(図中の青枠、青矢印) レポートシステムのデータを準備する Auroraのクローン作成機能でテンポラリDBを作成 Embulkを用いて、テンポラリDBからレポートに関わるテーブル、列のデータをAthenaにロード ( civitaspo/embulk-output-s3_parquet を使用) report(図中の赤枠、赤矢印) レポートのデータを抽出する 店舗預り金に関わるデータを抽出するクエリをAthenaに発行する クエリの結果からCSVを作成する レポートシステム 各決算データの取得機能 各決済データとBASEシステム上のデータを突合するために、各決済システムからデータを取得してAthenaにインポート report-datacollector 各決済の外部のシステムからデータをダウンロードをしてきてS3に保存 決済システムによって取得方法が異なる 各種APIの利用など report-dataloader S3に保存したデータからAthenaのテーブルを作成 report-dataloader reportシステム構築による効果 reportシステムの構築によりAmazon Athenaへクエリを実行することによって、重たいクエリを実行しなくともBASEシステム上で取得していた店舗預かり金と同等のデータが取得可能になり、日次でのデータの突合もし易くなりました。 BASEにはさまざまな決済方法があるため一概には言えませんが、一番トランザクション数の多い決済で約1人/日分の工数の削減出来ています。クエリの実行時間は決済方法に関わらずほぼ同一なので、トランザクション数が多い決済ほど工数が削減されていることになります。 終わりに 上記に挙げた以外にも、BASEではreportシステムには経理業務を改善するための細かな機能を随時追加しています。 ここに挙げた例はCSEの活動の一部です。CSEは Corporate Solutions Engineering の略であり、社内業務のより良く改善できればと思っています。 CSEチームでは、エンジニア力で社内業務の効率化やJ-SOX対応をリードできるコーポレートエンジニアを募集しております。 下記のリンクから気軽にご連絡ください。 https://open.talentio.com/r/1/c/binc/homes/4380
アバター
こんにちは。BASE BANK株式会社 Dev Divisionにて、 Software Developerをしている永野 ( @glassmonkey ) です。 今回は弊社でブロンズスポンサーとして協賛しました。 PHPをメイン言語として使用しているBASE社と異なり、BASE BANK社ではGoをメイン言語として使っているので、今回は初めてBASE BANK社としてスポンサードさせていただきました。色々至らぬこともありましたが、この場を借りてお礼を申し上げます。 登壇の内容に関してですが、業務ではなく趣味で触ってるFlutterネタです。勢いでProporsalに出したら通していただいたので、趣味全開な形になりました。 GoConference2021 Springについて gocon.jp https://gocon.jp/ Go Conferenceは半年に1回行われるプログラミング言語Goに関するカンファレンスです。 今回は初のオンライン開催という記念すべき回でもありました。 本記事のGopherアイコンのライセンスは以下の通りです。 github.com The Go gopher was designed by Renee French. ( http://reneefrench.blogspot.com ) The gopher stickers was made by Takuya Ueda ( https://twitter.com/tenntenn ). Licensed under the Creative Commons 3.0 Attributions license. 感想と振り返り 発表内容について 今回は個人の趣味でハマってるFlutterネタを発表させていただきました。 私自身はGo歴1年ぐらいのまだまだ初心者ではありますが、機会をいただけてよかったです。 speakerdeck.com 発表の様子はこちらです。 youtu.be 発表についての振り返り 話のベースとして触れた gomobile が何かと不安定なので、サンプルアプリを作る際は色々とハマりました。それも登壇ネタになったかなと考えると美味しい思いはできた気がします。 その後の懇親会でも、色々Firebaseの話ができたりGoのイベントなのにモバイルアプリの開発談義ができて大変楽しい時間を過ごせました。 また、「FlutterとGoのAPIの通信する」アプリを予想されてた思われてた方も居たようで、いい意味で予想をひっくり返せたのは良かったです。改めてGoの自由さを紹介できた点は良かったとおもっております。 時間配分を少し間違えて後半駆け足になってしまったのは反省として今後の登壇には活かしたい所存です。 アドリブ気味ではあったのですが、発表時にオープンクエスチョンを投げることができたのも満足でした。 懇親会 Remoで実施されており、前半はテーブルごとで雑談、後半はみんなでわいわいクイズ大会な流れでした。 remo https://remo.co/remo-101 特に @tenntenn さんの出題した go quiz めちゃくちゃ難しかった思いです。一番印象に残ったのはgo 1.16から入るembedに関してのクイズですね。勉強になりました。 これコンパイル通るか… embedが適応されるケース。変数の中身にファイルの内容が格納される。 //go:embed hoge.json var message string ただのコメントアウト(//の後にスペースがあるのでembedではなくコメント扱いになる) // go:embed hoge.json var message string 感想と謝辞 発表時の進行に関しては事前にリハーサルや説明を @micchiebear さんを始めとした運営の皆様の厚いフォローがあったので当日はスムーズに行うことができたので発表に集中できました。運営の皆様ありがとうございました。 また、スポンサードの窓口としても関わらせていただいたのですが、運営の皆様に手厚くフォローしていただいてありがとうございました。 https://gocon.jp/ に自社のロゴが載っているとわかった瞬間感無量でした。 私自身Go歴は1年ぐらいなので全然わからないことだらけではあったのですが、 参加された方々の Go Love が伝わってきてもっと私自身頑張ろうという気持ちにさせていただきました。 特に登壇資料を業務時間を割いて作ってる中フォローしてれた同僚にも感謝です。 宣伝 今回は趣味ネタでFlutter×Goを触れさせていただきましたが、 私たちの普段の業務はGo, Python, PHPを中心に、フロントからインフラまでを一気通貫で開発しています。 また、開発だけでなく・機能をグロース・サポートまで担当します。 そんな開発スタイルに興味あるぞって方は永野( @glassmonkey )にDMを送っていただくか、 下記のリンクから気軽にご連絡ください。 open.talentio.com 最後までご覧いただきありがとうございました。
アバター
こんにちは、デザイナーの河越です。 「BASE」では今年の2月に、注文時に購入者に送られる購入完了メールをリニューアルしました! baseu.jp これまで「BASE」から購入者に送られるメールはほとんどがテキストメールでした。 各ショップが工夫してネットショップ上でブランドの世界観を表現しているにも関わらず、購入者が受け取るメールがショップの世界観とマッチしておらず購入者に違和感を与えてしまうという課題がありました。 そこで今回、「BASE」から送られているあらゆるメールをHTMLメール化する「メール改善プロジェクト」が始まりました。 まず第一弾として購入完了メールがHTMLメール化されましたが、どのような経緯でリニューアルに至ったのか、デザインで工夫した点などを振り返りたいと思います! 購入完了メールのHTMLメール化の経緯 「メール改善プロジェクト」は、デザイナー主導で始まりました。 「BASE」の体験をより良くできそうな箇所があれば、デザイナーが主体的に課題を解決していける環境があるのはBASEの良いところだなと思います☺︎ プロジェクトのキックオフが終わったら、まずは「BASE」から購入者やショップオーナーに送信されているすべてのメールをリストアップしました。 購入完了メールや発送完了メール、お問い合わせ完了メールなど、購入者に送信されているものだけで32種類ものメールがあることがわかりました。 メールを洗い出したあとには、ショップオーナーや購入者の声を一番近くで聞くCS (Customer Support) チームに、メールに関してどのような問い合わせが来るかをヒアリングしました。 その結果「 ブランドのお店で注文をしたのに"BASE"からメールがきて混乱した 」「 銀行振込決済を選択して注文したあと、どこに振り込めば良いのかわかりづらかった 」と感じている購入者がいることがわかりました。 CSチームからのヒアリングを経て 購入者の購入体験を向上させること ショップのブランドブランドの世界観を壊さないこと を念頭に、まずは購入完了メールをリニューアルすることに決めました。 完成したデザイン ショップロゴや、設定しているSNSを表示 ショップがロゴを設定している場合、メールのヘッダー部分にショップロゴが表示されるようになりました。 これにより「"BASE"ってサービスからメール来たけどなぜ?」という戸惑いをなくすことや、ショップの世界観と大幅に解離したメールをなくすことで、注文時から一貫したショップでの購入体験を購入者に提供することを意識しました。 また、フッターパーツに設定しているSNSやショッピングアプリ「BASE」への導線を配置することで、ショップの普段の活動や商品について購入者が知りやすくなりました。 購入した商品がファーストビューに表示させることで、購入直後のわくわく感も醸成できたら良いなと思っています😆 レコメンドエリアを追加 そのショップで販売されているその他の商品を画像付きで表示させることで、リピート率の向上やショップの売上増加を狙う導線を新たに取り入れました。 まとめ 購入完了メールをリニューアルして早2ヶ月。 期待通り「その他の商品」からの購入が増えています。 また、メールが見やすくなったことで社内からも好評をいただいています! これまで以上にカスタマーサポートやカスタマーサクセスの声を聞きながらデザインを作ったことや、影響範囲の大きい品質向上に関われたことで達成感が大きく、このプロジェクトにアサインされてよかったなと思います。 また、「BASE」から送られているHTMLメールがほとんどなかったため、コンポーネントを1から考えて作れる楽しさも感じれました。 購入完了メールは「BASE」を利用しているwebのネットショップからの購入でも、ショッピングアプリ「BASE」からの購入でも見られるので、購入のさいにチェックしていただけると嬉しいです😉 今回は購入完了メールをリニューアルしましたが、「BASE」から購入者に送信しているその他のメールもどんどんリニューアルしていきたいと思います。
アバター
集合写真:写真真ん中よりちょっと上にて、左胸にBASEロゴがあるパーカーを着ている大津 こんにちは。Product Dev Divisionに所属している 大津 です。 今回、PHPerKaigi 2021にコアスタッフとして参加しました。私がなぜコアスタッフとして参加したのかという点も含め、カンファレンスの裏側についてレポートしたいと思います! PHPerKaigi 2021とは PHPerKaigi 2021とは、3月26日(金)〜3月28日(日)の期間で開催されたPHP系の技術カンファレンスです。今年で4回目の開催となります。 phperkaigi.jp オフラインカンファレンスだった例年とは違い、今年はニコニコ生放送を使って進行するオンラインカンファレンス形式になりました。 グリーンバックに写っている人が見る生放送用ディスプレイ (グリーンバックに写っている人が見る生放送用ディスプレイ) 弊社もゴールドポンサーとして協賛し、スピーカーや一般聴講者として数名が参加しました。以下の記事に参加レポートが書かれていますのでそちらも是非ご覧ください! devblog.thebase.in 舞台裏ではどんなことをしたの? PHPerKaigi 2021では以下のような仕事がありました。(私が担当したのはこの中の一部です!) プロポーザルの採択 デザインの採択(ロゴ、ノベルティ(ボックス)、Tシャツ、パンフレット、公式サイトなど) ノベルティ(ボックス)の発注、発送 公式サイトのコーディング 放送時に使う動画や録画されたセッション動画の編集 広報、宣伝 生放送の進行 セッション動画を再生するなどの生放送機材操作 動画終了後のアナウンス Ask The Speakerの司会 TL(Twitter)監視、お問い合わせ対応 アンカンファレンスに参加して賑やかす twitter や discord、ニコニコ生放送でわいわいコメントする ノベルティボックスの発送やセッション動画の編集、生放送の進行はオンラインカンファレンスならではの仕事だったかもしれません。 今年のPHPerKaigiはオンラインだけど、ノベルティ、あります! ステッカーやTシャツ、スポンサーさまご提供ノベルティが入ったステキなボックスがみなさんのお手元に! ノベルティ付きチケットは2/28まで販売中! (写真はデザイナーさんによる試作品です) #phperkaigi https://t.co/bjcXHQuoD9 pic.twitter.com/33Qr5hzOea — PHPerKaigi 2021 @3/26-3/28 (@phperkaigi) 2021年2月25日 生放送と録画用の機材 一般聴講者やスピーカーの方々は終始完全オンラインでしたが、大体のスタッフはオフラインで作業していました。 物理的な受付がない分お問い合わせが増えることを予期して、TL(Twitter)監視やお問い合わせ対応の人数を増やすなどの対応もありました。 生放送の進行をするスタッフも、それぞれが聞きたいセッションを休憩時間にしたり、Ask The Speakerでスタッフも質問を投げかけてみたり、スタッフもアンカンファレンスに参加するなど、スタッフ自身が楽しむということも心がけながら良いオンラインカンファレンス体験を目指して裏で動いていました! スタッフが念(?)を送っている時の様子 コアスタッフとして参加し続ける理由 実はPHPerKaigi 2020でもコアスタッフだったので、コアスタッフは2回目となります。 私がコアスタッフとして参加し続けるモチベーションについてお話しします。 いちエンジニアとしてメリットがある コアスタッフとして参加すると、仕事を通して様々なメリットを得ることができます。 例えば、プロポーザルの採択の際に自分がコアスタッフであれば、自分が聞きたいセッションを採択しやすくすることができます。もちろんプロポーザル採択の観点は色々あるため、必ず叶うというわけではありません。ですが、観点の1つとして「スタッフがいち参加者としてこのセッションを聴いてみたいか」があるため、可能性は十分にあります。 またイベントを運営するにあたって様々な仕事をこなすことになるため、イベントの運営力が上がります。カンファレンス運営のノウハウは、規模を問わず他のイベント運営でも活かすことのできる点は多いです。自分がイベントの開催をすることになった時、とても役に立ちます。 他にも、PHPコミュニティに所属しているスタッフと仲良くなれる、スタッフはカンファレンスに無料で参加できるというようなメリットもあります。 お世話になっているコミュニティに貢献したい BASEでは、私たちが使っている技術スタックのイベント・カンファレンス・勉強会への参加を推奨しています。 その理由の一つとして、「自分たちが使っているOSSや技術情報を発信している方々へ貢献できるから」ということを弊社では挙げています。どのような行動が貢献に繋がるのでしょうか?私なりに例を挙げてみました。 聴講者として参加し、当日のイベントの様子を口頭やブログなどで伝えること スピーカーとして登壇し、自分の知見を広めること。 カンファレンススタッフとしてイベントを盛り上げること。 上記のいずれもコミュニティの活性化へと繋がるので、全て立派なコミュニティへの貢献活動だと私は考えています。その中でも私は、PHPerKaigiのカンファレンススタッフとしてイベントを盛り上げるという形で貢献することを選びました。 PHPerKaigiは、私が技術カンファレンスの中で 初めて登壇した イベントです。そのことがきっかけで他のカンファレンスでも登壇することになり、登壇を通じてエンジニアとして成長することができました。 初めて登壇したカンファレンスがPHPerKaigiで思入れがあることと、同じように誰かへ成長の機会を与える側になりたいという想いで、コアスタッフとして参加していました。 最後に 来年もPHPerKaigiが開催されるならば、コアスタッフで参加したいと思っています。参加した皆さんにとって良いカンファレンスであったのであれば幸いです! また、カンファレンススタッフだけでなくスピーカーとして登壇すること、聴講者として参加ブログを書いてイベントの様子を広めていくという形でも貢献し続けていきたいと思います!
アバター
この3ヶ月で行ったBDIの内容を紹介します こんにちは、デザイナーの渡邊です。 今回はBASEのデザインチームが行っている勉強会「BDI」の内容をご紹介したいと思います。 BDIとは? 『BDI』は「BASE Design Inspiration」の略。 2018年の秋頃から活動している、デザイナーがやりたいことを持ち寄って、 デザインに関する幅広い知見をみんなで楽しく学ぶことを目的とした任意参加の社内勉強会です。 BASEのデザイナーであれば、デザイナーだけでなく誰でも参加することができます。 Inspirationの名の通り新たなひらめきにつながる新しいトピックを取り上げることも多くあります。 過去にはBDIのロゴも制作したりしました↓ devblog.thebase.in BASEのデザイナーがどんな活動をしているのか気になっている方に読んでいただきたいのはもちろん、 社内勉強会やワークショップのネタとしてもご活用くださいね! 1月 新年!書き初めで作字を学ぼう 新年ということで、お正月らしいオンライン書き初め会を開催しました。 デザイナーだけでなくいろんな方に参加していただきたかったので、 作字のコツを解説する簡単なLTを冒頭に実施 初心者歓迎ムードを打ち出し! 手書きOK、写真を撮ってアップするだけ などなど、参加ハードルを下げる工夫をしました。 見る専の人も楽しめるように、ファシリテーターの手元のiPadをミラーリングしてリアルタイムに作字が出来上がる過程も見てもらいました。 できた作品はJamboardに貼り付け、みんなで観賞。 褒め言葉の嵐でさながらボディビル大会のようになり、かなり盛り上がりました! 身近なデザイン4大原則を分析してマスターしよう デザインの基本となる「デザイン4原則」を実際に使われている事例から分析するワークショップをしました。 デザインの基本となる「デザイン4原則」を心理学にも触れながら解説するLT 事例を見ながら4原則のどの要素が使われているかを分析 実際にかんたんなワークショップで実践 と、マジメでしっかりタメになる会でした! 丁寧なLTは初心者にもわかりやすく、デザイナー以外の方も「なにが良いデザインなのか」を判断するための大きなヒントになる内容でした。 スライド/LP/パンフレットを観察しながら4原則と照らし合わせることで、レイアウトへの理解がより深まりました。 また経験豊富なデザイナーが「なんとなく」で出来てしまっている情報の強弱も、改めて原則を学び直すことで根拠あるデザインなんだな〜と振り返ることができた会になったと思います! 2月 ノーコード系ツールを触ってみよう コーディングを必要とせずにシステム開発やアプリケーション開発ができるノーコードツールが盛り上がっているということで、BDIの時間でみんなでおさわり会を開催しました。 前半はWebflow, Wix, STUDIOの3つのノーコードツールについて簡単に解説、実践編ではSTUDIOを実際に触りながら、感想や気付きを共有しました。 複数人の同時編集が出来るのは個人的に衝撃でした...!上手く使いこなせば爆速でLPとか作れちゃうんだろうな、といろんな可能性を感じてかなり盛り上がりました。 1時間で架空ブランドのコンセプトを作ってみよう BASEを利用するショップオーナーの仕事を追体験するため、ブランディングのワークショップをやってみました。 競合調査からターゲット決め、ブランド名を作るところまで1時間の短い時間でこなすのはかなり大変でしたが、 事前にmiroに用意したテンプレートを埋めることで(途中かなり急かしたりしちゃいましたが)やり切りました...! ここで制作したブランドコンセプトたちは後のBDIでロゴ制作、ショップ開設ワークショップに活かしていきます。 3月 俺みたいになるな!しくじりLT会 某人気テレビ番組のフォーマットを拝借し、 4名のBASE社員に、仕事や私生活でのしくじりとそれを経て得た教訓をLT形式で発表してもらいました。 今回は試験的に視聴者のslackコメントをリアルタイムで画面上に流してみたのですが、結果かなり盛り上がりました! ツールは こちら を使用させていただきました。 すぐに反応が返ってくるので、リモートでも登壇者が寂しい気持ちにならずに楽しく発表できてました。(しくじりの内容は社内限定なのでお見せできないのですが、涙と笑いなしには見られない素晴らしいものでした) 90秒ドローイングをやってみよう 美術・デザイン系学校でよく行われるドローイング。シルエットや構造を観察する目が身に付くといわれていますが、社会人になってからもやっているという人は少ない...。というわけでBDIでチャレンジしてみました! ドローイングのwebサイト を利用したり、社員の写真を使いながら集中してドローイング、後半はみんなで講評をしました。人によって書き込む箇所が違ったり、立体感の出し方に気付いて短時間で画力が成長している人が現れたりとなかなかためになる1時間でした。単純に絵を書くことは楽しい!と思い出せる機会にもなりましたので、みなさんも是非挑戦してみてください! まとめ 月2回ペースで開催しているBDI。最近はありがたいことに参加者も増えて盛り上がってきました。 紹介した企画は全てオンライン上で行っています。オフィスに出社できずチーム間のコミュニケーションが少なくなってしまいがちですが、こういった勉強会はゆるっとした絡みを作るのにもとても良い機会だと感じています。 これからもためになって楽しい企画を運営メンバーを中心に日々考えていきます。4月以降もお楽しみに!
アバター
BASE株式会社 ServiceDev Payment Group 所属の田仲です。 現在はエンジニアリングマネージャーを担当していますが、以前はサーバーサイドエンジニアとして開発をしていました。その頃の経験を紹介したいと思います。 BASEのエンジニアはPM・ディレクターから要件を聞き、設計を行います。 仕様や設計を元にサーバーサイドエンジニアはサーバーサイドの実装し、フロントエンドに関してはフロントエンドエンジニアが実装しています。 少し前のプロジェクトになるのですが、「送り状データダウンロードApp」の開発プロジェクトを担当した際にサーバーサイドだけではなくフロントエンドの開発にも携わることになりました。 普段、サーバーサイドエンジニアとして開発を行っているエンジニアがフロントエンドの開発も行うことで良い経験になったという話を紹介したいと思います。 送り状データダウンロードAppについて 商品を発送する際にダンボールなどの箱に送り状を貼りますが、出荷件数が多いショップでは1件づつ送り状を印刷して貼るのは手間が掛かり限界があります。手間を解決するために配送会社の集荷を利用することや外部倉庫への委託などの方法はありますが、費用が掛かるためショップの負担になります。 そのようなショップの課題を解決するために配送業者各社が提供している送り状発行システムを利用して送り状を一括印刷できるようにするためにBASEのシステムからCSVデータを出力できるようにする機能になります。 普段の開発プロセス BASEの開発プロジェクトは、サーバーサイドエンジニアとフロントエンドエンジニアがそれぞれアサインされ協働しながら開発を進めています。 今回の送り状データダウンロードAppの開発プロジェクトでは、サーバーサイドとフロントエンドのタスクはざっくりですが下記のようになりました。 サーバーサイド 固定値の登録・更新(データのCRUD) 各配送会社システムに合わせたCSVファイルの出力処理 フロントエンド 固定値の登録・更新画面 CSV出力処理モーダル 入力値のバリデーション/エラー表示 BASEはフロントエンドはVue.js サーバーサイドはCakePHPを採用しています。 サーバーサイドの担当でいうとAPIの実装がメインになり、フロントエンドの担当でいうと画面UIの作成やサーバーサイドのAPIを利用してデータの登録/更新処理を実装することがメインになります。 フロントエンドを担当した流れ 今回のプロジェクトにアサインされる際、エンジニアマネージャーから提案を受けフロントエンド開発の担当も兼務する形でアサインされることになりました。 組織図上、エンジニアはサーバーサイドとフロントエンドで分かれていますが別職種の開発を担当してはいけないというわけではなく、開発する案件規模やスケジュールなども関係してきますが手を挙げれば前向きに検討してもらえます。 私はサーバーサイドエンジニアとしてBASEに入社しましたが前職でVue.jsの開発経験もあり、フロントエンド開発にも興味があります。個人的に思うところですが、ユーザーに直接に見える部分はフロントエンドの実装になりますし、実装したコードがブラウザ上で目に見えて表現される部分はモチベーションも維持しやすく好きです。 また、BASEではフロントエンド開発を担当したことがなかったので経験してみたかったというのもあります。 苦戦したところ フロントエンドのアーキテクチャの理解 具体的にいえば、単方向のデーターフローを意識することやBASEのコンポーネントライブラリであるBBQの使い方です。 既存のコードを読むのはもちろんのことですが、理解を進めるに辺り参考になったのはドキュメントの存在でした。 BASEではドキュメント共有を主にkibelaを利用して行っているのですが、フロントエンド開発の手順書が用意されておりコードと合わせてドキュメントを参考にすることで比較的早く理解を進めることができました。 BASEのコンポーネントライブラリであるBBQについては、UIコンポーネントのカタログとしてStoryBookが用意されており各コンポーネントの使用方法が書いてあるので、開発環境で試しながら理解を進めることができました。 担当してみて良かったところ デザインの開発経験 サーバーサイドの担当としてアサインされてもプロジェクトの初期フェーズにおいてUIデザインetcに関して話し合いをするタイミングはあるのですが、詳細な部分については実装を進めてみないと分からないということもありました。 今回はフロントエンドも担当することで実装を進めながら、デザイナーの方とFigmaやAbstractなどのツールを使用してコミュニケーションを取りながら開発していきデザインを決定していきました。BASEでのデザイン開発の進め方やツールの使い方を知ることができて良い経験になりました。 コミュニケーションコストの削減 サーバーサイドとフロントエンドの両方を担当していて追加したコードについては大体は分かっていたので、PMからの仕様相談を受けた際なども回答しやすかったです。また実装後に行うQAでのフィードバック対応も原因箇所の特定がしやすく普段よりも素早く対応できたと思います。 フロントエンドの仕組み フロントエンド側がどのように動作しているか?をフロントエンドエンジニアの方やドキュメントを読んでなんとなく理解していたのですが、実際に担当して実装することで「なんとなく理解していた」部分がコードレベルで明確にできて理解することができました。 最後に 実際にできあがった画面が下記になります。 ※ CSV出力処理も担当予定だったのですが、上記の苦戦した部分で想定以上に工数使ってしまい途中で別エンジニアにヘルプを出しました…。 担当が多かった開発でもあったのでリリース時の達成感は強く、SNSなどのユーザーからの反応もいつもより嬉しいものでした。 サーバーサイドとフロントエンドを交互に行き来するような開発で頭の中のスイッチングコストはありましたが、エンジニア同士でのコミュニケーションコストは減ったりもしていたので、開発する案件の規模に影響するものだと思いますが今回のプロジェクトでいうと分業していたとしても最終的に掛かる工数としては大きな差は無かったかなと思いました。 また、コードレビューに関してはフロントエンドエンジニアの方がレビューしてくれるので心強かったです。 BASEはサーバーサイドエンジニアとして入社してもフロントエンド開発できないというわけではなく、手を挙げれば開発することができます。 普段は担当しない領域を担当してみることで不明瞭な部分が明確にできたので、今後の工数見積もりの精度向上やコミュニケーションコストの削減にもつなげることも出来ると思います。 最後にはなりますが、BASEを一緒に支えてくれるエンジニアを絶賛募集中です!より良いプロダクトの開発の為に一緒に働きませんか? open.talentio.com open.talentio.com
アバター
この度は、3/26 (金) 〜 3/28 (日) にオンラインで開催された PHPerKaigi2021 にゴールドポンサーとして協賛し、また 1 名のメンバーが登壇しました。 今回は上記メンバーの他に一般聴講者として参加した 2 名のメンバーからの参加レポートをお届けします! 発表内容と補足(東口) BASE BANK 株式会社の東口 ( @hgsgtk )です。PHPerKaigi 2021 では次の 2 つのトークをしておりました。 実践ATDD 〜TDDから更に歩みを進めたソフトウェア開発へ〜 (40 分) PHPUnit 9 時代のTest Doubleの作り方 (5 分) 1. 実践ATDD 〜TDDから更に歩みを進めたソフトウェア開発へ〜 発表に用いた資料はこちらです。 発表の際には、ニコニコ生放送・Discord・Twitter での実況が行われていました。Twitter でのリアクションは以下の Togetter にまとめています。 https://togetter.com/li/1688539 資料公開後土日にも関わらずたくさん反応いただき、2021 年 3 月 28 日(日)のお昼ごろには 100以上ブックマーク いただきホットエントリー入りする反響でびっくりしました。 翌週以降も『 テスト駆動開発 』を翻訳されている和田卓人さんに直接感想をしていただいたりと、多くの方の目に触れる資料となり「頑張ってよかったな」と感慨深くなりました。 Acceptance test-driven development(ATDD: 受け入れテスト駆動開発)を切り口に、Example-driven development (ATDD, BDD, SBE 等) の歴史と実践の総まとめになっている。圧倒的な情報量の資料 / “実践ATDD 〜TDDから更に歩みを進めたソフトウェア開発へ〜 / ATDD by genba…” https://t.co/CAxmhf0L6z — Takuto Wada (@t_wada) March 31, 2021 以降、収録された発表を見ながら補足情報として入れていた話・Ask the speaker でご質問・意見交換させていただいた内容を抜粋してまとめます。 イテレーション分の受け入れテストをまとめるアイデア テストが増えた場合のテスト速度とフィードバックループについてどういうアイデアがあるかな〜という話を Ask the Speaker でしていました。その中で取り上げさせていただいたのが、「イテレーション分の受け入れテストをまとめる」アイデアです。 これは 『Specification by Example』のChapter 10. Validating frequently で紹介されているアイデアです。 A common special case of breaking long tests into smaller packs is creating the current iteration pack. This pack contains the executable specifications that are affected by the current development phase. ATDD を実践するために用いられるツールには、概ねディレクトリ分けや Tag 等で受け入れテストを分類する機能が備わっています。テスト量が増えた際には、当該イテレーションで関心を持つ受け入れテストのみを絞れるようにすると、開発中のフィードバックループの周りを維持できるかもしれません。 手動テストの扱い 手動テストも Specification に記載するという話を現場での試行錯誤の 1 つのアイデアとして紹介していました。 発表資料から引用 これについて Ask the speaker でいろいろ意見交換させていただきました。 手動テストの Specification には何を書いている? 手動でテストしたいテスト項目について記載している、自動テストでは実行がスキップされるようなイメージ スプリントレビューでデモするような動かし方を書いていたりもする 手動テスト分を毎イテレーションテスター向けのキュー追加するとかできるとよさそう 手動テストの実行結果はどうする? 現状実行結果を記録する仕組みを用意してるわけではないが、イテレーションごとの実行結果としては記録したいモチベーションはたしかに。手動テストをイテレーションごとに Issue 作るなどするアイデアがありそう おすすめの一冊『Specification by Example』 ATDD について知っている方であれば 『実践テスト駆動開発 (Object Oriented SELECTION)』 (通称 GOOS 本)の外側のフィードバックループを想起される方が多いかと思います。 個人的なおすすめとしては、GOOS 本は技術的要素へのフォーカスがメインなので、ATDD 自体の前提となるコラボレーションを重視する考え方などを知るには違う書籍で補完したほうがいいかもしれません。逆に言うと、ある程度 INPUT した上で実践書として GOOS 本を読むと、とても効果が高いと思います。 GOOS 本は 原著 が 2009 年出版ですが、ATDD や BDD のような考え方はそれよりももっと前から実践されているものです。具体的には Ward Cunningham 氏が FIT を作った 2002 年にはそれらの考え方は確認されています。その後にも、2007 年に『 Test Driven - Practical TDD and Acceptance TDD for Java Developers 』が出版されており、そこでも ATDD の考え方と実践方法について触れられています。 様々書籍がある中で個人的に「現場で実践する上で一番良かったな」と思ったのは、 『Specification by Example』 です。 www.manning.com 著者の Gojko Adzic 氏はこれ以前に 『Bridging the Communication Gap』 にて、自身の実践方法について解説しています。しかし、 『Specification by Example』 は数十の企業へのインタビューを通じた実践事例集となっており、インタビューを通じて Gojko Adzic 氏自身の考え方がアップデートされた点なども解説に含んでおり面白いです。 実際に現場で実践するにあたって困ること・気になることについて網羅的に解説されていて、大変参考になりました。 おすすめをもう一冊『Agile Testing Condensed Japanese Edition』 『Specification by Example』 は翻訳版が現在無いのですが英語に抵抗のある方であれば、全体像としてアジャイルテスト自体を抑えてから本腰を上げていただくのが良いのかなと思います。初めての一冊としてのおすすめは『 Agile Testing Condensed Japanese Edition 』です。 leanpub.com Janet GregoryとLisa Crispinによる2019年9月発行の書籍『Agile Testing Condensed』の日本語翻訳版です。アジャイルにおいてどのような考えでテストを行うべきなのか簡潔に書かれています! ソフトウェアテストを実践するに当たり、アジャイルテストという観点から現場を見直してみると、大局観をもって挑めるのでおすすめです。 2. PHPUnit 9 時代のTest Doubleの作り方 発表に用いた資料はこちらです。 PHP 発表資料中に引用したいテストコード付近で若干修正点があったので PR を出したりしておりました(当日発表 1 時間前)。 github.com Lightning Talk で Ask the Speaker の時間があるのは新鮮な体験でした。「PHPUnit のバージョンどのくらい頻繁にあげてる?」みたいな雑談を @goodoo さんとさせていただいてたり、"カンファレンスの廊下感"がありました。 スピーカーとして参加した感想 事前収録を見ながら補足する体験がよかった 事前収録良かった。収録したものを見ながら様々自分でここはこういうのが関連して〜的な補足が入れれたのが自分としては良かったのと、参加した方の感想でもそれがあって、濃密度の高い時間になったという声をいただけておりました。 事前収録のセッションを聞きながら Discord(などのチャット)で登壇者ご本人が適宜補足をしてくれた結果、 めっっっっちゃ濃厚なセッションになったな… すごかった…✨ #phperkaigi #a — うえしー (@ueshiy) March 27, 2021 オンライン + 事前収録の組み合わせならではでした。5 回くらいリテイクしてるので誰もいない部屋で一人ただ喋り続ける時間は大変だったのですが、その分当日に濃密度が上がる点がいいなと想いました。 Ask the speakerに人がたくさん オンラインになってから Ask the speaker の時間は「はてどうしたらいいかな」と困る時間を過ごすことが多かったのですが、PHPerKaigi 2021 では Discord でのボイスチャットでたくさん人がいらっしゃっていて Ask the speaker が過ごしやすかったです。 その中で得られた新たなアイデアや言語化・分析の突破口みたいなのもたくさん会話の中から出て感謝です。 トラックごとにスタッフの方々がファシリテーターとしていらっしゃるのも時間の安心感としてありがたかったです。Track A ではスタッフの @akki_megane さんや ueshiy さんが、トーク前・トーク後に会話を回してくださりとても感謝です。たまたまマイクがオンになってる気配を見て唐突に @koyhoge さんに無茶振りさせていただいたり、オフラインで休憩室でわいわい雑談する感じがあってよかったですね。 質問する身としても、「とりあえずボイスチャット入っとこ」くらいのノリで Ask the Speaker に入れたのもハードルがひくくてよかった気がします。 運営の皆様ありがとうございました 発表資料内では題材システムとして fortee を例に取らせていただきましたが、例に取るくらいめちゃめちゃ作り込まれていて、トークの自動収録システムが用意されていたりと、小並感ある感想ですが「すごいな!」と驚嘆しておりました。 ノベルティのボックスやパンフレットのクオリティも高く、自宅から参加していましたが、「カンファレンスに来た」という感覚が強い時間でした。 コミュニケーションを促進する場の設計・運営をしていただいた PHPerKaigi 2021 運営スタッフの皆様ありがとうございました! 参加レポート(小笠原) BASE に 2021 年 2 月に入社した小笠原奈々( @cureseven )です。土日ほとんどずっと聞いていたので全体を通しての感想を先にお話しします。 学生の頃からちょっとずつ各方面のカンファレンスに参加しているんですが、学生の頃は「エンジニアってどんな感じの人たちで、どんなことしてるんだろう?」の興味で行っていたので内容がさっぱりわかってなかったのですが、社会人になって、業務をしていく中で実体験を元に話が聴けるようになってきたことを実感しました。 反面、新しく聞く話も多く、まだまだ知らないことたくさんあるなあと感じました。 PHPで学ぶ、セッションの基本と応用 fortee.jp あまり体系的に学ぶ機会のない Session の概要がきちんと整理された、初心者にも優しい内容でした。 そもそも Session という言葉の使い方が曖昧で、正しくは「Cookie を使った Session 管理」。元々ステートレス(状態を持たない)なプロトコル World Wide Web を、ビジネス利用しだし、状態を持ちたくなったため生まれたのが Cookie という技術です。 Cookie は 4KB の制限があるので、Session ID というキーを引き回し、それを元にセッションストアにアクセスしデータを取得することによって状態を表現します。 最低限の知識として Cookie に属性が設定されている。 Session 管理を狙った攻撃がある。WAF ではそれを考慮しているので安全に $_SESSION を扱える。 保持された情報によってバグが起こりやすいので、Session 管理の設計は大事。本当に Session 管理すべきなのか設計を見直すことも必要かもしれない。 セッションはファイルなので、サーバの台数を増やしてスケールアウトすると参照するセッションが別れてしまうので共通のセッションストアを見るなどする ということが紹介されていました。 Cookie を使った Session の知識はかなり曖昧でした。この発表を通して用途と注意すべき事項がわかったので、今後の開発に生かすことができそうです。 PHP8になった今の時代に、PHPの「エラー」「例外」そして「Error」をおさらいしておこう fortee.jp エラーと例外とは何かを比較すると以下です。 エラー 正常な処理ができない。難しそうな状況 プログラミング的に間違っている PHP が発生させるもの 例外 事前条件、事後条件が満たされない状態になる時吐くもの 契約プログラミングにおける契約的に間違っている プログラム自体が発生させ、自身でコントロールしたいもの 改めて日本語にするのは難しいですが、感覚的にエラーと例外は自分の中では区分できているかなと思います。 このセッションでは、「エラーも例外もまとめて投げる \Throwable を闇雲に使うべきではない。」「例外を投げるにしても、捕捉してそれをどう扱いたいか明確なものを投げよ」ということを主張されていたと思います。 曖昧な throw を残すことは、何が起こるか不安な状態のままにしておくことと理解しました。 \Throwable は \Error \LogicException \RuntimeException をキャッチします。 \Error \LogicException は本番では起こりえない状況なので throw せず、本番環境に乗せる前に修正すべきです。対して、 \RuntimeException は本番ではどうしても起こるものなので、捕捉して回復する余地のある場合は回復する処理を書き、どうしようもない場合は諦めるのが良いと主張されていました。 今まで正しい理解がないまま \Throwable を書いていたのでこちらのセッションを聞いて理解が進みました。想定されるエラーと例外を考えるようにします。 自前の Exception を定義し、どういうコンテキストのエラーかをエラー名を見ることによって把握できる、という話もありました。 今私が参加しているプロジェクトではちょうど自前の Exception を作っていたのでよかったです。 レイヤーに合わせた抽象度の Error にして渡すのが良い、という話があったので、DDD の考え方は例外にも当てはめるべきということが新たな発見です。意識して書くようにしたいと思いました。 そのコード、フレームワークの外でも動きますか? fortee.jp tech.quartetcom.co.jp このセッションでは、フレームワークに依存しない書き方を実践を通して紹介されました。40 分のセッションのうちに Laravel から Symfony にフレームワーク移行するデモンストレーションが入っていて驚きでした。 フレームワークから業務ドメインのコードを独立させておくことで、後方互換性を高くすると、リプレイス・リニューアルの時にまる写ししなくて済みます。 今私が参加しているプロジェクトでは保守性を高めるために DDD で開発しているので、参考になりました。 デモンストレーションの中でほとんど変更点がなく、名前空間や view の表記方法をちょっと修正したくらいのものを別のフレームワークに持ってきており、フレームワークに依存した書き方をしなければこんなに簡単に移行できるんだと改めて DDD で開発するありがたさがわかりました。 独立パッケージにするのも賢いと思い、検討したいです。 参加レポート(炭田) Service Dev Section に所属しています炭田高輝( @tac_tanden )です。自分は 2021 年 3 月 27 日(土)〜3 月 28 日(日)の 2 日間参加しました。 PHPerKaigi は去年初めて参加して今年で 2 回目の参加です。今年は新型コロナウイルス感染症の影響でニコニコ生放送でのオンライン開催でしたが、どのセッションも熱いトークばかりでオフラインに負けず劣らずとても楽しいカンファレンスでした。この場をお借りして運営スタッフ/スピーカーの方々にお礼申し上げます。ありがとうございました! 各セッションでは、日頃開発をしているだけでは出会うことが中々難しい様々な知見にふれることができ、とても勉強になりました。自分が聴講したセッションの中から特に気になったセッション 3 つのレポートをしたいと思います。 PHPでCSVを安心して扱うために fortee.jp github.com CSV への認識が 180 度変わったセッションでした。恥ずかしながらこのセッションを聞くまでは、CSV は正規化されたお手軽なデータ形式という認識でいました。 なぜこのような認識を持っていたかというと、今まで自分が携わった開発だとサービスの管理画面からデータ入稿をするのに便利だから使う程度の事しかしてこなかったからだと思います。 そのような場面で自分が扱っていた CSV データは「信頼している人が作成した信頼性の高い」という但し書きがついた CSV データだったんだなということを痛感させられました。 CSV データを処理するエンドポイントを、悪意があるユーザを含む様々なユーザが利用する場合、入力される CSV は文字コードや改行コード 1 つとっても様々なものが想定されます。また、クラッキングしようとするような悪意があるデータも含まれる可能性もあり、それらに対して全て考慮した実装にする必要があります。 セッションでは、PHP で CSV を扱う複数の手段とともに、それらの長所/短所やベストプラクティス、様々な文字コードへの対応を解説しながらどう PHP で CSV を扱うのか知ることができともて勉強になりました。次回 CSV を使った機能を開発するときに、ライブラリの利用やその内部実装を参考にさせていただきたいなと思いました。 モックの泥沼から脱却するために、あえてDBにつないでテストしている話 fortee.jp データソースから取得したデータを整形したり何らかのロジックを適用したあとにデータを保存するといった処理のテストを行う場合、データに関連する部分以外の実装を細かなメソッドに切り出して、データに依存しないようにしてテストすることで、データ入出力まわりのテストの複雑さをさけてユニットテストを記述できると思います。 しかしこの場合、細かく切り出した複数のメソッドを協調させて使うようなメソッドのテストが最後にどうしても残ってしまいます。これらのメソッドをテストする場合、どうしても実装が複雑になってしまい、またデータへも依存するのでテスト用のデータの生成するのにもコストがかかります。 場合によっては、このようなメソッドを組み合わせたメソッドのテストは行わない戦略を取る場合もあると思いますが、自分個人としてはそのような Large サイズのテストもできるだけ実装したいと思っています。 このセッションでは、上記のようなデータにも依存し、かつ処理も複雑なテストをする場合、モックを使ったテストではなく DB につないでテストする選択をした場合のメリット/デメリットについて解説されていました。 テスト時にモックはたしかに便利ではあるのですが、どうしても自作自演のテストになってしまい「作成したモックが正しいことをどう担保するのか」がすごく難しくなってしまうと実感しています。なので、発表にあったように「実データ」を実際に用意して、特にユースケースやシナリオといった役割のクラスのメソッドに対してはテストしていくことが良いのかなと改めて考えさせられました。 ちなみに、BASE では実装者がテストで所望のデータを生成できるよう、Fabricate というライブラリを利用しています。 Fabricateの記事 devblog.thebase.in devblog.thebase.in 改めて、テストの意味と役割について考えさせられたセッションでした。 作って理解するDIコンテナ fortee.jp tadsan.fanbox.cc PHPerKaigi 最終日の trackA は DI(Dependency Injection)祭りでした。 その中でも自分がすごく勉強になったのがこちらのセッションでした。依存とは何かの初歩の部分から依存にどう立ち向かうのか、最後には DI コンテナを実装するところまで詳細に解説されていました。特に、依存とはなんなのか、なぜ DI という考え方を取り入れるべきなのかを今一度自分の中で整理することができました。 自分は DI をという概念を利用して実装はしています。ですが、DI コンテナのようなライブラリは利用しておらず、発表中は恥ずかしながら理解が追いつかない部分がありましたが、セッションの後に資料を見返してなんとか理解したつもりです。また、この発表を通じて初めて PHP-DI を知ることができました。 最後に 今回弊社は登壇者含めて計 3 名のメンバーで参加させていただき、たくさんの参加者の方々や発表にふれることができ、とても充実した時間を過ごさせていただきました。 それも実行委員長である長谷川さんをはじめ、実行委員会の皆様のおかげです。心より感謝申し上げます。 それでは、来年もまた皆様にお会いできることを楽しみにしております。最後までお読みいただきありがとうございました。
アバター