こんにちは。BASE株式会社でプロダクトマネージャー(以下PdM)をやっています、船坂です。 この記事は BASE Advent Calendar 2023 の13日目の記事です。 アドベントカレンダーもあっという間に折り返し地点、12月は日が経つのがあっという間ですね。 さて、この記事では、自分が今年関わったプロジェクト(以下PJ)の中から、特に特徴のあった2つのPJ、「BASE AI アシスタント」アップデートと、インボイス制度対応についてご紹介できればと思います。 前者はBASEのAI関連機能の総称である「BASE AI アシスタント」の機能追加という整理ですが、実質的にはAIを用いて新たな可能性を探る、新機能の開発プロジェクトです。どちらかというと輝かしいプロダクト開発的な側面が目立ちます。 後者は、ECプラットフォームである「BASE」上で起こるありとあらゆる取引をインボイス制度に対応させるという、対応必須な法対応かつ影響範囲の広い地味で大変なプロジェクトです。どちらかというと縁の下の力持ち的な作業となります。 これらのPJ、正反対かと思いきや、PdMとして両PJに参加していると似ている側面も持っていました。 それらをご紹介しつつ、PdMとしてやっていたことを書きながらふりかえっていこうと思います。 AI機能開発とインボイス対応、2つの共通点 これらの2つのPJ、大きくは以下の2点が似ていました。 要件を定める際に、制約が発生する リリース時期が非常に重要な意味を持つ それぞれについて詳しく説明します。 要件を定める際に、制約が発生する 各PJにおいて、自分はPdMとして作るものを決める立場で参加するわけですが、どちらのPJにおいても、要件を定める際の外的要因の影響力がとても大きかったと感じました。 AIの場合:できるかできないかはAI次第でもある AI機能においては、目下の施策の要件を考えるためには「現時点でAIにできるのか」「それは自分たちでもできるのか」が非常に重要になってきました。PJ初期では、企画を作ったあとにAIを触ってみると、想定していたよりもAIを利用してアウトプットのクオリティを上げることができず、現状では諦めるといった決断をしたアイデアも発生しています。(画像生成系など) これまでの機能開発においてはエンジニアが「できそうだ」と想定できれば、それはほぼほぼ実現できたわけですが、AIをくみこんだ機能開発では、ソリューション実現の根幹にAIがいるため、AIも含めてクオリティを担保できなければ、リリースまで持っていくことができません。 企画を検討する中で、早い段階でAIに指示を出すためのプロンプトを作成し、どこまでAIができそうか、アテをつけながら企画することが非常に重要でした。 インボイスの場合:インボイス制度が絶対的な存在である 言うまでもなく、インボイス制度対応の要件に影響を与えるのは、インボイス制度です。BASEにおけるインボイス制度対応の内容が決まるまで、大きく下記のような流れで進行していきました。 インボイス制度概要を理解する BASEにおける全ての課税取引を洗い出す BASEに関連する部分のインボイス制度の内容を更に詳しく理解する。法的に定められた「しなければならない」「することが望ましい」を把握する 「しなければならない」「することが望ましい」ことの実装方法を調査(※)し、ユーザー体験を考慮しながら各取引のインボイス対応方針を定める。 ※ここでの調査とは例えば「メールでインボイスを送ることは認められるか」など、法的に認められる実装方法を調査すること 感想 見てきたように、どちらのPJも、PdMはそのドメインの知識をかなり深くまで知ることが要求されました。というより、筋の良い施策を考えるためには、制約を生み出す要因について詳しく知らないと、定義の難しい領域のプロダクト要件を的確に設定することができないのだと感じます。 外的制約がたくさん発生する複雑な領域こそ、PdMを設置する意味が出て来るんだなと改めて感じました。 リリース時期が非常に重要な意味を持つ AIの場合:リリースタイミングでのAIの話題性 生成AIの劇的な進化が注目を集めて以来、様々な企業がAI機能をリリースしています。現在は、毎日のように新たなAI関連ニュースを目にします。 BASEとしては、今年の4月に出した「BASE AI アシスタント」では、まずはとにかく早くリリースしてAIの流れにしっかり対応しにいきました。逆に、この間の12月6日に新たにリリースした新機能3つは、本当に価値のある機能を提供することを目指して、スピード感は持ちつつも、確かに価値を感じてもらえるであろう機能を検討してリリースしました。 社内では、早期に一つずつリリースする方向性なども含めて様々な議論を行いましたが、結果的には、「BASEらしく、かんたんに使えてしっかり価値を感じられる」機能をいくつかまとめてリリースすることを優先した形です。 AIの話題性が刻一刻と変わる状況を見ながら、どのような単位でいつリリースするか、検討した結果が今回のリリースとなっていました。 インボイスの場合:どのような状態でXデーを迎えるか インボイス制度対応については、2023年10月1日のインボイス制度適用開始日は動かないので、それ以前でどのようなタイミングからなんの機能を提供するか、実装順やリリース日の決定、事前告知告知はどのように行うかを非常に重視しました。 BASEでは、インボイス発行記録を保持するDBの一ヶ月分のデータをまるっと経理のデータと突合して、金額のズレがないか(=実装漏れが無いか)のテストをする必要がありました。これは実装後、1ヶ月間の集計期間をおいたうえで初めて実施できるテストだったため非常に期限がシビアでしたので、各種取引において、DB書き込みまでの作業のみ先行して全て実施してから、突合テスト対象外の部分の実装を進めるなど、開発作業順を工夫しました。 また、今回はBASE株式会社自身だけではなく、BASEをご利用のショップもインボイス制度に対応できるようにしたので、制度開始ギリギリでの機能リリースではなく、インボイス制度対応の準備ができる期間を想定したうえでの機能リリースとなりました。 インボイス制度対応のために必要な「適格請求書発行事業者の登録番号」だけは早めに入力して貰わないと制度開始日から自動でインボイスを発行することができないため、登録番号を入力してもらうフォームだけ先行リリースして設定期間を設けるなど、どの機能がいつまでにあれば全てのショップが10月1日に対応できているのか、時期を調整しながら制度開始当日を迎えました。 感想 これらのプロジェクトはGo-To-Marketの視点が非常に重要で、「要件を決めたら、あとは頑張って作って、でき次第出す」というようなシンプルなPJではなかったのが特徴でした。PdMとしてはどのタイミングでその機能を提供することで価値が最大化されるのかを真剣に考える必要があり、良い経験になりました。 ブランディングチームやお問い合わせに対応していただいてるチームなどと連携しながら、どういう状態を目指してリリースするかをすり合わせるのが重要だと改めて感じました。 まとめ 今回はAIとインボイス対応という、一見すると正反対の2つのPJの共通点として、 要件を定める際に、制約が発生する リリース時期が非常に重要な意味を持つ というよく似た側面をご紹介しました。 自分はPdMとして色々なプロジェクトに参加していると、ついついその施策ごとに異なる人格を作ってしまいがちというか、意外と学習したことを転用できると気づかなかったりするんですよね。 こうやってあとから共通点を抽出してみると様々なPJに転用できるような汎用的なナレッジが見つかって、いいふりかえりになるなと感じました。 PdMは役割の定義が難しい職種の一つだと思いますが、色々なことを経験できるのは、魅力の一つでもあります。 様々なことに興味を持って、学習をしながらできることを増やしていくことに楽しさを感じる方は、ぜひプロダクトマネージャーというキャリアを検討してみて下さい! そして、興味があればBASEも検討してみてもらえるともっと嬉しいです! 明日は @zan さんの記事です!お楽しみに。
この記事は BASE Advent Calendar 2023 と 身の回りの困りごとを楽しく解決! by Works Human Intelligence Advent Calendar 2023 の12日目の記事です。 はじめに こんにちは、BASE BANK Division で資金調達サービス「 YELL BANK 」の開発を担当している Doarakko です。 BASE BANK Division については、最近社内異動制度を使用して入られた方がブログを書いてくださったので気になる方は読んでみてください。 参考: フルサイクルエンジニアリングの第一歩を進める - BASE BANKでの新たな挑戦 昨今リモートワークが増えていますが、弊社でも出社とリモートを組み合わせたハイブリッドワークとなっています。 出社頻度が少なくなったことで「オフィスに全然人がいない…」「なんだか職場が暗いな」とぼんやり思われている方が多いのではないでしょうか。 そこで今回はその課題を解決するために「職場を明るくする」ための画期的な装置を作りました。 作成したもの BASE でリリースが行われるとオフィスにあるライトがカラフルに光り出すというものです。 youtu.be 何だかリリースするのが楽しくなってきましたね。 ちなみにこちらのライトスタンドは弊社の福利厚生「 BASE加盟店の購入補助制度 」を使用して購入したものです。 こちらの制度では毎月1万円まで BASE 加盟店で買い物することができます(2023年12月現在)。 構成 Slack Platform 上で動かしているアプリが、リリース完了のメッセージをトリガーに SwichBot API にリクエストを送信することでライトを光らせています。 SwichBot にはスマート電球以外にも多くの IoT 製品があり、その多くが Web API からも操作することが可能です。 参照: OpenWonderLabs/SwitchBotAPI Slack Platform とは Slack の 次世代プラットフォーム機能 とは、今までより簡単に Slack アプリを開発、実行できる基盤機能です。 Slack の有料プランでのみ使用可能となっています(2023年12月現在)。 ポイント Slack アプリの実行基盤(AWS Lambda、Heroku、etc)を用意する必要がない デプロイは CLI で行い、アプリの作成もマニフェストファイルから自動で行われる ブラウザ上でボタンぽちぽちする必要がない Slack のアプリ作成フレームワーク「 Bolt 」 は動かせない 専用の SDK (ランタイムは Deno)を使用する必要がある Bolt よりも型サポートが強力 Bolt とは構造が完全に異なっており、癖がある(個人的に)ため初期の学習コストは少し高め データストアも提供している DynamoDB のシンタックスで使用可能 ワークフローの実行回数のみが課金対象のためいくらでもデータを保存することができる(2023年12月現在) 参照:Slack プラットフォームの機能と料金プランガイド 実行基盤を用意する必要がないというのは魅力的すぎます。 ただ現在の仕様だとアプリが使用されるチャネルをハードコードする必要がある(Bolt の場合はチャネルに入れるだけで OK ですが)ため、使用されるチャネルが頻繁に増えるようなアプリには向いていません。 workflow: `#/workflows/${Workflow.definition.callback_id}`, event: { event_type: TriggerEventTypes.MessagePosted, channel_ids: ["CXXXX"], filter: { version: 1.0, root: { operator: "AND", inputs: [ { // triggers only bot messages statement: "{{data.user_id}} == null", }, { operator: "OR", inputs: [ { statement: "{{data.text}} CONTAINS 'デプロイが完了しました'", }, { statement: "{{data.text}} CONTAINS 'リリースが完了しました'", }, ], }, ], }, }, }, Slack アプリを開発する方法はいくつかありますが、ちょっとしたツールを作る際に上記を許容できるのであれば Slack Platform 一択と言っていいのではないでしょうか。 コードは Doarakko/release-party に公開しているので、ぜひみなさんも職場を明るくしてみてください。 トラブル発生:社内ネットワーク環境が快適 自宅でのデモとブログを書き終えて意気揚々とライトスタンドをオフィスに設置しに行ったところ、トラブルが発生しました(冒頭のデモ動画は上述とは別の方法で光らせています)。 SwichBot を操作するためには本体を 2.4GHz 帯の Wifi に接続する必要があるのですが、なんと社内で接続可能な SSID の一覧に 2.4GHz 帯のものが存在しません。 慌てて情シスの方に確認したところ、バンドステアリング機能を使用して自動で帯域が切り替わるようにしているとのことでした。 バンドステアリング機能とは、Wifi に接続された端末を 2.4GHz と 5.0GHz のうち混雑していない帯域に自動的に接続する機能です。 こうして快適にオフィスで仕事ができていたんだなと知れたのは良かったのですが、それどころではありません。 ここまできて引き下がるわけにはいかないので別の方法で明るくすることにしました。 解決策:Raspberry Pi + Philips Hue Philips Hue は SwichBot と同じスマート電球ですが接続方法が異なります。 Wifi との接続はブリッジを介して行い、ブリッジとスマート電球本体との通信は ZigBee という無線通信規格で行われています。 Philips Hue の Web API へのリクエストはローカルのネットワークからしか行えません。 同じネットワークに接続した Raspberry Pi 上で Slack ボット(Bolt for JavaScript)を動かし、リリース完了のメッセージをトリガーに Philips Hue API にリクエストを送信する形でライトを光らせます。 当初は Web API が公開されている別のスマート電球を購入して API の向き先を変えるだけかなと思っていたのですが、私が調べた範囲では 5.0GHz 帯の Wifi に直接接続可能なスマート電球を見つけることはできませんでした。 どのスマート電球も 2.4GHz 帯の Wifi にしか接続できないのはおかしいなと思い色々と調べましたが納得のいく理由には辿り着けず、Q&A サービスの「Mond」に質問を投稿したところなるほどなという回答をいただけたので気になる方は読んでみてください。 参考:最近スマート電球についていろいろと調べていたのですが… おわりに インターネットから少し現実世界に飛び出したことで、IoT 開発ならではの楽しさを味わうことができました。 ただ家で遊ぶ用のラズパイがなくなってしまったので次回の購入補助を使って買いたいと思います。 明日の BASE Advent Calendar 2023 は @takumi_funasaka さん、 身の回りの困りごとを楽しく解決! by Works Human Intelligence Advent Calendar 2023 は @wanko_in_lunch さんが担当です。 お楽しみに!
はじめに こんにちは。シニアエンジニアのプログラミングをするパンダ( @Panda_Program )です。本記事は BASE アドベントカレンダー 2023 の11日目の記事です。 BASE のバックエンド開発では巨大なモノリスからモジュラーモノリスへの移行が進んでいます。この記事では、モジュラーモノリスの中で自分のチームが担当しているモジュールに導入した PHPStan のカスタムルールの導入とその効果について紹介します。 PHPStan は BASE のモジュラーモノリスなバックエンドシステムに既に導入されていました。モジュラーモノリスの中で PHPStan のカスタムルールは2種類あります。各モジュールが守るべき共通のルールと、それぞれのモジュール内で特有のルールです。 PHP のコード品質を担保する PHPStan は多くの開発現場で採用されていますが、具体的なカスタムルールの事例はあまり公開されていないように思います。そこで、自分たちが実際に現場で使用しているカスタムルールの一部を共有します。PHPStan のカスタムルールに関しては、どのようなルールを設けているか、また具体的なコードにどう落とし込んでいるかの2つの側面があります。特に後者の面で一定の価値があるのではないかと考えて記事にしました。 自分が所属しているチームでは、 CRM モジュールという BASE の CRM 領域の機能を担う部分を開発しています。本記事では CRM モジュールで適用するカスタムルールに限定して紹介します。 課題: レビューで同様の指摘をしていること ツールは何らかの課題を解決するための手段です。カスタムルールを作成するに至った理由も、コードレビュー時にどうも似たような指摘を複数回、いろんな人にしているかもしれないという課題があったからです。 ストレートに考えると同様の指摘を複数回することの解決策は、コーディング規約を作成したりモブプロ・ペアプロで担保することです。そこで開発チームの前提を共有すると、現在2つのスクラムチームがCRM モジュールの担当になっています。メンバー構成について、Aチームはバックエンドエンジニアが6名、Bチームはフロントエンドエンジニア2名+バックエンドエンジニア3名であり、自分もその中の一人です。 LeSS (Large-Scale Scrum)というフレームワークに基づき、2チーム体制で開発をし始めて1年以上が経ちます。しかし、当たり前のことですが、チームは均質ではありません。時間が経って互いのチームが LeSS に慣れたとしても、一人一人のスキルにバラツキがあったり、他チームからサポートとして来てくれている方が2名いたりと、コミュニケーションコストが減ることはありません。 以前はコーディング規約や細かい書き方のドキュメントは整備されていませんでした。このため、チーム歴が長い人がペアプロをして CRM モジュールではこう書くんだよと口頭で伝えるか、コードレビューで誰かがコメントすることで知識の平準化が図られていました。しかし、ペア・モブプロにも限界はあります。設計レベルの議論はチームを横断して行われるものの、「今はこう書くようになった」という細かい実装の知識はチーム内に閉じてしまいます。 自分は両チームのコードレビューをしているのですが、レビューコメントでその中で細かいコーディングレベルの話を共有することを心がけていました。同一モジュール内でも UseCase ごとに書き方がバラけてしまうを防ぐためです。 AチームのXさんのコードレビューで、プロパティが readonly ではない DTO を見つけては、コメントで「最近Bチームでは DTO は必ず readonly class にすることになったよ」と伝えたり、BチームのYさんのコードに switch 文を見つけては「過去に swtich 文は使わず match 式にしようという指摘があって、あの時はチーム内で特に議論はなかったけどやっぱり match 式使う?どうする?」と決定を促す質問をするうちに、両チームの過去のプルリクエストに遡り、当該コメントのURLを貼るのも流石に大変だなと思うようになってきました。 そこで、非同期で誰でも参照できるコーディング規約を作ることと、静的解析で実装レベルの表記揺れを防ぐことを目指しました。そのために、まずは意を決して(というほど大変でもなかったですが)直近3ヶ月の約180件のPRを見返しました。結果的に、期間は1週間ほどで開発のスキマ時間で過去のPRを辿っていきました。これは良い指摘だと思えるレビューコメントをPRの中からピックアップし、その内容を以下の3つに分類しました。 静的解析で検出できるもの 必ず守る方針 どちらでもいいもの 解決方針: コーディング規約を作る一方で、静的解析で表記揺れを検出する それぞれの解決方針は以下の通りです。 分類 対策 静的解析で検出できるもの PHPStanのカスタムルールを作成して CI で検査する 必ず守る方針 ドキュメント化されていなかった暗黙のルールだったのでコーディング規約という形で文書化 どちらでもいいもの 本当にどちらでも良いので特にルール化はしていない、ということを明示するために文書化 「必ず守る方針」も「どちらでもいいもの」については、マークダウンで文書化してモジュラーモノリスがあるレポジトリにコミットしました。そうすると、S3 にデプロイした VitePress 上で誰でも(もちろん社内の人限定ではありますが)閲覧することができます。この仕組みもちょっと面白いので、その話はどこかで書くかもしれません。 「静的解析で検出できるもの」については PHPStan で対応することにしました。なお、カスタムルール作成を検討するにあたり、ルールにするほどか迷うものは両チームにアンケートを取っていました。全て過去の PR でコメントがあったものです。 slack の投稿 1つ目の自身をインスタンス化する書き方については、本当にどっちでもいいので特に文書にも記載していません。2つ目の「テストコードで if 文を書けないようにする」ものは、カスタムルールを作成しました(下に実コードを載せています)。3つ目の PHP 組み込みの enum の生成テストについては、あって困ることはないが特に書かなくても問題ないということを「どちらでもいいもの」の文書に掲載しています。 (3つ目は、例えば String の Backed Enum SomeEnum::Foo がある時に、 SomeEnum::Foo->name === 'Foo' , SomeEnum::Foo->value === 'foo' であることをテストするかしないかというものです) カスタムルールの紹介 CRM モジュール内で実際に使用しているPHPStanのカスタムルールをいくつか紹介します。 DTO は Readonly Class にするというルール 名前の最後が Dto であるクラスは、必ず readonly class にするというルールです。readonly class は PHP 8.2 からの機能で、コンストラクタ以外でプロパティに代入ができないので DTO に最適です。 <?php declare ( strict_types = 1 ) ; namespace BaseInc\Modules\Crm\Tools\PhpStan\Rules; use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; /** * DTO は Readonly Class にするというルール */ final class ReadonlyDtoRule implements Rule { public function getNodeType () : string { return Class_ :: class ; } public function processNode ( Node $ node , Scope $ scope ) : array { if ( ! ( $ node instanceof Class_ )) { return [] ; } $ isInApplicationDirectory = str_starts_with ( $ node -> namespacedName -> toString () , 'BaseInc\Modules\Crm\Application' ) ; $ isRequestClass = str_ends_with ( $ node -> namespacedName -> toString () , 'Dto' ) ; if ( !$ isInApplicationDirectory || !$ isRequestClass ) { return [] ; } return $ node -> isReadonly () ? [] : [ 'DTO が Readonly Class ではありません' ] ; } } DTO には setter が不要なので、PHP 8.2 だと以下のようにシンプルに書くことができます。 readonly class MembershipMemberSummaryDto { public function __construct ( public MembershipId $membershipId, public UserId $userId, public MembershipName $membershipName, public MembershipMemberActionType $actionType, public int $count, ) { } } switch 文を禁止するというルール switch 文を使ってはいけないというルールです。CRMモジュール内では swtich はそもそも1箇所でしか使われていなかったので簡単に書き換えができること、switch でできることは match 式で簡単にできることから採用されました。 <?php declare ( strict_types = 1 ) ; namespace BaseInc\Modules\Crm\Tools\PhpStan\Rules; use PhpParser\Node; use PhpParser\NodeFinder; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; final class NoSwitchRule implements Rule { public function getNodeType () : string { return Node\Stmt\ClassMethod :: class ; } /** * @param Node\Stmt\ClassMethod $node */ public function processNode ( Node $ node , Scope $ scope ) : array { if ( !$ scope -> isInClass ()) { return [] ; } $ nodeFinder = new NodeFinder () ; $ switchNodes = $ nodeFinder -> find ( $ node -> stmts, fn ( Node $ node ) => $ node instanceof Node\Stmt\Switch_ ) ; if ( count ( $ switchNodes ) === 0 ) { return [] ; } return [ 'switch 文は使用せず、match 式に置き換えてください' ] ; } } アンケートの投票中に「このケースとあのケースは match 式で実現できないのでは?」という質問がありました。そこで「それは全部 match 式に置き換え可能ですよ」と返事をしたところ、全員一致で賛成になりました。ルール化の検討がチーム内で match 式の理解を深めるきっかけになってよかったです。 if 文のネストを禁止する 過去のPRを遡ると、「ここは早期リターンで書けるのでは?」というコメントがちらほらあったのでif 文のネストを禁止するルールを追加しました。 <?php declare ( strict_types = 1 ) ; namespace BaseInc\Modules\Crm\Tools\PhpStan\Rules; use PhpParser\Node; use PhpParser\Node\Stmt\If_; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; final class NoNestedIfRule implements Rule { public function getNodeType () : string { return If_ :: class ; } public function processNode ( Node $ node , Scope $ scope ) : array { if ( !$ node instanceof If_ ) { return [] ; } $ parent = $ node -> getAttribute ( 'parent' ) ; while ( $ parent !== null ) { if ( $ parent instanceof If_ ) { return [ 'if 文のネストは禁止されています' ] ; } $ parent = $ parent -> getAttribute ( 'parent' ) ; } return [] ; } } またテスト界隈では「テストで if 文を使うのは良くない、それならそもそもテストケースを分けるべきだ」という意見があり、実際に過去の PR で同様の指摘があったため、テストメソッドの中で if 文があれば検出するルールも書いています。 <?php declare ( strict_types = 1 ) ; namespace BaseInc\Modules\Crm\Tools\PhpStan\Rules; use PhpParser\Node; use PhpParser\Node\Stmt\If_; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; final class NoIfInTestRule implements Rule { public function getNodeType () : string { return If_ :: class ; } public function processNode ( Node $ node , Scope $ scope ) : array { if ( !$ node instanceof If_ ) { return [] ; } $ method = $ scope -> getFunction () ; if ( $ method === null ) { return [] ; } $ isTestMethod = str_starts_with ( $ method -> getName () , 'test' ) || str_contains ( $ method -> getDocComment () ?? '' , '@test' ) ; return $ isTestMethod ? [ 'テストメソッド内で if 文は使用できません' ] : [] ; } } Repository の参照系のメソッドの命名の OrFail を末尾に固定する とても細かいことなのですが、Repository の参照系のメソッドの命名が「findById OrFail 」や「find OrFail ById」のように、 OrFail の位置が固定されていませんでした。命名が Repository ごとに異なることは、レビューする側として気になったため、こちらもアンケートを取りました。投票の結果、末尾以外で書けないようにするルールを作りました。 <?php declare ( strict_types = 1 ) ; namespace BaseInc\Modules\Crm\Tools\PhpStan\Rules; use PhpParser\Node; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Interface_; use PHPStan\Analyser\Scope; use PHPStan\Rules\Rule; final class FindOrFailRule implements Rule { public function getNodeType () : string { return Interface_ :: class ; } public function processNode ( Node $ node , Scope $ scope ) : array { if ( !$ node instanceof Interface_ ) { return [] ; } $ target = 'OrFail' ; foreach ( $ node -> stmts as $ stmt ) { if ( $ stmt instanceof ClassMethod ) { $ methodName = $ stmt -> name -> toString () ; if ( str_contains ( $ methodName , $ target ) && ! str_ends_with ( $ methodName , $ target )) { return [ 'メソッド名は findOrFailByFooBar ではなく、findByFooBarOrFail のように、OrFail を末尾に記述してください' ] ; } } } return [] ; } } このルールを適用した結果、なんと同一 Repository の中でも findOrFailByXxx と findByYyyOrFail のように混在しているインターフェースを一つ見つけました。それを見た瞬間に「細かいルールだけど、これはこれでやる意味があったな」と思いました。 繰り返される些細な指摘が なくなった もともと上記のような細かい指摘の数が多いわけではなかったので、たくさんのPRを遡ったりチームでアンケートを取ったりカスタムルールを記述してルール化するコストより、スポットで指摘するコストの方が低いと判断してルール化してはいませんでした。しかし、最近同じような指摘が増えてきてるかもと思い、今回整理することにしました。 これらのカスタムルールの導入から1ヶ月が経ち、レビューでの些細な指摘事項はなくなりました。表記揺れや細かい書き方を PHPStan の解析で検出できるようにしてから、コードレビュー時に本質的なところに意識を向けるだけで良くなり、「ここは細かいけど見逃せない!」と思う回数が減りました。細かい指摘というものはレビューされる側も小さいといえどもストレスを感じるものだと思います。また、コメントをする自分自身も気が楽になったので導入してよかったと思いました。他の人のレビューコメントを読んでいても、些細な指摘事項から生まれる些細な議論は無くなったと思います。 ただ、過去の PR をたくさん遡ることの副作用にも注意が必要です。悲しいことに、PHPStan のルールを作成する前後の期間、無意識のうちに自分のレビューコメントの指摘が細かく、厳しくなっていました。 slack 何か最近自分のレビューコメントが過剰かもなと自分でなんとか気づけたこと、またフォローしてくれる同僚がいてよかったです。 明日は、TanaamiYukiさんの記事です。お楽しみに!
この記事は BASE Advent Calendar 2023 の10日目の記事です。 ちわ BASEから代わりまして、PAY株式会社(BASE株式会社グループ会社)のクリス @x86_64 です。数か月前、絵を買うよう執拗に勧めてくる人に言いくるめられ、額縁入りのライザのアトリエ複製原画だのデジタルアートブック特典付きのアーマード・コア6だのを入手しました。 この記事は私がセキュリティエンジニアとして日頃感じることや昔話、将来のイメージについてろくろを回しまくり、なんか宣誓を立てるものです。少しはPCI DSS v4.0の話もしますがあとは自分の話しかしません。 PAY株式会社とは BASE株式会社グループ会社で、同社をはじめとして国内のスタートアップなど多くの加盟店にクレジットカード決済サービス「PAY.JP」を提供する会社です。クレジットカード情報を加盟店のみなさまに完全に代わって安全に取り扱います。 IT業界の中でも特に厳格と知られるセキュリティ基準「Payment Card Industry Data Security Standard (PCI DSS)」に準拠していることが要求されており、もちろん弊社はこの基準の最新バージョンであるv4.0に準拠しています。 昔話をしよう ここからは完全に自分の話です。似た思いの人を探すため自分から情報を発信します。アクティブソナーです。 私は学生時代から現在の役を務めており、入社してから数えるとまもなく8年目になりますが、ここに至るまでには少年時代からの経験が積み重なっています。 時は遡り2005年、市井はとある大手サイトでSQLインジェクションに起因する情報漏洩が起こったことで震撼していましたが、この事件こそが私がWebセキュリティの門をたたくきっかけとなりました。つまりは私のセキュリティへの関心を喚起した原動力は、この不善なる者たちが示した威力でした。 セキュリティにはもともと善悪の二面性がありますが、私がセキュリティを学んだ情熱の根源はいたずら心でした。システムにちょっかいを出すと情報や特権をポロッと出すという現象には征服欲をそそられました。この背景があるので動機が不純であったのは認めざるを得ないのですが、幸いなことにその当時からハッキングを学び試せるサイトが合法的に存在していたので、知識と楽しみを追求する道が閉ざされることはありませんでした。 そのときから抱いてきた情熱を胸に、私は弊社のPCI DSS準拠を主導するだけでなく、社内でペネトレーションテストもしています。身もふたもない話をすると、この仕事もしているやむにやまれぬ事情は、弊社のシステムが複雑すぎるうえに更新頻度が高いせいで専門機関によるテストが永遠に終わらない状況にあるということではありますが、おかげで却って社内で先進的なレッドチーム活動を行う機会が常にあるので、挑戦が終わることはありません。 ペネトレーションテストはPCI DSSにおいては少なくとも年に一度欠かさず実施する義務があるため、数奇なことに私がただただいたずら心の満足のために求めてきた知識がほぼそのまま役に立っています。ハッキング趣味は人に言うと怪訝な顔をされることもあり、親には誤解されてやめさせられそうにもなったものですが、今どきはセキュリティを確保する活動の中でも最も精密で厳正な手順としてペネトレーションテストが当たり前のものになりました。クレジットカード会社やクレジットカード決済を行う会社があればあるほど善なるハッカーもそれだけ多くいる前提になっている仕組みなので、この巡り合わせも奇妙なものです。 今を模索し、未来を描く 私にはこの仕事をカッコいいと思える時とだるいと思う時が交互に来ています。充実しているのですが、義務です。義務の履行はお世辞にも楽しいとは言えないものですが、使命感が燃えます。これは世界を救える仕事です。 セキュリティは技術的に非常に深遠で複雑怪奇ですが、今の世界では企業にとってセキュリティの確保は必達の義務であり、また他方では市民生活のうえでも基本的なリテラシーとなってきています。誰しもがいつなんどきでも標的にされうるため、自らを保護する必要があります。 私は決して恐怖やパラノイアの煽動をしたいのではなく、情報化社会ではひとりひとりが守護者として行動することができるということを伝えたいのです。もはや誰も彼もがセキュリティの確保を義務化されたも同然であるこの時代は、セキュリティに造詣の深い人材がありとあらゆる所に遍在すべき局面になっています。専門分野も生活も違う万人がセキュリティを共通の文化としてともに擁することができれば、守護者のひとりとして幸甚に存じます。 千里の道も一歩から BASEを筆頭とする弊社グループはIT産業の上場企業であり、扱う情報資産の価値やそれに伴う責任の重さは計り知れません。このような重要なポジションにある企業として、弊社はグループが一体となって数々の取り組みを行っています。 ひとつの例としては、全メンバーに対し徹底的な情報セキュリティ研修を実施しています。このような取り組み自体は広く普及していますが、弊社グループではその研修ならびにテストをセキュリティ専門のチームがすべて内製している点が一線を画すると考えています。 ディテールにもこだわり、全員にそれなりに高度なセキュリティ意識が浸透するように取り組んでいます。 例えば、多要素認証の分野では適切な2つの要素のペアを選択する問題と、厳密には多要素ではない多段階認証 *1 を指摘する問題を出すなど複数の着眼点を用意し、受講者がセキュリティについて自ら正しい判断を下せるレベルに成熟した体系的知識を確実に持つように工夫しています。 2020年からのコロナ禍を起点に世界中の働き方が変わり、急速にWFH(いわゆるリモートワーク)が普及しました。弊社も例に漏れず大急ぎでWFHが導入され未来がどうなっていくのかまるで予測できない状況が続きましたが、現在はハイブリッドワークスタイルが定着したようです。この働き方を作れたのは世界の激変が発端の数奇な出来事でしたが、速やかなWFH移行に技術から体制まで全面的に携われたのも、セキュリティエンジニア冥利に尽きます。今後もこういった画期的な取り組みをたくさん行う機会があるでしょう。 おわりに(採用活動オチです) この仕事、どうですか?セキュリティエンジニアになりたいですか? 募集しています! この募集要項の技術要件も4割くらい私が書きました。 AWSのイベントで登壇したことがありますが探さないでください。そうしなくてもたぶん変なところで巡り会うので。 次回はシニアエンジニア @Panda_Program の記事です。お楽しみに! *1 : 具体的には ID/パスワード と 秘密の質問の答え のペア。これらは共に知識要素である
この記事は BASE Advent Calendar 2023 の9日目の記事です ごあいさつ はじめましての人ははじめまして、こんにちは!BASE BANK Divisionのフロントエンドエンジニアのがっちゃん( @gatchan0807 )です。テックブログに出てくるのは半年ぶりぐらいですね。お久しぶりです ちょっと大それた感じのタイトルを付けてしまいましたが、今回の記事では、先日 BASE BANK Divisionに社内公募という制度で異動して感じた BASE 組織との違いと、オンボーディングタスクでAWS ECSと格闘した記録をご紹介していこうと思います! また、20日の記事では私も含む、実際に社内公募制度を使って異動したメンバーの体験談や感想などをまとめたものが公開される予定ですので、そちらもぜひご覧ください! BASE組織とBASE BANK組織の違い まずはBASE BANKの組織構造や使用技術を簡単に紹介し、その上で、いくつか自分が関わってきたBASE組織と比べて感じた違いについてお話します ざっくりまとめると、BASE BANKの組織の特徴として主に以下のようなものが挙げられます👇 フルサイクルエンジニアを目指すという志向がより強い 関わる技術領域がより広く、Go / Pythonを使ったAPIの開発を行いながら、BASE側のPHP製アプリケーションとの連携やフロントエンドの対応も行っている チーム全体の人数がまだ少ないので、より全体感を持ってプロダクトの成長方法を考えながら、さらに打ち手を増やしていくための取り組みを行っている また技術的には、今後組織とプロダクトが大きくなってもスケールできるよう、以下のような仕組みで作られています 責務で分かれた複数のAPIを使った分散システム レイヤードアーキテクチャベースで整理されたバックエンドコード FE / BE間の連携を容易にするためにOpenAPIを使ったスキーマベースでのやり取り これらの仕組みは、1人で複数領域を担当する場合にはBASE側のリポジトリも含めて複数のリポジトリを開いたエディタ反復横とびすることが必要になることもあります(このあたりは絶賛練習中…) ここに関しては慣れが必要で大変なこともありますが、それでも依存関係やコードの責務が細かく分かれており、より見通しがしやすいという余りあるメリットがあります より詳しい内容についてはSpeaker DeckにあるBASE BANKチーム紹介資料とBASEチームのエンジニア向け会社紹介資料を見比べていただくと、もっとイメージしていただきやすくなるかと思いますのでそちらもぜひご覧ください! https://speakerdeck.com/base/basebank https://speakerdeck.com/base/for-engineers 異動後初タスクでAWS周りでハマったポイント ここからは、実際にBASE BANKチームに異動して1つ目のオンボーディングタスクで学んだこと、ハマったことについて書いていこうかと思います🕳🏃♂️💨 BANKチームに異動して新しい環境でコードを書き始めたところ、いくつかの問題に直面しました これらの問題をどのように解決したか、ケーススタディとして共有していきます オンボーディング時に開発用AWS環境を使用している場所があることに気づいた 自分の異動時には、昨日の @glassmonkey さんの記事( LocalStack/MinIO を導入して開発者体験が捗った話 - BASEプロダクトチームブログ )で紹介された minio / localstack 環境がすでに整っていました しかしながら、一部のGo製アプリケーションはまだそれを利用していない部分がありました これ自体は即座に問題が発生するものではありませんが、今後開発環境を新メンバーが構築するたびにAWSクレデンシャルを登録する必要があるため手間がかかってしまう状態になっていたため、オンボーディングタスクとしてDockerとアプリケーションの構成を修正してminio / localstackに対応することになりました ローカル環境での動作確認は問題なかったが、動作検証用環境へのデプロイで問題が発生した… BASE / BASE BANKには 「ローカル環境 / 動作検証用環境 / ステージング環境 / 本番環境」という4つのアプリケーション実行環境があり、今回はローカル環境での動作確認後、リリース前に動作検証用環境で問題なく動くことを確認しました とはいえ、動作検証用環境 自体はステージング環境ほど頻繁には使われておらず、基本的に各エンジニアのPCから直接 ecspresso のコマンドを実行する形でのデプロイが行われており、CIを介したデプロイは行われていませんでした(ステージング環境 / 本番環境へのデプロイはCIを介して行われます) しかし、 エンジニア全体に支給されるPCが去年から少しずつARMアーキテクチャ(M1系 Macbook)に切り替わった ことにより、ビルドをおこなう開発機とアプリケーション実行をするECSインスタンスのCPUアーキテクチャが異なってしまい、コンテナが起動しないという問題が発生するようになってしまいました(たまたま、このタイミングでこれに気づき、ハマってしまった形) さらに、その問題の確認中に、ECS側で何度もリビジョンを作成しようとしてしまっている(絶対に起動できないコンテナを起こそうとし続けてしまう)ことに気づかず、そのリビジョン内で同時に使用しているサービス監視ツールのコンテナをDocker Hubから取得する部分でレートリミットがかかるという問題も併発させてしまいました…😵💫 ECSのリビジョン設定を変更して問題を解決 対応としては、BANKチームで使用しているECSのタスク定義の設定ファイル( ecs-task-def.json )でデプロイ先のCPUアーキテクチャ設定を変更したり、本番用構成では使用しているログ用コンテナやサービス監視用コンテナを一時的に外したりして、まずは アプリケーションがECS上で単独で動作することを目的としてフォーカス しました その結果、アプリケーションの実装に問題はないことがわかったため、最終確認のためにステージング環境を使用し、最終的には問題なくBANKチームへの異動初のタスクを完了させることができました🎉 フルサイクルエンジニアへの第一歩が進みました このような経験を異動直後からすることができたので、これは自分もフルサイクルエンジニアへの第一歩を踏み出せたんだな…と噛み締めながら作業していました これまでのBASEチームでは主にフロントエンドの業務が中心であり、インフラ領域については仕様や実現したいことの相談メインで、実際の作業はSREチームにお願いするという形で進めることが多かったため、自分で手を動かせるようになったことは、とても学びがあり、非常に楽しかったです とはいえ、まだまだ勉強中ですので重大な問題などが発生しないようにしながら頑張っていきたいなと思います BASE BANKでは一緒にフルサイクルエンジニアリング目指して頑張っていく人も募集しています! 自分自身はBASE組織で2年半の間に身につけた知識を活用しながら、BASE BANKをさらに発展させていけるように頑張っていきたいと思います💪 とはいえ、BASE BANKを発展させていくためにはまだまだ人が足りていません… 先述の通り、BASE BANKチームにはスケーラブルな技術構成や組織構造の土壌があり、様々なプロダクトの改善を進めつつ、それらをより良くするための活動を行っているため、多くの学びがあってとっても面白いチームです ぜひ、カジュアル面談などにお越しください!お話しましょう! open.talentio.com https://twitter.com/gatchan0807 明日は10日目、 @x86_64 さんの記事です!お楽しみに!
はじめに この記事は BASEアドベントカレンダー 8日目の記事です。 LocalStack/minioを導入して開発者体験が捗った話 こんにちは、BASE 株式会社 BASE BANK Division でソフトウェアエンジニアをしています。 @glassmonkey こと永野です。 最近ではAWS上にVPCから環境を作っては、壊したりしています。 今回の記事では、AWSのサービスを使ったアプリケーション開発における、開発者自身のPCにおける開発、いわゆるローカル環境での開発者体験をあげた話をします。 YELL BANKについて 私が担当しているサービスであるYELL BANKは、BASEでショップを運営しているオーナー様に「気軽な資金調達」を提供するプロダクトになります。 yellbank-lp.thebase.com 現在のYELL BANKのアーキテクチャは以下のように複数のシステムコンポーネントが組み合わさることで実現しています。インフラにはAWSを利用しています。 BASE株式会社 BASE BANKチーム紹介資料 - Speaker Deck 特にYELL BANKは様々な計算を常日頃を内部的に行っており、それらの算出された結果が「どう推移した?」「異常は無いか?」を意識することがサービス運営上必要です。 そこで内部的な計算結果を定期的に記録することが求められており、そのためにAmazon SQSやS3を使って簡単なログ収集基盤を作っています。 実際の施策の一例にはなりますが、過去にショップカルテなるものを作ったので良かったらご覧ください。 devblog.thebase.in 最近のYELL BANKにおける開発環境の課題 YELL BANKでは前述のシステム実現のために、システムコンポーネントはAmazon SQSやS3に依存したものになります。 実際にAWS上に展開されたアプリケーションだと大きな問題はないですが、開発者の手元のPCのローカル環境だとどうでしょうか? 真っ先に考えられる方法としては、開発環境のAWS環境を用意しておき、そこで開発用のIAMを発行してアクセスキー・シークレットキーを扱うという方法が考えられるのではないでしょうか? 当初は開発メンバーの規模も大きくはなかったので問題にはなってはいませんでしたが、最近は体制も代わり開発メンバーが増えたこともあり、開発用のAWS環境を共有する点がボトルネックになることが増えてきました。 基本的にIaCしているものの、開発環境用のIAMはIaCの対象外にしていたこともあり、作業が煩雑でオンボーディングでのボトルネックにもなっていました。 また、セキュリティ的にもいたずらにIAMを増やす点は好ましくないはずです。 ローカル環境用のエミュレータを用意する チーム体制的にもローカル環境での開発がつらい状況になってきたので、ローカル環境でエミュータを立てる方向で対応しました。 今回ではLocalstackとminioを導入して解決を図りました。 LocalStackについて LocalStackとはローカルPC上でAWS上の各サービスを再現するエミュレータサービスです。 Lambda, SQS, S3, Dynamoといったメジャーどころは一通りカバーしています。 最近めでたくv3が出ましたが、多機能で高性能です。 www.localstack.cloud 使い方は簡単で以下をcompose.ymlなどに追加するだけです。 環境変数などの各種設定に関しての詳細は 公式ドキュメント をご確認ください。 localstack : image : localstack/localstack:3.0 healthcheck : test : [ "CMD-SHELL" , "curl http://localhost:4566/_localstack/health" ] interval : 2s start_period : 20s retries : 30 timeout : 30s ports : - "4566:4566" volumes : - localstack:/var/lib/localstack environment : - USE_SINGLE_REGION=1 - PERSISTENCE=1 - TZ=Asia/Tokyo これにより、各種コンテナからはlocalstack:4566(host上ではlocalhost:4566)に各種向き先を変えることでローカル環境上で各種AWS操作が可能になります。 例 aws s3 ls --endpoint localhost:4566 ただし、無料版と有料版でカバーしている機能に差があり、 そのなかでS3の永続化は無料版だと対応していないことがわかりました。 その他の各プランごとの機能のカバー範囲については公式の AWS Service Feature Coverage をご覧ください。 MinIO について YELL BANKのシステムは、S3上でデータを加工して利用者に提示するといった機能が多く、s3の永続化は死活問題といえました。 そこでLocalStackとは別にs3 api互換のminioを採用しました。 主な決め手はローカルスタックと同じようにendpointを変えるだけで気軽に立ち上げることができる点でした。 min.io MINIO_ROOT_USER は AWS_ACCESS_KEY_ID に、 MINIO_ROOT_PASSWORD は AWS_SECRET_ACCESS_KEYに対応します。 minio : image : minio/minio:latest healthcheck : # init スクリプトが完了する前に app が起動しないよう ヘルスチェックする test : [ "CMD-SHELL" , "curl http://localhost:9000/minio/health/liveh" ] interval : 2s start_period : 20s retries : 30 timeout : 30s command : [ 'server' , '/data' , '--console-address' , ':9001' ] ports : # 管理画面にアクセスできる - "9001:9001" - "9000:9000" environment : - MINIO_ROOT_USER=dummy-accesskey - MINIO_ROOT_PASSWORD=dummy-secretkey volumes : - ./services/infra/data/:/data ローカルで利用するときは以下のようなコマンドでs3 コマンドを各種利用することができます。 aws s3 ls --endpoirnt localhost:9000 また9001番ポートでも管理画面が用意されており、 IDには MINIO_ROOT_USER で設定した値、パスワードには MINIO_ROOT_PASSWORD を利用することで気軽に確認が可能です。 簡単にブラウズすることができ、アップロードされたファイルの内容も以下のように確認することができます。 変更後の影響 各種アプリケーション上で、AWSの処理を呼び出しを行っている点は変更対象になります。 基本的にはAPIコールのエンドポイントを、明示的に環境変数で設定することで実現しました。 たとえば Goのアプリケーションのaws sdk-v2の場合は以下のような形です。 client := s3.NewFromConfig(cfg, func (o *svc.Options) { o.BaseEndpoint = aws.String(os.Getenv("ENDPOINT")) } ) この変更に関してはオンボーディングタスクを兼ねて、新しくチームに異動してきた @gatchan0807 にやっていただきました。 詳細は明日の記事の「フルサイクルエンジニアリングの第一歩を進める - BASE BANKでの新たな挑戦」で紹介されるはずです。乞うご期待!! この対応で、当初の課題だったローカル環境の開発環境の悪さやオンボーディング時の複雑さの解消は無事に達成することができました。 最後に クラウド技術が便利になってきた昨今ですが、ローカルの開発者体験まで含めて技術選定することはなかなか難しいように思います。 今回のケースではエミュレータで頑張る方針取りましたが、IaCを頑張って個人ごとに環境をつくるという方法も取るといった他のアプローチも考えられはします。 皆さんはどういった方法を取っていらっしゃるでしょうか? 日々の開発者体験向上や、クラウドネイティブなアプリケーション作りにもっと邁進していきたいところですが、全然人が足りていない状況です。 よかったら @glassmonkey までDMやカジュアル面談含めてお待ちしています!! open.talentio.com
こんにちは! BASE 株式会社 BASE BANK Divisionでエンジニアのトップをやらせてもらっている @applepine1125 です。 12/3に Hatena-Blog-Workflows-Boilerplate に関する記事も書いたのでぜひ読んでみてください! さて、ここから本題です “夜な夜な.go”というイベントを始めました! 実は今年9月から、弁護士ドットコムさんと一緒に 夜な夜な.go というイベントを始めていました! ゆるく公式のドキュメントなどを一緒に読み、ドキュメントリーディングのハードルを下げたりみんなでワイワイ話すことでより理解を深めて行くためのイベントです。 現状は皆で読みたいドキュメントを募集して、皆で回し読む輪読会形式をとっています。 12/5時点で第4回まで開催しており、ちょうど Go 1.21で導入された新機能 slogの中で、メッセージやレベルなどの構造化されたログ情報を処理するHandlerの実装方法のドキュメントを読み終えたところです。 github.com なんで始めたの? Go Conference 2023 にて、BASEと弁護士ドットコムさんはスポンサーブースを出していました。 ブースに遊びに行った際に色々とお話をさせていただき、BASE、弁護士ドットコムさん双方ともPHPを使っていることや、新しい事業ではGoを使っていることなどで意気投合をし、ぜひ一緒になにかイベントをやりましょう!という話になりました。 テーマを決めるに当たり、自社アピールのためのLT会なども検討したのですが、どちらの会社もシンプルにGoを勉強する場を求めていました。 クローズドで合同の勉強会をやるのであれば、場をオープンにして色々な方と継続してGoに付いて知見を深めることができる場を作りたいと考え、輪読会形式で社外イベントとして開催しています。 夜な夜な.goで心がけていること 参加ハードルを下げる 輪読会と聞くと、ドキュメントを読みながら内容もきちんと即座に理解できないといけないのではないか? や、初対面の人達の中で色々読まないといけないの緊張する・・・など、かなりハードルが高く感じる方もいると思います。(正直自分もそう思ってます) そこで、夜な夜な.goでは参加ハードルを下げるために以下の2点を心がけています。 参加枠、盛り上げ枠に分ける スピーディに読み進めることよりもきちんと中身を理解することを心がける。寄り道や素人質問大歓迎 ドキュメントを回し読む参加枠だけでなく、見学しつつチャットで参加することもできる盛り上げ枠を用意することで、どんな感じか様子を見てみようかな?と参加しやすいようにしています。 また、ドキュメントによっては他のドキュメントへの参照やissueへのリンクなども多数あるため、ガンガン寄り道をして広く理解し、実際にThe Go Playgroundで動かしてみたりすることを心がけています。 途中参加しやすくする 一度ドキュメントを読み始めると、ドキュメントの量にもよりますがじっくり読んでいくため2, 3回分かかることもあります。 “それまでの内容を理解してないからその状態で途中参加してもなあ・・・” と参加を見送られてしまうのはもったいないので、回の最初にそれまでのおさらいをしたり、connpass上にてその会でどのドキュメントのどこから読むかを記載しておき、事前に予習ができるようにしておくことで、途中からの参加でもある程度共通の知識を持って会に臨んでもらえるように心がけています。 継続開催する こういったイベントは、何よりも継続して開催し続けることが何より重要だと考えています。 そのためにも、上に書いたように参加ハードルを下げたり途中参加しやすくすることで、一定人数参加者に集まってもらえるようにしています。 ファシリテートもBASE、弁護士ドットコムさんで交互に行ってファシリテートの経験を広く積めるようにしつつ特定のメンバーに負担がかからないようにしています。 夜な夜な.goのこれから 引き続き輪読会形式は続ける予定ですが、この形式もまだまだ実験的で色々な形式を試せればなと思っています。 来年も引き続き開催していくので、ぜひ盛り上げ枠から参加お待ちしています!(いきなり参加枠も大大歓迎です!!!!) 最後に BASE BANKでは、BASEのショップオーナーさんの資金繰りの課題解決を行うプロダクトをGoやPHPを使ってアウトプットしていきたい方や、夜な夜な.goのようなイベント運営にも興味のある方々を大募集中です! 求人から、自分のXのDMから、どういった形でご連絡いただいてもOKです。ぜひざっくばらんにお話しましょう~~~ open.talentio.com https://twitter.com/applepine1125 明日12/8は @glassmonkey さんのminio導入話です!ぜひお楽しみに!
はじめに Creative Time 1という、ショップオーナーがクリエイティブな活動にもっと充実した時間を作れるように、あれやこれやをしているチームでマネージャをしています、bonです。 このbonって名前は、子供の時は受け入れられませんでしたが、大人になって都落ちした元ボンボンなので、もうネタにして自ら名乗っています。 本記事は BASE アドベントカレンダー 2023 の6日目の記事です。前日のBASEアドベントカレンダー「 Notion導入について(前編) 」の続きとなります。前編が導入にあたっての技術的な困難をどう乗り越えたか、に対して、私の記事ではなぜ挑戦したのか、どこを目指しているのか、を記事にしています。 Notionと私 私自身、前職でNotionを使い始め、今では公私共に3年以上愛用しており、Notionのコミュニティやイベントにも参加しております。そのコミュニティ活動の中で、ベータテスターにもなったり、Notion Championsコミュニティにも参加していることから、Notion導入においてもリードしていった経緯があります。 Notionチャンピオンズコミュニティ Notion導入によって解決したかったこと 約1年前に私がBASEに入社し、その際に受けたオンボーディングで私が根本的に解決しなきゃな課題に気づくキッカケでした。 BASEでは、リモートワークも組み合わせたハイブリッドワークを導入しているため、得た知見をドキュメントや何かしらのテキストに落とし込む文化は根付いていました。しかし、これがGoogle DocsやらExcelやらGitHub wikiやらAsanaやらFigma/FigJam、Slackといった局所最適されたツールに散らばっていました。そのため、オンボーディングでも調べたらすごくわかるドキュメントに辿り着くことは出来ても、その調べかたを都度、誰かに聞いて回る状況でした。 これを解決したかった、です。 しかし当時から私はほぼ脳死で「ーーーーNotionッッッッ!!!」と唸るほど、Notionに脳を焼かれていたので冷静になるべく課題を整理してみました。 解決に向けての課題設定 私が困っていたことは、どこに欲しい知見があるか、だけでした。 どこかを探せば、ほぼ間違いなく欲しい知見がある状態でした。 しかし、どこを探せば欲しい知見に辿り着けるかの知見が、新参者の私には分からない状態でした。 探し当てた知見については内容が濃いものが多いものの、更新がされていないドキュメント類も多いなと感じました。もちろん、ここには更新をしていく類のドキュメントではないものも含まれています。 この状況を、SECIモデルで言うと、以下のような状況だと認識しました; 欲しい知見がどこかにある=共同化&表出化は出来ている 欲しい知見に辿りつきにくい=連結化が出来ていない 未更新ドキュメントが少くない=内面化がうまくいっていない テキストに残す文化はあるので内面化は、そのフェーズまで情報を届ければ循環していくと想定し、この中で一番解決すべきところは連結化にある、と絞りました。 出典:野中郁次郎(2002)「企業の知識ベース理論の構想」『組織科学』Vol.36 No.1 4-13 p.10 よって課題解決の初手には、連結化を改善していく事として定めました。 Notionとは 今更ですが、強調します。単なるノート・メモ帳の類ではありません。 Notionは、様々なツールを繋ぎ合わせるハブであり、ポータルであり、これらの役割を満たすことでSingle Source of Truthになるツールです。 BASEでは元々、Kibelaというドキュメントツールを使っていましたが、導入するにあたってNotionも、ドキュメントツールとして機能することから、ドキュメントツールをBASEではNotionに移行しました。これによって予算も捻出しました。(※1) そのため、結合化の課題の解決策としては、適切なアプローチだなと判断し、同期の凄腕なんでも屋さん(※2)に全体リードをしてもらいながら、社内でプロジェクトチームを立ち上げました。 ※1 同様の整理で、Asanaからもタスク管理ツールをNotionに移行完了しております。 ※2 職域問わず活躍している方なので言い表しにくく、bonのできる最大限ポジティブな表現を使用しています。 情報整理の狙い Notionを導入し、今ある知見が繋がる事で知見が1箇所に蓄積されていくことになりますが、これが最終地点ではありません。手に入った知見をもとにショップオーナーへ如何に早く、多く、大きな価値を届けるかを目指しています。 情報そのものを整理する ここまでで、知見と情報とを無断で使い分けてきました。これらは以下のように使い分けをしております。この使い分けは、田坂広志氏著書『 成長し続けるための77の言葉 』に記載されている定義を継承しています。 情報とは、一般的に手に入る知見であり、一言でいえばググると出てくる類のものと認識しています。一方で知見とは情報をベースに動いてみた結果のうち、言語化ができる部分のことを指しています。しかし当然ですが、知見を見聞きするだけでは得られない部分、言語化しきれない部分もあります。こここそが知見をベースに動いて得られる深い知見であり、経験と人間とからでしか体得できない智慧だと認識しており、これによってショップオーナーへの価値提供が早く、多く、大きくなると思っています。 イノベーション for owners’ success 智慧に繋がる経験を積み重ねていくためには元となる知見が、個人なら頭の中に、チームならどこか1箇所に集約されている必要があります。そのための手法は数多くあれど、少なくとも知見が散らばっている状態は、探し回る労力が途方もありません。何がどこにあるかがわかっている人でしか辿り着けない知見からは価値が生まれません。これを打破していければ、智慧を得るための行動を助長していくことになり、作りたい未来を作る土台になると信じ、今回私はNotion導入を積極的に進めていきました。無事に導入できたことによって、私たちが作り出した知見と知見とが繋がって智慧を蓄え、価値の交換が最適化された未来をともに実現していく基盤が出来ました。 We are hiring !! 突然、価値の交換が最適化された未来と言う言葉が出てきて『?』となった方、この下のリンク先を是非ご確認ください。アドベントカレンダーならではのお決まりですが、 We are hiring !! 採用情報 | BASE, Inc. - BASE, Inc. 最後に アドベントカレンダー 7日目はmatsuyukiさんとmatzzさんです。お楽しみに!
本記事は BASE アドベントカレンダー 2023 の6日目の記事です。 はじめに こんにちは。 Shop to Shop チームでマネージャーをしている髙嶋です。 本記事は昨日からの続編になりますので、前編については以下の記事を参照ください。 devblog.thebase.in さて、本日は開発チーム内で取り組んだ10個の取り組みのうち、後半5個についてご紹介させていただきます。 再掲すると、以下の No.6 以降を取り上げようと思います。 No. 取り組んだ内容 1 チームとしての出社日運用の廃止 2 雑談の活性化 3 No Slack Day(日単位での原則 Slack 禁止) 4 No Slack Time(時間単位での原則 Slack 禁止) 5 オンライン会議ツールを Google Meet からハドルミーティングに変更 6 タスク管理ツールを Notion から GitHub Projects に変更 7 スクラムイベント時に使用するツールを FigJam から GitHub に変更 8 スクラムイベント実施日を毎週水曜日から金曜日に変更 9 デイリースクラムを4日間から2日間に削減 10 GitHub Discussions の活用 本日も、まずはこの記事で伝えたい要点だけ先に列挙しておきます。 GitHub サイコー スクラムイベントも、前提を疑って守破離で言う破にチャレンジしていくことも大事 では続きに参りましょう。 ⑥タスク管理ツールを Notion から GitHub Projects に変更 こちらもまずはその背景から話を進める必要があります。 全社的に、それまで使っていたタスク管理ツールとしての Asana、加えてそれ以外のツールも含めて Notion に一本化しようという動きがありました。 その際、私たちのチームでも Asana でやっていたことを Notion で表現しようと試みました。 Notion は非常に便利で強力なツールであり、タスク管理という文脈においても一定再現することはできました。 しかしながらその変更のカジュアルさや機能性の高さが却って仇になる場面もあり、もう一度自分たちのやりたいことをフラットに考え直しました。 結局のところ、開発チームとしての成果物は GitHub 上に生み出されることが多く、であればその近くにタスクの管理場所も作るのがよいのではと考え、GitHub Projects を使ってみることにしました。 結論、これも今回の取り組みの中で評価が高かったものの一つになりました。 チームメンバーが抱えていた課題への打ち手としてハマった、そしてイテレーションでの開発を進めるうえで必要な機能が GitHub Projects に必要十分なレベルで備わっていたことがその理由になるかと思っています。 メンバーからの声としては以下のようなものがありました。 仕様の質問などがその機能開発のための Issue 上で行え、「あの話どこでしてたっけ」と迷子になることが減った エンジニアの成果物がコードであることが多いという前提もあり、Issue / PR / Discusssion と、GitHub 上で連携して一元管理できるメリットが大きい GitHub API を利用したチーム活動に関するデータ集計がしやすくなった 機能上、親タスクに子タスクをぶら下げるような階層は作れるが、同列に見えてしまったり親タスクの進捗がぱっと把握しづらかったりする 結果:4点満点中3.9点で継続 GitHub Projects でできることへの理解が進み、後続の取り組みへと繋がっていきます。 ⑦スクラムイベント時に使用するツールを FigJam から GitHub に変更 ここで言うスクラムイベントが何を指しているかでいうと、以下の4つになります。 スプリントレビュー レトロスペクティブ リファインメント スプリントプランニング それらを進行するための会場として、FigJam を使用する形でしばらく運用してきていました。 FigJam は各々の頭の中にあるものを発散するという意味では非常に優れたツールであり、各スクラムイベント自体はそれなりに活気のある状態ではあったと思います。 一方で、結局タスク化に際しては GitHub などツールをまたいだり、あるいは発散するのはよくてもそれを一つに収束させていくといったことが求められたり、冷静に見つめ直すとそのデメリットも浮かび上がってきました。 そうした状況を踏まえて、実は GitHub Projects である程度表現できるのではないかと考えて一度トライすることにしてみました。 結論、これも想像以上に上手くできた、という結果になりました。 FigJam と比較すると表現の幅に制約が生まれたのは確かですが、その制約があるからこそ最適な形を改めて考えることができたように思います。 メンバーからの声としては以下のようなものがありました。 レトロスペクティブで出たトライをそのままタスク化しやすいなど、話して発散だけして満足とならないのが良い 扱うツールが減ったことで運用しやすくなった スクラムイベント終了後も話途中や気になった議題などについては引き続き GitHub 上(GitHub Discussions 等)でやりとりできるようになり、そういう意味での時間的制約がなくなったと感じた 不便さはないが、スタンプなどで反応できないのは若干寂しい 結果:4点満点中3.4点で継続 慣れ親しんでいたスクラムイベントのやり方に大きくメスを入れたことで、他にも前提を疑ってトライできないかといった思考が強くなっていきます。 ⑧スクラムイベント実施日を毎週水曜日から金曜日に変更 これはある意味やったことはその通りでしかないのですが、一般的に連休にされやすい月曜日や金曜日にスクラムイベントを置くのはアンチパターンのように言われることも多いと思います。 ただそれまでのチーム状況を改めて振り返って水曜日と金曜日を比較したときに、特段の違いを感じられませんでした。 これは BASE における休暇の取りやすさということであったりも影響しているのではないかなと思います。 そうなると、週の終わりにスクラムイベントを置くということの分かりやすさ、リズムの作りやすさのメリットの方が大きくなるのではないかと考えました。 結論、これは良くも悪くも特に影響がありませんでした。 水曜日に実施していたことによるメリットは、実はそこまでなかったことが証明されたとも言えます。 メンバーからの声としては以下のようなものがありました。 スプリントが月曜日から開始するため気持ちが良く、そのまま木曜日まで連続するため振り返りもしやすい プロジェクト外の業務あるいは業務外のことも含めて、週のサイクルと揃って分かりやすくスケジュールもたてやすくなった 当初懸念していた「土日を挟むことで忘れてしまう」といったことも特に起きなかった 特に水曜日と比較したときの変化は感じなかった(ニュートラル) 結果:4点満点中3.5点で継続 がっつりスクラムイベントを実施する日だけではなく、デイリースクラムも見直せないかなと考えることになっていきます。 ⑨デイリースクラムを4日間から2日間に削減 スプリント期間としては1週間、つまり稼働日ベースでは5日間でまわしています。 そのうちの1日は上述したスクラムイベント実施日のコンテンツにマージされているような格好であるため、明確にデイリースクラムのみやっているのが元々4日間であったというのが前提となります。 ここの課題感としては、デイリースクラムを待って問題を話すという事象を極力なくしたかったのと、よりバックログを正しい状態に保つこと、作業時間を柔軟に確保することも併せて推し進めたかったということが挙げられます。 チームないしプロジェクトが発生してから一定期間は毎日やる意味は確かにありましたが、そこから時間がたって同じように必要かで言うと、もはやそういう状況ではないだろうと考えたのが出発点になります。 結論、2日間に削減したからといって業務が滞るわけではない、ただチームメンバーの顔を見る機会が少し減ったのは若干寂しいといった結果になりました。 メンバーからの声としては以下のようなものがありました。 以前は開催すること自体が目的のように思っていたが「なぜ開催するのか」を考えやすくなり、改めてその意義を見直すことができた 問題が発生したら、デイリースクラムを待たずにコミュニケーションをとるようになったと感じた 特に業務上の支障があるわけではないが、顔を見る機会が多少減ったことで他メンバーがどんな感じのテンションでいるのか気になる(根詰まってるのか、全然大丈夫なのかなど) 結果:4点満点中3.1点で継続 スクラム開発のフレームワークは認識しつつも、そこから外れていくこともポジティブな理由であれば、今のチーム状況を考えれば決して悪手ではないということが確認できました。 ⑩GitHub Discussions の活用 レトロスペクティブといったスクラムイベントに限らず、課題になる前のトピックについては FigJam や Slack 上でコミュニケーションをとるのではなく GitHub Discussions に集約してはどうかと考えました。 これは各々がより考えたうえで言語化するとともに、Slack で散見されるような「あれどこで会話したっけ?」といった事象の削減を促進したかったというのが背景にあります。 つまり特定のトピックに対するやり取りをフロー情報として扱って迷子にさせない、加えて GitHub の活用が推進されてきたという状況も相まって、積極的に GitHub で提供されている機能を使っていこうといったマインドになっていたということもあります。 結論、こちらも概ねポジティブで、とりわけそれまで GitHub 上でそこまで業務を進めるということが少なかった PdM やデザイナーのメンバーからも好評だったのが意外ではありました。 Slack がフロー情報で流れやすいというつらみを、上手く補完できたのが良かったのかなと思っています。 メンバーからの声としては以下のようなものがありました。 問題提起したけどそのままうやむやになるということがなくなり、会話が流れず非同期で確認できるので良い 一つの話題を深く議論したい際に、まさにピッタリなツール、使い方だと思った あとから話題を思い出す時に、どこで会話したかを探すのに苦労するということがなくなった GitHub Discussions から明確なトライに繋げる動きがまだまだできていない 結果:4点満点中3.6点で継続 自身の考えを適切に言語化してアウトプットする、それに対するフィードバックを重ねていくという点において有用なツールになりうることを確認できました。 まとめ なんとなくそうだろうと思っていたことも、実際に試してみると良い意味でちょっとギャップがあるものだなと改めて感じることができました。 とにかく失敗を恐れずにどんどんチャレンジしてみる、そしてそこから得た知見をネクストアクションとして繋げていくという空気を今後も醸成していきたいと考えています。 ただ本当にしたいのは、一つのチームに閉じて自分たちのやり方を磨いていくということではなく、チームをまたいでこういった知見を共有しあい、組織全体として開発手法や運用、あるいはその他様々なことをアップデートしていくことなのではないかなとも思っています。 明日は matzz さん、matsuyuki さんの記事です。お楽しみに! devblog.thebase.in
はじめに 本記事はBASE アドベントカレンダー 2023の5日目の記事です。 こんにちは!BASE株式会社でエンジニアをしている田中です。 Creative Time1 Groupに所属し、主にBASEのBackOffice領域の機能開発を担当しています。 BASEでは、社内でのドキュメント管理の課題解決に向けて、2023年2月頃からツールをKibelaからNotionに移行しようという検証が始まりました。 この記事では、どのようにしてKibelaの記事をNotionに移行したのかや、移行の際に困ったポイントを紹介していこうと思います。 Notion導入にあたっての詳しい経緯や課題などについては、12/6公開予定のbonさんによる「Notion導入について(後編)」にて紹介予定です。 Kibelaのエクスポート機能とNotionのインポート機能 Kibelaには記事の エクスポート機能 があり、記事および画像などのアップロード済みファイルをZip形式でダウンロードできます。 ダウンロードしたZip内には、attachmentsという記事の添付ファイルが含まれるフォルダ、notesというMarkdown (.md)形式の記事が含まれるフォルダが存在します。 エクスポートされたMarkdownには、以下のようにYAMLのヘッダー部分と記事本文の情報が入っています。 --- id: QmxvZy8x path: "/notes/1" author: "@ayako" contributors: - "@ayako" coediting: true folders: - 初期グループ / fuga groups: - 初期グループ published _ at: '2023-03-09 18:10:44 +0900' updated _ at: '2023-03-10 19:31:08 +0900' archived _ at: comments: - id: Q29tbWVudC8x path: "/notes/1#comment _ 1" author: "@ayako" published _ at: '2023-03-10 19:31:08 +0900' updated _ at: '2023-03-10 19:31:08 +0900' content: コメント --- # テスト記事 # タイトル ## 見出し1 hoge < img title = 'スクリーンショット' alt = 'スクリーンショット' src = '../attachments/2.png' width = "376" data -meta= '{"width":376,"height":364}' > Notionには現状Kibelaから直接インポートする機能はないため、Kibelaからエクスポートしたマークダウンファイルを、NotionのText & Markdownのインポート機能で取り込みます。 標準インポートでは取り込めなかった情報 ここまでの手順で記事本文自体のインポートは可能なのですが、標準インポート機能では取り込めなかった情報があります。 YAMLヘッダー部分に記載の情報 以下のような記事の詳細情報は標準インポートでは取り込めません。 著者・編集者 記事の所属グループ 記事の属するフォルダ 記事作成日 最終更新日 記事についていたコメントの内容 どうにか解決する方法はないか調べる中で、既にKibelaのNotion移行を行い、同じ事象で困ってツールを作成していた方を見つけました。 Kibela から Notion に約2万+件の記事を移行するために移行ツールを作った話 こちらの kibela-to-notion を利用し、YAMLヘッダー部分に記載の情報については問題なく取り込めました。 画像やPDFなどのファイル群 attachments/ 内にエクスポートされた記事内に存在したファイル群は、エクスポートした段階で <img src='../attachments/image.png'> のように相対パスになっており、そのままNotionにマークダウンをインポートしても画像を参照できず読み込みはできません。 記事執筆時点(2023/12/1現在)でも、Notion APIで画像のアップロード機能がないため、自分でどこかにアップロードする必要があります。 The Notion API does not yet support uploading files to Notion. Start building with the Notion API 先述のkibela-to-notionの記事でも画像問題は触れられており、S3やGoogleDriveを利用する方法もセキュリティ上の問題や、inline表示ができない点で懸念があり、何か他の方法はないかと模索しました。 検証を繰り返し、Notion上に画像をアップロードしてURLを発行し、APIでURLを取得して置き換える方法を取りました。 ファイルのアップロード Notion上にKibelaからファイルをアップロードするDBを作成し、Files&mediaのプロパティを追加し、空のページを作ります。そこにKibelaからエクスポートしたZIP内の attachements/ 内のファイルをアップロードしていきます。アップロードするファイル数はかなり多かったため、1000ファイルごと程に分割しアップロードしました。 アップロードしたファイルの取得 作成した空のページごとに、 ページプロパティを取得するNotion API を使用してファイルを取得し、ファイル名をkey、Notion上にアップロードしたファイルのURLをvalueにしてRedisに格納します。 相対パスのファイルURLの書き換え あとはRedisに格納したファイル名を元に、マークダウンファイル内の相対パスになっている箇所を書き換えます。ここの手順に関しては、 kibela-to-notion 内にそのまま利用できるロジックが存在していたため、利用しました。 ファイルURLを置き換えたマークダウンファイルをNotionにインポートすると、無事にファイルが読み込めており、ファイルが参照できない問題は解決しました。 その他苦労したこと Notionへの大量のファイルアップロードやインポート 今回どうしても必要だった作業として、相対パスの参照問題を解決するためのNotionへのファイルのアップロードと、マークダウンファイルのNotionへのインポートです。 数が数なだけにまとめてアップロードしたいのですが、同じファイル数や同じファイルの大きさだとしても、すぐ完了する場合もあれば途中で原因も分からず失敗してしまうことが何度もありました。そのためにはできるだけファイルを分割して小さい単位で作業を行うしかなかったため、社内の有志の方にご助力いただき、作業を分担し解決しました。 おわりに このNotion移行計画が2月ごろ始まり、計画・検証・作業含め完了したのが10月のため8ヶ月ほどかかりました。メインの業務とは別で行なっていたため時間もかかりましたが、社内の有志の方のご協力もあり無事に移行を終えることができ感謝しています。 アドベントカレンダー 6日目はtakashimaさんとbonさんです!お楽しみに!
本記事は BASE アドベントカレンダー 2023 の5日目の記事です。 はじめに こんにちは。 Shop to Shop チームでマネージャーをしている髙嶋です。 役割としてはエンジニアリングマネージャー(以下 EM)と言われるものを想像していただくとイメージしやすいかもしれません。 そんな私から、開発チーム内で取り組んだ10個の実験もとい取り組みについてご紹介させていただきます。 開発プロジェクトを遂行するチームの開発現場をスコープにした話になりますが、一つでも参考になるものがあれば幸いです。 ちなみにチーム構成としては PdM 1名、デザイナー1名、エンジニア5名、EM 1名(私)の総勢8名となります。 最後まで読むのが億劫になる可能性もあるので、この記事で伝えたいことだけ先に列挙しておきます。 出社(オフライン)とリモートワークの使い分けが難しいためにチームとしての活動はリモートワークに振り切ってみたが、それが自分たちの働き方を見つめ直すキッカケになった 雑談サイコー Slack は便利、ゆえに付き合い方や捉え方も様々で難しい では早速参りましょう。 取り組み一覧と評価方法 まず、何に取り組んだのかその一覧をご紹介します。 詳細が伺い知れないものもあるかと思いますが、後述するのでここでは割愛させてください。 本記事では No.5 まで、明日の記事でそれ以降を取り上げさせていただきます。 No. 取り組んだ内容 1 チームとしての出社日運用の廃止 2 雑談の活性化 3 No Slack Day(日単位での原則 Slack 禁止) 4 No Slack Time(時間単位での原則 Slack 禁止) 5 オンライン会議ツールを Google Meet からハドルミーティングに変更 6 タスク管理ツールを Notion から GitHub Projects に変更 7 スクラムイベント時に使用するツールを FigJam から GitHub に変更 8 スクラムイベント実施日を毎週水曜日から金曜日に変更 9 デイリースクラムを4日間から2日間に削減 10 GitHub Discussions の活用 そしてどう各取り組みを評価、つまり効果測定をしようかと考えたときに、定量的な形で表現できると一目瞭然なので望ましくはありました。 例えばベロシティや Pull Requests 関連の数値、あるいは稼働時間といったものを計測すること自体はそれほど難しくありません。 しかしそれらは様々な要因が絡み合って影響されうるものであるため各取り組みと結び付けて結論づけるのは難しく、なんとも言えない結果になるであろうことは容易に想像できました。 そのため今回はメンバーからの定性的な評価に重きを置くことにしました。 最終的には各取り組みに対する評価を以下の基準でつけ、なぜそう思うのかというコメントと併せて回答するアンケートを実施しました。 点数 意味 4 満足(続けたいし、現在の運用にも概ね満足している) 3 やや満足(続けたいが、一部運用は見直せないかと考えている) 2 やや不満(積極的にやめたいというわけではないが、少なくともあまり効果は感じていない) 1 不満(期待していた効果がないのでやめたい) さて、前置きが長くなってしまいましたが、ここから各取り組みについてその詳細をご紹介させていただきます。 ①チームとしての出社日運用の廃止 何をやったかの前に、まずは弊社としての前提、そしてチームとしての前提を先にお話しなければなりません。 弊社の働き方としてはハイブリッドであり、フルリモートではありません。 そのため会社としても出社を対面でのコミュニケーション機会として有効に活用していくことなどが推奨されています。 ただ、その出社頻度や活用方法については各チームに任されており、私たちのチームにおいては毎週のスクラムイベント実施日を出社日として定めました。 単にスクラムイベントを実施するというだけではなく、それ以外のオフラインイベントを同日に開催したり、あるいはチームメンバーとランチに行ったりするなど一定活用はできていたと思います。 結論、それをやめました。 誤解のないように補足しておくと、あくまでもチームとして定期的かつ同期的に出社することをしないだけで出社そのものを禁止するわけではありません。 各々の判断で出社したい日は自由に出社する形をとっていますし、あるいは互いに声がけしてスポットで出社して一緒に何らかの活動を行うといったケースもあります。 出社日運用を廃止した背景として、同一プロジェクトを遂行するチームメンバーであっても組織図上は別チーム所属の方がいる、また雇用形態等の事情により遠方で従事されている方もおり、出社することの足並みを完全には揃えられないといったことがありました。 そうなると結局はスクラムイベントもオンラインとオフライン混合の MTG であることに違いなく、その体験の違いもあって十分な効果を生み出せているとは言えない状況でした。 それならいっそのことリモートワークに振り切ってしまい、環境差異を極力なくそうという判断をしました。 メンバーからの声としては以下のようなものがありました。 スクラムイベントの日の出社をやめたからといって、特段困るようなことはなかった 前提として、メンバーの関係性を一定築けていたからできたというのはあったかもしれない 一緒にランチに行くなどして親交を深めていた側面もあり、そういう機会は減ってしまったのでたまにはオフラインで会う機会も作りたい 結果:4点満点中3.4点で継続 これが以降の取り組みの布石となっていきます。 ②雑談の活性化 チームとしての出社日運用を廃止したことで、業務外のコミュニケーション量が減ってしまい、そこから翻って業務にも悪影響を及ぼすのではないかという懸念がありました。 リモートワーク下において、雑談の大切さといったことも一般的に語られることが多いように感じます。 そこで仕事云々の前にまずはお互いをもっと理解することで心理的安全性を確保しようと、チームで雑談ができる random チャンネルを Slack 上に用意し、積極的にコミュニケーションをとってみることにトライしました。 元々チャンネル自体は存在していましたがそこまで活用されていなかったのと、「雑談しよう!」と突然言ってもできるものではないかなと思い、最初は私が中心となって話題を投稿することから始めてみました。 併せてカジュアルに発散したいもののアウトプット場所を Notion 上に用意し、それを当該チャンネルと連携することでさらなる活性化を図ることにしました。 結論、これが一番受けが良かったかもしれません。 正直やる前はどういう結果になるのか、どういった効果を生み出せるのかについて半信半疑でしたが、実際にやってみてその良さを感じ取ることができました。 当初は私が発信して始まることの多かった会話も、徐々にメンバーからトピックを提供する場面も多くなっていきました。 メンバーからの声としては以下のようなものがありました。 チームメンバーとの距離が近くなり、仕事中のちょっとした息抜きにできてよかった 以前よりチームメンバーのパーソナリティを知ることができ、雰囲気もよくなったように感じる 仕事に関係のない話をする場ができて良かったし、非同期で強制されるものでないことも併せて良かった 結果:4点満点中3.9点で継続 次は業務上のコミュニケーションの話へと繋がっていきます。 ③No Slack Day(日単位での原則 Slack 禁止) ここもまずは取り組み自体について話す前に、BASE 組織全体での前提について話しておく必要があります。 現在、毎週木曜日の午後は No MTG Day となっており、原則会議は非推奨となっています。 私たちのチームとしてもその効果は一定感じつつも、作業の分断という意味では決して会議だけではなく、Slack もそうではないかという仮説をたてました。 会議をしない分、それが Slack 上に移って効果が半減してやいないかといった仮説です。 そこで木曜日の No MTG Day に合わせる形で、極力チーム内での Slack コミュニケーションを抑えることに取り組んでみました。 結論、この取り組みはやや評価が分かれる形になりました。 Slack が業務のど真ん中にあるツールであること、人によってツールとの付き合い方にも微妙な違いがあること、あるいは働き方のリズムも違うことなどから一律のルールを設けることの難しさがありました。 メンバーからの声としては以下のようなものがありました。 木曜日が MTG や Slack を気にせず集中できる時間になったのがよかった 開発作業を一人で集中して行いやすくなり、そうできるよう前日のうちにそのための準備をするような動き方ができた Slack でとっていたコミュニケーションが、今度は GitHub の Issue 上などに移っただけのような印象を受けることがあった(目的が Slack を使わないことになってしまった) その日のうちにカジュアルに話しておきたいことはどうしても発生するのでモヤモヤが残った そもそも Slack の通知が作業の妨げになっているとは思っておらず、非同期コミュニケーションであることでその受け手が自分のタイミングでレスできる点にメリットがあるので、作業効率とはあまり相関がないように感じた 結果:4点満点中2.6点で廃止 本施策はやめることにしましたが、Slack との付き合い方についてはもう少しだけ粘ります。 ④No Slack Time(時間単位での原則 Slack 禁止) 日単位で禁止するのはちょっとつらみも大きいということであれば、時間単位ではどうかと考えました。 具体的には14時以降終日という形から始め、その後14〜17時に変更して検証しました。 結論、またまた評価が分かれることになりました。 結局本質的な課題感は変わらず存在し、一定メリットはあるものの、少なからずデメリットもあるといった状態です。 メンバーからの声としては以下のようなものがありました。 No Slack Dayより No Slack Time の方が1日の中で作業計画が立てやすかった 14時まで話していいが故にそのまま続きの会話をしたくなることがあり、No Slack Day よりも切り替えが難しく感じた タイムゾーンが違うわけではないがリモートワークだと個々人の行動の柔軟性は増すので、オフラインで同じ場所にいるといった形じゃないとチームとしてはワークしないかもしれない 結果:4点満点中2.4点で廃止 いったんこれをもって Slack の制限という形での取り組みには区切りをつけることにしましたが、最適解を探っていく試み自体は続けていく予定です。 ⑤オンライン会議ツールを Google Meet からハドルミーティングに変更 まずこちらも社内全体での傾向にはなりますが、オンライン会議ツールとしては Google Meet がメインで、私たちのチームも基本的にはそうでした。 しかしながら上述したように業務上コラボレーションする場としては Slack が最も多く、コンテキストに応じたチャンネルのハドルミーティングを使用することで会議参加をシームレスに実現できるとともに、参加者の分割や移動といったこともライトに行えるのではないかと考えました。 また、ハドルミーティングのスレッドなどに会話したことを議事録としてタイムリーに残せる(議事録を別ツールに探しに行くこともなくなる)ということも理由の一つです。 結論、この取り組みは概ね好評でした。 もちろんそれぞれのツールの良さはありつつも、会議に対する要求がハドルミーティングでも担保できると改めて理解できたことが大きかったのではと思います。 メンバーからの声としては以下のようなものがありました。 Slack でのテキストコミュニケーションからシームレスに口頭での会話に移行できる 会話の中で出たメモやリンクなどをスレッド内に残すことができ、後から検索することも容易である 会議の性質によってはカジュアルにリアクションを取りたいシーンがあり、Google Meet の方がその表現はしやすかった ブラウザの特定タブの画面共有や、開始時にデフォルトでビデオ ON にするといったことがハドルミーティングではできず、ちょっとした不便を感じた 結果:4点満点中3.5点で継続 まとめ 取り組みのご紹介という意味ではまだ折り返し地点ではありますが、一つひとつは小さくともトライを積み重ねることで、何か新しい取り組みを始めることに対するチームとしてのハードルが下がったように感じます。 メンバー構成的にも一定期間同じような顔ぶれであった、またその業務ないし開発スタイルについてもある程度固まってきていたことで、効率性はありつつも一種の停滞感のようなものも同時に感じていました。 別にめちゃめちゃ悪いということがあるわけでもなく、かと言ってすごくいいというわけでもなく、なんとも評価しづらいといった感覚でしょうか。 繰り返しにはなりますが、今回の取り組みを通じてその状況をいくばくか打破でき、今後の活路を見出す機会になったと捉えています。 明日は本記事の続編をお届けします。お楽しみに!
はじめに この記事は BASEアドベントカレンダー の四日目の記事です。 こんにちは!私は @shiiyannn と申します。現在、メンバーシップ Appの開発に携わっています。メンバーシップ Appはショップオリジナルの「メンバーシップ」(会員制度)を作成することができる機能です。 2023年9月、メンバーシップ Appは大幅な 機能アップデート を遂げました。今回のアップデートでは、ショップオーナーが商品購入時に独自のポイントを付与できるようになりました。付与されるポイントの量は、注文金額にショップオーナーが設定したポイント付与率を掛け合わせて計算されます。 この記事では、ポイント付与機能の開発中に直面した、浮動小数点計算の問題とその解決策についてお話しします。この問題を深掘りすることで、料率計算や金額処理に取り組む開発者の皆さんに有益な情報を提供できればと考えています。 リリース直前に発見した浮動小数点問題 エラーの発生 メンバーシップ Appのリリースをスムーズかつ安全に行うために、私たちは一般公開に先立ち社内で限定公開するという二段階リリース戦略を取っています。社内限定公開中の機能検証テストで、予期せぬエラーが発生しました。具体的には、ショップオーナーが7%や14%の付与率を設定した場合、注文時の付与予定ポイントの計算処理が失敗し、結果として注文が完了できない状況が起こりました。 エラーの内容は、ポイント計算に利用される7%の付与率と、ショップオーナーが設定可能な7%の付与率がシステム上で不一致ということでした。メンバーシップポイントの計算をするために、ポイント付与率というバリューオブジェクトを構築する必要があります。計算用の付与率がコンストラクタの引数として渡される際、バリデーションエラーが発生しました。 以下に示すソースコードは説明のためのもので、本番環境でのメンバーシップ Appと同じ実装ではありません。 class PointRate { private const ALLOWED_VALUES = [6.0, 7.0, 8.0]; function __construct (private float $value) { if (!in_array($value, self::$ALLOWED_VALUES, true)) { throw new Exception ('ポイント付与率が不一致'); } // ... } } エラーの解決 このエラーの根本的な原因は、浮動小数点の精度に関連するものでした。PHPで使用される浮動小数点数(float)は、IEEE754フォーマットを使っていて、その精度には限界があります。例えば、0.7という値は二進数の浮動小数点数として正確に表現することができず、その結果、丸めの誤差が発生します。 私たちのケースでは、ポイント計算のために小数点形式の付与率をパーセント形式に戻す処理があります。in_array関数を使って、パーセント形式に戻した値を元々の7.0と比較すると、(7.0 / 100) * 100 != 7.0という丸めの誤差により予期せぬ結果が生じました。 エラーの解決策は、PHPの公式マニュアルにも記載されている通り、浮動小数点数の比較に一定の誤差を許容することです。具体的には、私たちはイプシロンという丸めの単位を決めて、その許容範囲内であれば、2つの浮動小数点数を等しいと見なす方法を採用しました。 https://www.php.net/manual/ja/language.types.float.php class PointRate { private const ALLOWED_VALUES = [6.0, 7.0, 8.0]; private $epsilon = 0.00001; function __construct (private float $value) { foreach (self::ALLOWED_VALUES as $allowedValue) { if (abs($value - $allowedValue) < $this-> epsilon) { return; // 許容範囲内であればOK } } throw new Exception ('ポイント付与率が不一致'); } // ... } 今回の課題点 リリース直前に発見したこのエラーは、チームメンバー全員の協力により迅速に対処され、幸いにもメンバーシップ機能のアップデートを予定通りにリリースすることができました。エラーが解決された後日、私たちはこのエラーの背後に潜む課題点を改めて考えました。 画面上の表示とシステム出力の内容が一致せず、エラーメッセージを正確に理解するのが困難でした。 エラーの原因を特定するためのデバッグ範囲が広く、問題発生の箇所を効率的に特定することが難しかったです。 リリース直前まで行われていたユニットテストや機能検証テストでは、このエラーを検出できませんでした。 具体的に説明します。 まず、エラーメッセージは直感と矛盾していました。ポイント付与率を設定する画面では、付与率が正確に7%として表示されているにもかかわらず、システム上では、付与率が不一致と判定されていました。そして、注文時のポイント計算においては、なぜか7%と14%の付与率を設定した場合のみ計算が失敗し、他の付与率では注文が正常に処理できました。問題の解決には、まずエラーメッセージの内容とシステム振る舞いとの間にあるギャップの正体を突き止める必要がありました。 リリースの直前に発見されたこの問題は、限られた時間内での対応が必要でした。特に挑戦的だったのは、デバッグの対象範囲が非常に広かったことです。リリース前の機能検証テストでは、メンバーシップポイント付与の全機能を対象としていたため、問題が発生しうる箇所が広範囲に渡ります。例えば、フロントエンドからリクエストされる際、データベースから設計された付与率を取得する際、あるいはアプリケーションロジックでポイント計算する際、これらのいずれかの段階でも付与率が変更された可能性があります。 私たちは、メンバーシップ Appの開発中、アジャイルテストの4象限理論に基づき、ユニットテストと機能検証テストを実施してきました。具体的に、クラスごとに単体テスト、ユーザーストーリーごとに機能検証テストと受け入れテストを含めました。問題が発生した付与率のバリューオブジェクトとポイント計算のユースケースに対しても、ユニットテストが定義されていました。しかし、これらのテストは全て問題なく通過していたにも関わらず、問題の発見と対処は遅れました。 これらの課題点を解決するために 浮動小数点数を扱う際のベストプラクティス 私たちは、今回のエラーを理解できなった主な原因の1つは、浮動小数点数の丸め誤差に対する理解が足りなかったと分析しました。そのため、エラー対処を終えた直後、浮動小数点数の丸め誤差の発生原因とその対処方法に焦点を当てた学習資料をまとめ、グループ内で浮動小数点数(float)に関する知識を補強しました。さらに、将来的に同じようなミスを避けるために、浮動小数点数を扱う際のベストプラクティスを以下のように定めました。 小数形式ではなくパーセント形式を利用するや、小数点以下を切り捨てるなど、可能な限り整数型(int)を使用します。 PHPにおいて、float同士の比較や計算時には、丸め誤差を考慮して、イプシロン(許容誤差)の使用を推奨します。 データベースでは、固定精度のDECIMAL型を利用し、必要な精度を明確に指定します。 問題の発生箇所を効率的に特定する工夫 問題の発生箇所がすぐに特定できない場合、特に広範囲にわたるデバッグが必要となります。そのような状況に対応するために、私たちのチームでは、PhpStormと連携したXDebugを活用してリモートデバッグを行うようにしました。これにより、エラーの発生箇所を特定する際に、PhpStormというIDEからブレークポイントを設定し、ステップイン(step into)やステップオーバー(step over)の機能を効率的に使用することが可能になりました。私たちの開発者のローカル環境では、XDebugはDockerで管理されており、DockerやPhpStormの更新によって機能しなくなる可能性もあるため、私は個人的に月に一度、XDebugが正常に機能するかどうかを確認するようにしています。 今回のエラーを特定する作業を困難にしたもう一つの要因は、バリューオブジェクトの値に対してバリデーションするタイミングに統一されたルールがなかったことでした。例えば、以下のようにバリデーションメソッドをstatic化し、任意のタイミングで実行するような実装が散見されていました。このアプローチでは、初期の有効な値がバリデーションを通過した後でも、後に不正な値に置き換わるリスクがあります。 class A { static function validate($value) {} } A::validate($valid); new A($invalid); この問題に対処するため、私たちのチームではオブジェクトのライフサイクル全体を通じてバリデーションを実施する方法を採用しました。これにより、オブジェクトが常に正しい状態でのみ生成されるようになります。この対処の一環として、staticなvalidateメソッドの使用を禁止するPHPStanルールを定義しました。これにより、バリデーションに失敗した場合でも、直前のインスタンス生成時に利用された値に問題があると素早く特定でき、問題のある値を探す範囲を効果的に縮めることが期待されます。 テストデータのカバレージも重要視 私たちのチームはこれまで、新規クラスを実装する際には必ずユニットテストを書くという文化を築いてきました。しかし、今回の件を受けて、ソースコードのカバレッジだけでなく、テストデータのカバレッジにも注目するようになりました。テストデータのカバレッジを上げるために、特に有効なアプローチとして、等価分割(Equivalence Partitioning)、境界値分析(Boundary Value Analysis)、状態遷移テスト(State Transition Testing)が挙げられます。 メンバーシップポイント付与率を例に取ると、等価分割の思想に基づいて、以下のように様々なグループに分けてテストすることが可能です。 低い付与率のグループ:0.5% 中間のグループ:7% 高い付与率のグループ:15% あるいは、 二進数で表現できるグループ:0.5% 二進数で表現しきれないグループ:7% このようにグループを分けて機能検証テストのテストケースを作成・実施することで、問題をより早く発見する可能性があると考えています。ただし、テストパターンが増えると、当然テストコストも増加します。この点に対処するために、Playwrightで作成した自動E2Eテストも導入しています。今回の問題が発生した付与率はすでに自動E2Eテストでカバーされており、今後も時間とリソースを効率的に活用しながら、自動テストのパターンを増やす計画があります。 終わりに この記事では、メンバーシップポイント付与機能開発中に発見した浮動小数点計算の問題とその解決策についてお話ししました。明日は、@Ayako Tanakaさんの記事です。お楽しみに。
<この記事はHatena-Blog-Workflows-Boilerplateによって作成されました> 皆さんこんにちは! BASE株式会社 BASE BANK Divisionにて、Dev TOPとしてBASE BANKのエンジニア組織全体を見ている傍ら、このBASE product blogの編集長も務めている @applepine1125 です。 今回は2023年9月に公開された Hatena-Blog-Workflows-Boilerplate をつかって、BASE product blogの新たな執筆フローを作ってみました。 今のBASE product blog 執筆の流れ 現在、BASEでは社内のドキュメントツールとして Notion を使っています。 Notionのデータベース機能などを駆使し、以下のような流れで執筆、レビュー、公開を行っています。 Notion上で下書き記事を執筆 下書き記事のステータスを変更すると自動でブログ編集部員に通知 Notion上でレビュー LGTMをもらったらはてなブログに転記 公開 改善点 Notionを使った記事公開のフローでは、ステータスの変更により自動で通知が飛んだり、コピーすればmarkdown形式でコピーされます。 はてなブログへの転記も比較的簡単なため、それなりにスムーズにフローが回っています。 しかしそもそも転記が面倒だったり、転記してフォーマット崩れの確認、修正作業が多少必要など、もっと記事執筆にフォーカスできるようにしたいなという声がちらほらありました。 そんなときに今回紹介するHatena-Blog-Workflows-Boilerplateが公開されたので、アドベントカレンダーネタがてら新しい執筆フローを試してみるか〜と思い、触ってみました。 Hatena-Blog-Workflows-Boilerplate Hatena-Blog-Workflows-Boilerplateは、株式会社はてなさんが公式で公開しているはてなブログ執筆、運営支援用ワークフローのボイラープレートです。 このボイラープレートを使ってリポジトリを作成し、簡単な設定をすることで、PRの作成、レビュー、マージを通じてWebエンジニアの手に馴染んたフローで記事の執筆、公開、編集を行うことが出来ます。 github.com 本記事執筆時点でベータ版であり、さらなる機能拡充も予定しているそうです。 ぜひ皆さんも利用して、改善点があればissueを立てたりコントリビュートしてみてください。 Hatena-Blog-Workflows-Boilerplateを使ってみる セットアップ https://github.com/hatena/Hatena-Blog-Workflows-Boilerplate/blob/main/README.md に非常に丁寧に記載してあるため、何の問題もなくセットアップすることが出来ました。圧倒的感謝! 記事の作成 README によると、下書きの作成方法が2つあります。 下書きの作成方法は以下の2通りの方法があります。 ブログメンバーが個人のアカウントで投稿する(記事の署名は個人のアカウントになります) ブログオーナーのアカウントで投稿する(記事の署名はブログオーナーアカウントになります) BASEの場合、記事は個人のはてなアカウントで署名されています。 その場合、 ブログメンバーが個人のアカウントで投稿する場合 の項にあるように、一度ブログの編集画面上で空の下書きを作成し、Actionsから下書きをpullしてきてPRを作成、記事の編集を行う必要があります。 画像の投稿 staff.hatenablog.com にもあるように、2023/12時点で画像の投稿機能はHatena-Blog-Workflows-Boilerplateでは提供されていません。編集画面やはてなフォトライフへ手動で画像をアップロードをしてURLを取得、記事に貼り付けて対応します。 レビュー 実際に作成されたPR 上記画像のようにPRが作成されます。 pushした時点で下書きがはてなブログに同期され、PR上に記事編集画面のURLが記載されます。 そこから下書きプレビューのURLを発行してPRに追記するか、レビュワー/レビュイーが記事編集画面経由で下書きプレビューの確認を行います。 公開 記事内のDraft設定を削除しmainブランチにマージすると記事が公開されます。簡単! 修正 通常の開発のように、対象記事を修正、PRの作成、レビュー、マージによって修正が反映されます。 執筆フローを構築してみた所感 BASE Product blogは様々な職種のメンバーが記事を書いていますが、メインの執筆者であるエンジニアが普段慣れ親しんだGitHubでのレビューワークフローに則って公開できるのは強みだなと思いました。 記事のレビューも、内容だけでなく実際の表示までまとめて確認ができるので楽だなと感じました。 また、このボイラープレートを導入したからといって、このフローにロックインされるわけではありません。 これまで通りNotionや直接はてなブログの下書きを書いてレビューするフローは残せるので、エンジニアに限らず他執筆者の手に馴染んだフローで執筆することが可能です。(あまりフローの種類が多いと大変なのである程度絞る必要はありますが・・・) とはいえ画像の投稿は一気通貫でできるようになるとより執筆効率をあげられそうですね。今後の予定として記載されているので楽しみです。 幸い、はてなフォトライフには API があるため、自前で画像アップロードCLIなどを作ることはできそうです。今回間に合いませんでしたが、また暇なときに作ってみます。 下書き作成に関しては、記事が個人のはてなアカウントに紐づく運用の場合に一旦編集画面で空の記事を作成するフローはうまいことやって自動化したいです。どうやるといいんだろうか・・・ あとは予約投稿機能もよく使うので、その設定もできると嬉しいな〜と思いました。 おわりに まだあくまでベータ版ではありますが、普段慣れ親しんだレビューワークフローで執筆ができるのはストレスフリーでとてもよいOSSだな〜これが公式から提供されるのはとても意義があるな〜と思いながら構築しました。 今回は一旦自分が試験的に導入しましたが、もうちょっと整備して正式なフローの一つとして加えられるようにしていきたいです! 今後の機能拡充もとても期待しています。最後になりますがこのOSSを開発してくださった皆様、本当にありがとうございます! ぜひこの記事を読んだ皆様も導入してはいかがでしょうか? さて、明日は @shiiyannn さんによる、「小数点の罠:メンバーシップポイント計算の裏側」です。お楽しみに!
この記事は BASE Advent Calendar 2023 の2日目の記事です。 こんにちは!BASE株式会社でエンジニアをしている大津(@cocoeyes02)です。 今回は自分たちが運営しているプロダクトにおいて障害対応をする中で、インシデント発生が観測されてから暫定対応をするまでの初動にあたるフェーズの動きについて書いていきます。 インシデント対応全体に関わる話は別の記事にありますので、そちらも併せてご覧ください! devblog.thebase.in インシデント発生が観測されてからいかに早く対応に参加できるか インシデント対応の初動フェーズにおいて、いかに早く対応に参加できるかというのは非常に重要なポイントとなります。その理由はいくつかあります。 今開発している機能を将来使うかもしれないユーザが減ってしまうリスクがあるから インシデント発生時は、今現在困っているユーザ(BASEのサービスでいうとオーナーさん、購入者など)がいて、今すぐ対応をしないとどんどん体験を損なってしまう状況であることが多いです。 普段のユーザからのお問い合わせ対応も、インシデントほど切羽詰まっているわけではありませんが、やはりこちらも対応をしないとどんどん体験を損なってしまいます。 体験を損ない続けた先に待っていることは何でしょうか?そう、 ユーザがプロダクトから離れてしまうということです。 その前提で考えると、原則業務の優先度としては、 インシデント対応 > お問い合わせ対応 > 普段の開発 の順であると考えています。ユーザが離れてしまうということは、今実装している機能を将来使う可能性があった人も減ってしまうということです。そうなるとせっかく新しい機能をリリースしても、価値を届けられるユーザの数が減ってしまいます。 インシデント対応には、ユーザがプロダクトから離れてしまうことで発生するリスクと戦う人々が集まっているのです。 そもそもインシデント対応において途中から対応に参加するのは、後になればなるほど難しい インシデント対応は遅くなればなるほど、インシデント発生時間(ユーザ体験を損なっている時間)も伸びてしまいます。なので、スピードを求められる状況であることが多いです。 今どこまで対応したのか、今なんの対応をしているのか、どこまで情報を手に入れているのか、インシデント対応にまつわるログは時間が経つにつれて爆増していきます。参加するのが後になればなるほど、インシデント対応にまつわるログの量も多いため、キャッチアップだけでも大変です。 その点インシデント発生が観測されてからすぐ対応に参加することができれば、キャッチアップは難しくはありません。 ちなみにBASEではインシデントが起きた時にインシデント対応専用のチャンネルをSlackで作成しており、インシデント対応専用のチャンネルが作成された瞬間希望者を全員チャンネルへ自動的に参加させるツールがあります。 自動的に参加されたタイミングでSlackの通知がくるので、多くの人がインシデント発生に気づき、すぐ対応に参加することができます。 初動フェーズにおけるインシデント対応の具体例 ここにいくつか具体例を書きますが、これらの対応は実際にやってみないと上手くなりません。 現時点の状況やインシデントのまとめ MTG等で途中から参加してきた人がインシデント対応へ参加しやすくするため、今起こっている事象をまとめるだけでも立派な対応になります。 インシデントのまとめはインシデント発生からの流れを一通り追っていないと書けないことが多いので、インシデント対応に慣れていない人はまずはここから取り組んでみると良いのではないかと思います。 必ず全部1人で書かなければいけないわけではありません。少しでも良いから書いてみるところから始めましょう。 インシデントの事象を再現しよう インシデントの種類によっては、どのOS・ブラウザ・バージョンで起きたか重要になる可能性もあります。 その場合、どのOS・ブラウザ・バージョンで再現したのか少しでも多くの情報が必要になります。 実際に自分の環境で試してみて、再現した / しなかったかを書きましょう。 本番環境で再現するとエラーが出てしまう類のものであれば、ステージング環境等で確認するのも良いでしょう。 今回のインシデントの影響範囲をクエリやログなどで算出しよう Xやメールなどでお知らせを出すため、今回のインシデント対象となったユーザ(のID)や購入者(のメールアドレス)などが必要になります。 ただしどのケースでも、テーブル構造の知識やクエリを書く力が必要です。 不安がある人は、社内のデータ抽出ログを一通り読んでみると良いでしょう。 事象に関連したエラーを社内で使用しているツールより確認しよう インシデントの原因を見つける上で重要なのが、どんなエラーやログが吐かれていたのか確認することです。 そのために、社内で使用しているツール(BASEであればNew RelicやSentryなどを使用しています)の使い方について日頃から慣れておくと良いでしょう。 また、各ツールの通知を見れるチャンネルに入っているのも重要です。 すぐ検知できるように設定しておくと良いでしょう。 インシデント発生日にリリースしたものの中に怪しいものがないか確認しよう インシデント発生日が分かれば、原因がインシデント発生日にリリースしたものの中にある可能性が高いです。 その日何が本番環境へリリースされたのか確認してみましょう。 関連してそうなリリースがあればPRのリンクを共有、さらにPRを読んで該当のソースコードを共有するなどできるとGOODです。 revert PRマージやロールバックの判断を促そう 何度も言いますが、インシデント発生時は、今現在困っているユーザがいて、今すぐ対応をしないとどんどん体験を損なってしまうという状況が多いです。 その今すぐ対応というのは必ずしも修正対応(≒根本対応)とは限りません。revert PRマージやロールバックすることで一時的にも復旧して体験が損なわれるのを止められるなら、その対応が優先です。 後から根本対応も必要になりますが、一番優先しなければいけない目的を見失わないようにしましょう。 revert PRや修正PRを作ろう、あるいはレビューしよう 暫定対応であればrevert PRを作る必要があります。原因がわかるのであれば、慌てずにかつ急いで修正のPRを作る必要があります。 また、自分がPRを作っていなくても、レビューすることも重要です。少しでも早くリリースできるよう、その辺りの感度も高めておくと良いでしょう。 インシデント対応はインシデント対応をやることでしか上手くならない こんな記事を書いておいて身もふたもないですが、読むだけではやはりインシデント対応は上手くなりません。何故なら、実際にインシデント対応をしてみると上手くいかないことが多いからです。 思ったより時間が掛かっている間に、インシデント対応に慣れている他のメンバーがパパッと対応してしまった ツールの使い方に手間取ってしまった 知識不足でどうすれば良いかわからなくなってしまった 単純に慌てて頭が真っ白になったり(深呼吸しよう) など… こればかりは数をこなして徐々に上手くなっていくしかありません。設計も設計の数をこなさないと上手くならないと思いますが、インシデント対応も同じです。 また、自チームに関係するドメインのインシデント復旧対応だけ参加しても、なかなか上手くならないこともあると思います。なぜなら、都合よく自チームに関係するドメインのインシデントばかり起こるとは限らないからです。 自チームはもちろん、可能であれば自チームに関係しないドメインのインシデント復旧対応にも参加して数をこなしていきましょう。 また、インシデント対応は対象ドメインの中でも深い知識を要するケースが多く、対象ドメインを理解するきっかけになります。実は インシデント対応はドメイン知識獲得のチャンス なのです。悠長にしている暇はないのは確かですが、ピンチはチャンスだと思って取り組んでみましょう。 最後に ここに書いてあることはあくまで入門が目的なので、インシデント対応に必要なことはもっとあります。例えば、あらかじめソースコードを広い範囲で読む、テーブル・インフラ(AWS)構成を把握するなど... ただ、今回の記事に書いてあることが一通りできる人は、次何が必要なのか自ずと見えてくるものではないかと思います。なので、入門が目的であれば十分かと思っています。 また、今回は初動フェーズの話なので触れませんが、再発防止策を考えたりインシデント対応のふりかえりといったポストモーテムも必要な動きになります。 色々書きましたが私もできていない部分が多く、自戒の意を多分に含んでいます。 とはいえインシデント対応する人が1人でも増えると、会社・サービス・組織どの主語においても嬉しいことがたくさんあると思います。失敗を恐れずにインシデント対応をやっていきましょう。 明日のアドベントカレンダーは @takashi_matsuyuki さんの「HatenaBlog Workflows Boilerplateを試してみた」です!お楽しみに!
おや... このページにも雪がふってきましたね... これは BASE Advent Calendar 2023 の1日目の記事です。 なんだかさいきん寒いなぁと思っていたら、ついに雪がふってきてしまいましたね! このはらはらと舞い落ちる雪の結晶、HTML と CSS で作れちゃいます。 そして BASE には HTML 編集 App という機能があります。この機能を使うと...? snowrry.base.shop ↑のデモショップのように、ショップに雪をふらせることができちゃいます!是非これからの季節に試してみてください ⛄ BASE ショップに雪をふらせる方法 まず HTML 編集 App の使い方については、こちらの BASE U の記事を参考に設定してください。 baseu.jp 今回は「テーマを編集する」機能を使ってショップに HTML と CSS を追加します。 HTML と CSS をテーマに追加する 「テーマを編集する」機能のエディタ画面 まずは CSS を追加します。head タグの中に以下の style タグをコピー&ペーストして貼り付けてください。 <!-- === snow ここから追加 === --> < style > .snow-container { display : flex ; position : fixed ; width : 100 vw; height : 100 dvh; z-index : -1 ; } .snow-container .snow span , .snow-container .snow :: before , .snow-container .snow :: after { position : absolute ; width : 100% ; height : 100% ; color : snow ; text-align : center ; } .snow-container .snow.small { font-size : 1.2rem ; opacity : 1 ; } .snow-container .snow.medium { font-size : 1.4rem ; opacity : 0.8 ; } .snow-container .snow.large { font-size : 1.6rem ; opacity : 0.6 ; } .snow-container .snow.small span { display : block ; animation : fall- small 7s linear infinite ; } .snow-container .snow.small :: before { content : "❄" ; left : -30% ; animation : fall- small 9s linear infinite ; } .snow-container .snow.small :: after { content : "❄" ; left : 30% ; animation : fall- small 13s linear infinite ; } .snow-container .snow.medium span { display : block ; left : 10% ; animation : fall- medium 8s linear infinite ; } .snow-container .snow.medium :: before { content : "❄" ; left : -40% ; animation : fall- medium 10s linear infinite ; } .snow-container .snow.medium :: after { content : "❄" ; left : 40% ; animation : fall- medium 6s linear infinite ; } .snow-container .snow.large span { display : block ; left : -10% ; animation : fall- large 5s linear infinite ; } .snow-container .snow.large :: before { content : "❄" ; left : -35% ; animation : fall- large 6s linear infinite ; } .snow-container .snow.large :: after { content : "❄" ; left : 35% ; animation : fall- large 7s linear infinite ; } @keyframes fall-small { 0% { top : -20px ; } 3% { transform : translateX( 5px ) ; } 7% { transform : translateX( 5px ) ; } 18% { transform : translateX( -5px ) ; } 22% { transform : translateX( -5px ) ; } 38% { transform : translateX( 13px ) ; } 42% { transform : translateX( 13px ) ; } 58% { transform : translateX( -13px ) ; } 62% { transform : translateX( -13px ) ; } 78% { transform : translateX( 13px ) ; } 82% { transform : translateX( 13px ) ; } 100% { top : calc( 100% + 20px ); } } @keyframes fall-medium { 0% { top : -200px ; } 3% { transform : translateX( 5px ) ; } 7% { transform : translateX( 5px ) ; } 18% { transform : translateX( -5px ) ; } 22% { transform : translateX( -5px ) ; } 38% { transform : translateX( 13px ) ; } 42% { transform : translateX( 13px ) ; } 58% { transform : translateX( -13px ) ; } 62% { transform : translateX( -13px ) ; } 78% { transform : translateX( 13px ) ; } 82% { transform : translateX( 13px ) ; } 100% { top : calc( 100% + 20px ); } } @keyframes fall-large { 0% { top : -300px ; } 3% { transform : translateX( 5px ) ; } 7% { transform : translateX( 5px ) ; } 18% { transform : translateX( -5px ) ; } 22% { transform : translateX( -5px ) ; } 38% { transform : translateX( 13px ) ; } 42% { transform : translateX( 13px ) ; } 58% { transform : translateX( -13px ) ; } 62% { transform : translateX( -13px ) ; } 78% { transform : translateX( 13px ) ; } 82% { transform : translateX( 13px ) ; } 100% { top : calc( 100% + 20px ); } } </ style > <!-- === snow ここまで === --> 次に HTML を追加します。body タグの中(できれば要素の一番最初)に以下の div 要素をコピー&ペーストして貼り付けてください。 <!-- === snow ここから追加 === --> < div class = "snow-container" > < div class = "snow small" >< span > ❄ </ span ></ div > < div class = "snow medium" >< span > ❄ </ span ></ div > < div class = "snow large" >< span > ❄ </ span ></ div > </ div > <!-- === snow ここまで === --> これで追加する HTML と CSS は以上です。 プレビューモードをみてみましょう。 「テーマを編集する」機能のプレビュー画面 はらはらと雪の結晶が落下していたら成功です。 保存して、現在ご利用中のテーマであれば変更内容をショップページに反映させましょう。 テーマに合わせて CSS を微調整する 例えばショップの背景が白っぽかったりする場合は、CSS を調整して雪の結晶の色を変えたりすることができます。 .snow-container .snow span , .snow-container .snow :: before , .snow-container .snow :: after { position : absolute ; width : 100% ; height : 100% ; color : snow ; /* <- ここの色を好きなカラーコードに変える */ text-align : center ; } 元のCSSでは snow (カラーコード: #fffafa )が指定されていますが、ここを #87ceeb のような水色に変えることで雪の結晶の色を変えることができます。 落ちてくる速さや雪の結晶の大きさを変えたりなど、より高度なカスタマイズがしたいときも同じように CSS のプロパティの値を変えることで柔軟にスタイルを変えることができます。 以上がショップに雪をふらせる方法になります! ここから下は実装についての細かいお話になるので、興味のある人は読んでみてください 🥳 HTML / CSS の解説 簡単に説明すると実要素としては3つ、それに ::before と ::after の疑似要素をプラスした合計9つの「❄」が、アニメーションで上から下にひらひらと落ちてくるといった内容になります。雪の結晶はランダムな感じにしたかったので small / medium / large を用意しました。 .snow-container の CSS .snow-container は雪の結晶をまとめた親要素です。画面いっぱいに広がっています。 .snow-container { display : flex ; // 子要素の配置のために flex にする position : fixed ; // 要素は固定! width : 100 vw; // 横幅いっぱいにして height : 100 dvh; // 縦幅も dvh をつかっていっぱいに z-index : -1 ; // 背景にふらせたいので -1 を指定 } .snow の CSS .snow の中の span 要素と疑似要素が実際にふっている雪の結晶です。 .snow-container .snow span , .snow-container .snow :: before , .snow-container .snow :: after { position : absolute ; width : 100% ; height : 100% ; color : snow ; text-align : center ; } .snow-container .snow.small { font-size : 1.2rem ; // 雪の結晶の大きさ opacity : 1 ; // 小さい雪は遠くにあるのではっきりと } .snow-container .snow.medium { font-size : 1.4rem ; opacity : 0.8 ; } .snow-container .snow.large { font-size : 1.6rem ; opacity : 0.6 ; // 大きい雪は近くにあるので薄めに } small / medium / large それぞれの snow にアニメーションをつける animation で指定している秒数と left で指定しているパーセンテージは全体でランダムな感じになるようにキメ打ちしてあります。JS を使うとここがもっとランダムな感じになったり、foreach とか使って端的に書けたりするのかなぁ。今回は HTML / CSS だけで作りたかったのでけっこう愚直な感じで書いていきました。 .snow-container .snow.small span { display : block ; // span は inline なので block に animation : fall- small 7s linear infinite ; // animation をランダムな速さでつける } .snow-container .snow.small :: before { content : "❄" ; // 疑似要素の雪の結晶 left : -30% ; // ランダムな場所に設置 animation : fall- small 9s linear infinite ; } 落下アニメーションの中身 こちらも small / medium / large で0%のときの内容を変えています。これで落下タイミングが変わってきます。 ひらひら舞い落ちるアニメーションがカクカクしてしまわないような工夫を少ししています。 @keyframes fall-small { 0% { top : -20px ; // 少し上らへんから落ちてくるように。ここも small / medium / large で開始位置を変えてランダムにしている } 3% { transform : translateX( 5px ) ; // 5% と 10 の倍数の % から± 2% のあたりで同じ位置になるようにゆらゆらと } 7% { transform : translateX( 5px ) ; // ↑の 3% と同じ位置 } 18% { transform : translateX( -5px ) ; // 20% の± 2% で、↑の 5px と逆の -5px } 22% { transform : translateX( -5px ) ; // ↑の 18% と同じ位置 } 38% { transform : translateX( 13px ) ; // 最初より大きめな揺れ幅に } 42% { transform : translateX( 13px ) ; // ↑の 38% と同じ位置 } 58% { transform : translateX( -13px ) ; } 62% { transform : translateX( -13px ) ; } 78% { transform : translateX( 13px ) ; } 82% { transform : translateX( 13px ) ; } 100% { top : calc( 100% + 20px ); // 少し下らへんまで落ちきるように } } と、このような感じで CSS を書いていきました。CSS 書くの楽しいね ☺ BASE でショップを作るとこのように HTML / CSS が編集できるので、コーディング次第でカスタム性の高いサイトを作ることができます。 以上、最後まで読んでいただきありがとうございました!今年の冬はぜひインターネットに雪をふらせてみてください ❄ 明日は 大津さん の記事です。お楽しみに〜!🎄 ❄ ❄ ❄ article.entry { background-color: #00000000; } .snow-container { display: flex; position: fixed; top: 0px; width: 100vw; height: 100dvh; z-index: -1; } .snow-container .snow span, .snow-container .snow::before, .snow-container .snow::after { position: absolute; width: 100%; height: 100%; color: #87ceeb; text-align: center; } .snow-container .snow.small { font-size: 1.2rem; opacity: 1; } .snow-container .snow.medium { font-size: 1.4rem; opacity: 0.8; } .snow-container .snow.large { font-size: 1.6rem; opacity: 0.6; } .snow-container .snow.small span { display: block; animation: fall-small 7s linear infinite; } .snow-container .snow.small::before { content: "❄"; left: -30%; animation: fall-small 9s linear infinite; } .snow-container .snow.small::after { content: "❄"; left: 30%; animation: fall-small 13s linear infinite; } .snow-container .snow.medium span { display: block; left: 10%; animation: fall-medium 8s linear infinite; } .snow-container .snow.medium::before { content: "❄"; left: -40%; animation: fall-medium 10s linear infinite; } .snow-container .snow.medium::after { content: "❄"; left: 40%; animation: fall-medium 6s linear infinite; } .snow-container .snow.large span { display: block; left: -10%; animation: fall-large 5s linear infinite; } .snow-container .snow.large::before { content: "❄"; left: -35%; animation: fall-large 6s linear infinite; } .snow-container .snow.large::after { content: "❄"; left: 35%; animation: fall-large 7s linear infinite; } @keyframes fall-small { 0% { top: -20px; } 3% { transform: translateX(5px); } 7% { transform: translateX(5px); } 18% { transform: translateX(-5px); } 22% { transform: translateX(-5px); } 38% { transform: translateX(13px); } 42% { transform: translateX(13px); } 58% { transform: translateX(-13px); } 62% { transform: translateX(-13px); } 78% { transform: translateX(13px); } 82% { transform: translateX(13px); } 100% { top: calc(100% + 20px); } } @keyframes fall-medium { 0% { top: -200px; } 3% { transform: translateX(5px); } 7% { transform: translateX(5px); } 18% { transform: translateX(-5px); } 22% { transform: translateX(-5px); } 38% { transform: translateX(13px); } 42% { transform: translateX(13px); } 58% { transform: translateX(-13px); } 62% { transform: translateX(-13px); } 78% { transform: translateX(13px); } 82% { transform: translateX(13px); } 100% { top: calc(100% + 20px); } } @keyframes fall-large { 0% { top: -300px; } 3% { transform: translateX(5px); } 7% { transform: translateX(5px); } 18% { transform: translateX(-5px); } 22% { transform: translateX(-5px); } 38% { transform: translateX(13px); } 42% { transform: translateX(13px); } 58% { transform: translateX(-13px); } 62% { transform: translateX(-13px); } 78% { transform: translateX(13px); } 82% { transform: translateX(13px); } 100% { top: calc(100% + 20px); } }
こんにちは。BASE株式会社上級執行役員SVP of Developmentの藤川です。2023年のアドベントカレンダーも実施したいと思っており、この記事が1日目になります。 自分自身がBASE社に正式ジョインしたのは2014年8月、取締役CTOとして入社しました。僕は2代目のCTOですが、その後、3代目にCTOを渡し、今では上級執行役員SVP of Developmentというちょっと珍しい肩書で仕事をしています。組織としてはCTOの上長でもあり、自己紹介では技術担当役員と表現することもあります。 自分がBASE社に入社した段階ではシリーズBを迎えていました。象徴としては藤田ファンドから出資をいただいてから、上場を意識した組織に変えていくという空気感だったと思います。 BASE社には正式ジョインする前から技術顧問として関わっていて、週一だけ会社にあらわれるおじさんだったのですが、そのタイミングでは39歳でした。40代はBASE社に捧げたわけですが、先日50歳を迎え、自分自身の節目も含めて、2023年のタイミングで振り返ってみたいと思います。 入社直後の初期スタートアップ感 入社直後の開発チームはデザイナーもPdMの役割もセットで10人ちょっとぐらいのチームでした。専門職の集まりというよりはそれぞれがデザイン、フロントエンド、バックエンド、PdMなどを0.5ずつの役割を持っているようなタイミングだったとも言えます。 この規模の入社当初のCTOの役割というのは、経営チームにジョインしたものの実態は開発マネージャみたいな役割であったり、他社であれば、ほぼテックリードという組織が多いのではないでしょうか。 自分自身の入社時には、すでに開発が回っているチームになっていたので、自分はコードは書かずに開発チームの皆さんを支えますと言ったことを覚えています。今で言うエンジニアリングマネージャとして動きますという宣言ですね。 現実には、BASEのネイティブアプリのコードを見ていたり、当時かCTOがデプロイ作業をやっていたのでリリースマネジメントを手動でやっていたり、redmineで不具合や要望管理など現場の仕事は続けていました。アップル社のお仕事でエンジニアとしてクパティーノにお伺いしたこともあります。 当時は渋谷の道玄坂の途中にある新太宗ビルという飲食店やマッサージ屋さんも入っているビルにオフィスがあって、会社の目の前には飲み屋や男性向けのマッサージ店、道玄坂の客引きトラックの音が聞こえてくる猥雑な環境で、我々の働き方はすでにスタートアップ企業でしたから牧歌的とまでは言わないですが、どこかのんびりしていた環境下で自由に仕事をしていた時期だったと言えます。 のんびりと書くと語弊がありますね。 BASEの初期フェーズだと、例えば競合他社とのリリース競争というものがありました。他社様が機能をリリースしたら、我々もすぐさま追いついて、次の日にリリースを出すような時期もありました。ある種のプレスリリース競争とでも言いますか、ミート戦略とも世の中では言うそうなのですが、MBAで学ぶような動きを鶴岡が自然にやっていたのが印象的でした。 こういうのをのんびりというのもおかしいのですが、仕事的な緊張感というよりは、生き残るためにはそれをやらねば!というグルーブ感の中で動いていて、明日をも知れぬスタートアップの初期タイミングとして、サラリーマン的なお仕事のあり方とはちょっと違うような働き方をしていた時期だったとも言えます。 ただ、あまりに過度なミート戦略の追求は、現場の疲弊をもたらすので、あるところから自分たちのプロダクトをしっかり作っていこうとシフトしたのも覚えています。 後から考えると、まだこの頃は、代表の鶴岡が思うBASEというサービスのビジョンがわかっていなかったタイミングでした。例えば、実はfintech企業であったこと。BASEという無料のECのストアフロントに込められた想いというのは、その後、理解していくことになります。 いろんな課題の意思決定の議論の中で、本当に鶴岡が目指しているもの、大事にしているものなど、彼自身の価値観というのが少しずつ見えてきたタイミングでもありました。 初期スタートアップから会社としての成長 その後、ほどなくしてGスクエアという道玄坂交番前のビルに移転します。アメーバビルと言うと知っている人もいるかもしれませんが、以前はサイバーエージェント社が一棟借りされていた縁起の良いビルでした。 オフィスが強化され、当時、メルカリさんが株主になっていただいたこともあり、技術面はもちろん採用面でもアドバイスをいただくことになります。当時のメルカリさんと言えば、採用をブイブイやっていて、他社のCTO経験者クラスをバンバン入社させていくというスーパーエリートスタートアップだったわけですが、採用の手法について聞くと、本当に地道に声をかけて入社していただくということを聞いて、どこか開眼したと言うか、甘えがなくなったことで、我々の採用ペースも加速されることになりました。 BASE社はBASEとしてのビジョン 「Payment to the People , Power to the People」に共感していたける方々を探すべくひたすら自社でイベントをやったり、技術カンファレンスのスポンサーでBASEやPAYという名前を知っていただいて、その人が転職する際、転職エージェントから紹介されるリストからの選択肢に入れていただくことを目指して技術ブランディングを行っていったり、スカウト活動やエージェントとのリレーション強化などをやっていたように思えます。 当時の面接記録を見返すと、2016年では220人以上の面接記録があり、これが多いのか少ないのかわかりませんが、丁度、個人としても大学院の博士論文の佳境のタイミングと被り、ものすごくハードだったのを覚えています。カジュアル面談はこちらが主体に喋るので、喉が辛かったですね。ちなみに、ざっくり25人ぐらいの方に入社いただきました。一人で内定判断をしている時期でもあったので、今より内定基準が緩かったとは思いますが、面接に対する内定承諾率が、10%ぐらいでしょうか。 個人的に思うところとしては、この採用コミットはコードを書くとか技術に携わるという行為とは非常に相性の悪い行為だと思っています。感覚的な表現で伝わらないかもしれませんが、採用活動が自分自身の思考をBASE社のアピールという面に広げて考える活動であれば、技術に向き合う活動はサーバの動きに深く深く沈ませる行為だと思っています。 具体的には、コードを書いている時に面接の時間が来ると、心のスイッチングコストが高くて、面接のパフォーマンスが出ないという時期がありました。結果として、どっちつかずになるところから、技術に深く向き合うのをやめたという経緯があります。 それこそ、2016年、2017年の入社組は後のマネージャのリーダーやCTO、プリンシパルテックリードなど今のBASEを支えている最上位のメンバーになってくれた時期で、開発を彼らに任せるというか、片手間では彼らのスピードに到底ついていけなくなったのがこの時期です。チームが30人いたら、1人日の30倍ぐらいのスピード感で何かが変わっていくわけですから追いつけなくなりますよね。 それらの後にBASEというサービスの技術トップとしてCTOの役割を再定義し、現状の川口に肩書を譲るわけですが、技術に手を付けられなくなったためにCTOという肩書を移譲するという流れになるのは、権限委譲を前提としてチームそのものを大きくすることにコミットするからというのが一番の理由です。 その段階で僕の役割は、一般的な言葉で言うと「VPoE的なマネジメント役を含め、サービス以外の技術経営的は仕事はなんでもやる」という形になりました。 時期は前後しますが、それ以外にもスタートアップCTOとしてやらなきゃならないことが増えていくステップは共通してあるのではないかと思います。 1.初期ではCTOでやれていたAWSやリリースの管理がままならなくなってSRE専門職を採用する。SREにインフラを委ねることで自分自身から手離れしていく (サービスに関わるポイントが減っていく) 2.人が増えて無線LANが繋がらなくなる。ネットワークの整備をちゃんとしないといけないところで情シスチームが生まれ始めて、インフラやPCの管理が手離れしていく (情シスタスクの増加) 3.上場に向けてIT内部統制の整備が始まり、サービス技術以外への集中力が取られる (上場準備に伴うコーポレートガバナンスの整備) 4.サービスとしてはクレジットカード番号を保有していないのに、ECプラットフォームという理由で、クレジットカードを取り扱うセキュリティ基準であるPCIDSS対応を求められる (関係業界との連携) などなどが起きていき、サービス技術以外の仕事が増えていくことになり、技術の意思決定については権限移譲していく流れになります。もしかしたらここら辺で向いてなくて離脱する初期CTOの人もいるかもしれないですね。 上場から今 その後、2019年に無事上場を迎え、上場後のさらなる成長のためにチームの醸成、採用の強化、権限移譲などを進めていき、コロナ禍を超えて今に至るわけですが、2023年は、会社のフェーズが変わり、個人的にはコーポレートガバナンスを実現するオペレーションの必要性を強く意識するようになったタイミングであったと思っています。 あ、もっとちゃんとやらないとまずいんだということを知らしめられた一年でした。 雑な表現をすると発生確率が0.0035ぐらいのものが、スタッフ100人の会社だったら1を超えないけど、300人だったら1を超えて実際に問題が発生するってことが見えました。あらゆることを予見しながら、しっかりリスク管理をしないと躓くことが増え始めることがよくわかりました。 また他社様の事例を見てみても、同時期に上場したとある会社さんでCTO役だった方が、より技術にコミットできる開発部長みたいな形に変化したと言う事例があり、やはり役員陣の役割がよりコーポレートガバナンスが求められる方向に変わり、それによる最適配置だったという話を聞いて、自分の直感と感覚があってると共感しました。 組織としても、それまでたくさんの仕事をやりすぎていた自分自身の兼務体制も整理していく流れの中で、より分化された組織として技術マネジメントをしていくという構造に変化していくと思っています。 特にBASE本体の開発チームだけでなく、新規事業寄りであるPayID開発チームやBASE BANK開発チームのサイズが順調に大きくなっていき、それまでのBASE開発チームを中心としていた世界観から間違いなく、それぞれがしっかり組織として成立させていくフェーズに入ると思いますので、そこに携わるエンジニアマネージャの方々との間での全社的な統一感を作っていく必要性を感じています。 それでありながらアグレッシブさをうまく両立させる形で、BASE社の成長を作っていくんだなという感覚が強くありまして、チームマネジメント、セキュリティ、リスクマネジメントなどをしっかりしながら、当社の行動指針の一つであるmove fastを実現するというバランス感ある組織にしていく必要があるな、と思うわけです。 自分語りにしかなっておらず、あまり汎用的な事例としては書けなかった文章ですが、一人でも何か共感などをもっていただけたら幸いです。 BASEアドベントカレンダー 2日目もお楽しみに! devblog.thebase.in
こんにちは!BASE product blog編集部です。みなさまそろそろ年の瀬ですが、いかがお過ごしでしょうか。 今年も恒例のBASEメンバーによるアドベントカレンダーを開催します! 毎年公開しているアドベントカレンダーも今年で6回目を迎えます。 過去の様子 2022年のアドベントカレンダー 2021年のアドベントカレンダー 2020年のアドベントカレンダー 2019年のアドベントカレンダー 2018年のアドベントカレンダー 今年も毎日記事を公開する予定です。1日に2記事リリースされる日もあります! 下記の記事カレンダーも随時更新していきますので、ぜひお楽しみに! 記事カレンダー 日付 テーマ、タイトル 12/1 創業期CTOが残っている会社が上場するとどうなるのか 12/1 HTML / CSS でショップに雪をふらせましょう ⛄ 12/2 インシデント対応入門 〜初動フェーズ編〜 12/3 Hatena-Blog-Workflows-Boilerplateつかって BASEのブログ書いてみた 12/4 小数点の罠:メンバーシップポイント計算の裏側 12/5 Notion導入について(前編) 12/5 開発チームで取り組んだ働き方の実験10選(前編)〜 出社とか雑談とか 12/6 Notion導入について(後編) 12/6 開発チームで取り組んだ働き方の実験10選(後編)〜 スクラムイベントとか GitHub とか 12/7 現代メカニカルキーボード用語の基礎知識 12/7 “ゆるく、途中からでも” 学べる、夜な夜な.goというイベントをやっています 12/8 LocalStack/MinIO を導入して開発者体験が捗った話 12/9 フルサイクルエンジニアリングの第一歩を進める - BASE BANKでの新たな挑戦 12/10 われはセキュリティの子 12/11 180件のPRを遡って、良いレビューコメントをLintのルールに組み込んだ 12/11 Figma to STUDIO(β)体験記 〜STUDIOでLPを作るって、実は簡単? 〜 12/12 職場を明るくする 12/13 AI機能開発とインボイス制度対応を同時にやったら、意外と似ていた 12/14 SMS OTP で使われるメッセージの形式の歴史 12/15 LLMを利用したテキストアノテーションのツール化 12/16 ドメイン知識を素早くキャッチアップする時に心がけている事 12/17 Pay IDアプリのWebViewにReact + Viteを導入した 12/18 Android WebViewで少し厄介だった実装の紹介 12/18 Mintで管理しているSwiftライブラリを定期的に更新する 12/19 社内でのデータ活用を推進する取り組みの紹介 12/20 社内公募制度を使ってみました!〜Youは何しにBASE BANKへ?〜 12/21 カスタマーサポートと連携してコスパよく問い合わせ件数を減らした話 12/22 ぼくらがPMMをやる理由 12/22 組織のUXライティング力ボトムアップへの挑戦 12/23 元インターンがインターン採用やってる話 12/24 単体テストの考え方/使い方 社内読書会をしました 12/25 CTOに必要なものとは
はじめに BASE でプロダクト開発をしている @rry です。 さいきん BASE では 予約販売 App にて「長期の予約商品」を設定できるようになりました! baseu.jp 🗓️予約販売期間が1年先まで延長可能 🧵製作や入荷に時間がかかる商品も取り扱いやすく 🎄季節の限定商品を事前に販売開始も◎ 今までの予約販売 App では最長で8週間までしか予約設定することができず、オーナーさんからのフィードバックでも「8週間先も設定できるようにしてほしい」という要望が多かったため、このたび 予約販売長期化 PJ として機能改修を行いました。 この PJ の裏側を開発目線からご紹介していこうと思います。 PJ 概要 PJ キックオフから10/19リリースまでの期間:約8ヶ月 PJ メンバー:開発エンジニア 2-6人、QA エンジニア 1人、PM 1人、デザイナー 1人 アジャイル・スクラムで開発 1スプリント:1週間 デイリースクラム:毎日15分 スプリントレビュー・レトロスペクティブ・スプリントプランニング・バックログリファインメント:まとめて毎週火曜日 自分はこの PJ では PjM と主にフロントエンドの開発という役割で PJ をリードしていきました。 どんなことをやったのか 長期の予約注文は内部的には、決済時に一度取得した オーソリ(与信) をキャンセルし、時が来たら再度オーソリを取り直す、という方法で長期化を実現しています。 また、長期の予約注文が発送開始予定日を迎えるまでは注文を発送させないようにしなくてはいけません。そのため以下のような開発が必要でした。 バッチの作成 注文時のオーソリをキャンセルするバッチ オーソリを再取得するバッチ このときにオーソリが取れずキャンセルしなければならない注文はキャンセル処理もする オーソリ取得に成功して時が来たら注文の配送ステータスを「対応開始前」から「未対応」に変える 注文管理画面から長期の予約注文が確認できるようにする・配送ステータス「対応開始前」のときは発送できないようにする オーソリを取り直す必要があるため、長期の予約商品では Pay ID ログインが必須。そのためカートを改修する 注文の配送ステータスに今回新しく「対応開始前」が追加されたため、配送ステータスが影響している箇所すべてを改修する BASE API や他の Apps に影響がありました 予約販売 App 画面と商品登録画面から長期の予約商品が設定できるようにする 商品詳細画面で長期の予約商品は発送時期が上旬・中旬・下旬表記に変わり、注意文言が増える なんかいっぱいありますね! ザックリ時系列の流れ 2-4月: DB・アーキテクチャ設計、影響範囲調査、概算見積もり 設計についてはいろいろありましたが4月あたりから開発着手できるところから開発着手していきました 5-9月: 開発 カート -> バッチ -> 予約販売 App -> 注文管理 -> 商品管理 -> その他... みたいな流れで進めていきました 10月: リリース前 QA -> リリース このように開発が進んでいきました。 PJ を通して大変だったこと 半年以上の PJ をやってみて、いくつか反省点や大変だったことがありました。 インシデントはトータル5件 ユーザーに影響がないような小さいインシデントからユーザーに影響があるインシデントまで、この8ヶ月間でトータル5件起こりました。 ユーザーに影響があるインシデントが起こった箇所としては「商品管理画面」「ショップの商品詳細画面」で起こり、同じような不具合を起こさないように対策&周知してこれからも気をつけて開発していきたいと思います。 PJ 初期の段階でポイント見積もりに時間をかけすぎてしまった感がある スクラムをしていく上で、開発がいつ頃に終わるのか予測を立てるために、開発前に概算のポイント見積もりをしていました。 が、全体の仕様が決まりきる前に全体のポイント見積もりをしてしまったり、開発していくとポイントの重みが初期の重みから変わってしまったりしてしまいました。こうなってしまうと初期につけたポイント見積もりはあまり意味のないポイントになってしまい、見積もりにそこまで時間をかけるよりも開発に時間をかけたい、となってしまいます。 この反省を生かして、次から全体のポイント見積もりを行うタイミングは、 どこまで開発するかの要件や仕様と設計が定まってから 数回スプリントを回してみてポイントに対する肌感が開発エンジニアの中で揃ってきたときに するのが良いのではないかと思いました。 次回の PJ では、開発初期は「ここまでにこれを開発していたい」というざっくりとしたマイルストーンを置くくらいに留めて、全体の概算見積もりは仕様と設計が固まってからにしたいです。 色々あった結果既存コードが多く残っているリポジトリでの開発で DDD を採用しないことにした こちらについては長くなるのですが、誤解されてしまうのを恐れずにあえてザックリめに書きたいと思います。 BASE では現在、既存コードが多く残っていて昔からあるリポジトリから、DDD + クリーンアーキテクチャで作られた新しいリポジトリにリアーキテクチャをしていっている最中です。カートのバックエンドなどは新しいリポジトリにコードがあります。いずれはバックエンドのコードはこの新しいリポジトリにコードを移していく予定です。 ですが今回は既存の注文まわりのコードが新しいリポジトリにないため、昔からあるリポジトリ上でメインの開発をすることにしました。 では、昔からあるリポジトリで今回の開発をする場合は、どういう設計にするのが適切でしょうか? 昔からあるリポジトリでも一部コードはドメインモデリングがされていて DDD のアーキテクチャが採用されていたり、はたまた軽量 DDD のような設計で構成されている状態ではありますが、まだまだ大部分のコードは MVC + S のアーキテクチャです。 当初予約 PJ では、 新しく書くコードの設計は DDD でいこう、いやでも昔からあるリポジトリでは DI や DB トランザクションまわりの基盤機能が十分ではないしな... それなら軽量 DDD にするか...?いやでもドメインモデリングがしきれないのに DDD のアーキテクチャパターンを少しだけ囓ったような設計ははたして「軽量 DDD」と呼べるのか... 否、呼べないな... そして社内でチーム内外含めて議論する日々... と、このようなことが起こりました。 そこで話し合った結果、以下の内容でチーム内外含めて全体での合意がとれました。 境界づけられたコンテキストで明確に分けられることが分かっているドメインであること ドメインモデリングをしており、尚且つそのドメインモデリングを保守・運用していくこと DDD による設計が生きてくる程度にはある程度複雑性のあるドメインであること この3つが満たされる場合のみ、昔からあるリポジトリでも DDD で開発することができます、満たせない場合は DDD で開発するのはやめましょう。 そして予約 PJ の場合は注文に関係するドメインと密接に関わっていて、「境界づけられたコンテキストで明確に分けられることが分かっているドメイン」ではなかったため、昔からあるリポジトリでのメインの開発にて DDD は採用しないことにしました。 ではどんな設計にしたのか? 基本は MVC + S のアーキテクチャに則っています。ただし、開発するボリュームを考えるに Service が肥大化するのが目に見えています。 そこで、以下のような設計を行いました。 Entity ... Model 層が実質2層に分かれている フレームワークの Model の責務はデータアクセス(DB クエリビルダー) 連想配列ではなく、ORM 的なオブジェクトとビジネスロジックは Lib/Entity へ(ValueObject のようなもの) Shared Service ... Service 層を2層に分ける Service ... Controller(もしくは shell)から1処理あたりに call できる Service は原則1つ、いわゆる MVC + S の S Shared Service ... Controller(もしくは shell)などから直接 call してはいけない、Service 層もしくは Facade 層から call する Facade ... いわゆる Facade パターン。外から Entity や Shared Service を呼ぶときは Facade を使う ここまで大変だったことを長々と書きましたが、PJ を通して良かったこともありました。 PJ を通して良かったこと 当初の設計から大幅な仕様変更や、影響範囲の考慮漏れなどがなく PJ を終わらせることができた 前回自分が担当した PJ では、開発終盤で影響範囲の考慮漏れがみつかり阿鼻叫喚、みたいなことがありましたが、今回はその反省を生かしてあらかじめ影響範囲調査をしっかりとやったので今のところ考慮漏れバグみたいなものはみつかっていないです。 バックエンドのテストカバレッジ結果が良かった これはバックエンドエンジニアの方がメインでテストを手厚く書いていったことによる功績ですが、今回新規追加したバックエンドコードのテストカバレッジ結果が他コードと比較してとても良かったです。 リリースしてから大きな不具合なく済んでいることに影響していると考えます。 フィーチャートグルを使ってビッグバンリリースにせず小出しにリリースできた 半年以上かかる規模の PJ だったので、初期からフィーチャートグルの仕組みを使ってスプリント中に開発したものをメインのブランチにマージしていきながら開発できたのは開発体験が良かったです。そしてリリース当日はフィーチャートグルを全ショップ公開にするだけでリリースすることができました。 QA エンジニアに PJ 開始時から入ってもらえた 今回の PJ では、PJ 当初から QA エンジニアの方に Join してもらうことができ、スプリント中に機能ができあがり次第随時機能単位での QA をしてもらうことができました。 また手厚い QA 項目があったおかげで、リリース前の複雑なバッチの QA なども手分けして行うことができました。 おかげで安心して開発を前に進めることができ、クリティカルな不具合がなく済んでいることに影響しています。 以上、これらをやることによって開発エンジニアとしては PJ を前向きな気持ちで進めることができました。 おわりに 長くなりましたが、以上が予約販売長期化 PJ の開発の裏側になります。 今回は開発にフォーカスしましたが、PdM やデザイナーの方など優秀な人たちの力を借りてようやくリリースできた PJ だと思っています。 これからも BASE のプロダクトをより良いものにしていくような開発を心がけてやっていきたいです! 🚀
ogp CTOの川口 ( id:dmnlk ) です。 2024年2月10日に行われるYAPC::Hiroshima 2024にBASE株式会社としてPlatinumスポンサーをさせていただくことにしました。 ついでと言ってはなんですがスポンサーLTもします。話す内容はまだ未定です。 YAPCはPerlを軸としたカンファレンスですが、Perlに限らず多様な技術について語られるカンファレンスです。 タイムテーブルを見ても多分Perlネタの方が少ないんじゃないかっていう気がするくらいには多様です。 fortee.jp キーノートに杜甫々さんが登壇されることが決定したタイミングで、個人的に行こうかなと思っていました。 blog.yapcjapan.org 2000年頃からインターネットに触れ、「とほほのWWW入門」にお世話になっていた自分にとってはヒーローに近い存在です。 ちなみに「とほほのKubernetes入門」など最近の技術についても未だに更新されています。凄い。 その後、都内某所吉祥寺であったとある会において、実行委員長のkobakenさんにお会いしてその熱い想いに感銘し会社としてスポンサーをしてみることにしました。 去年のYAPC Kyotoには行けずだったので久々のオフラインYAPCが今から楽しみです。 お会い出来る方、飲みに行ける方、そして登壇者と運営チームの皆様、当日よろしくお願いします。
ogp CTOの川口 ( id:dmnlk ) です。 2024年2月10日に行われるYAPC::Hiroshima 2024にBASE株式会社としてPlatinumスポンサーをさせていただくことにしました。 ついでと言ってはなんですがスポンサーLTもします。話す内容はまだ未定です。 YAPCはPerlを軸としたカンファレンスですが、Perlに限らず多様な技術について語られるカンファレンスです。 タイムテーブルを見ても多分Perlネタの方が少ないんじゃないかっていう気がするくらいには多様です。 fortee.jp キーノートに杜甫々さんが登壇されることが決定したタイミングで、個人的に行こうかなと思っていました。 blog.yapcjapan.org 2000年頃からインターネットに触れ、「とほほのWWW入門」にお世話になっていた自分にとってはヒーローに近い存在です。 ちなみに「とほほのKubernetes入門」など最近の技術についても未だに更新されています。凄い。 その後、都内某所吉祥寺であったとある会において、実行委員長のkobakenさんにお会いしてその熱い想いに感銘し会社としてスポンサーをしてみることにしました。 去年のYAPC Kyotoには行けずだったので久々のオフラインYAPCが今から楽しみです。 お会い出来る方、飲みに行ける方、そして登壇者と運営チームの皆様、当日よろしくお願いします。