私はキャリアの大半をスタートアップで過ごしてきました。予算は限られ、人手は足りず、納期は迫る——そんな状況が日常です。「とにかく今日、何かを出す」というプレッシャーに負けて手を抜きたくなりますが、その代償として数年後に技術的負債の山に埋もれ、自分の仕事が嫌になり、燃え尽きてしまうことになりかねません。
残念ながら、これは身をもって経験した話です。かつて3人分の仕事を一人で抱え、3年間そのペースで走り続けた結果、何にも興味が持てなくなり、ただ天井を眺めて一日を過ごしたいと思うところまで追い込まれました。
ご安心を。あれは過去の話で、今はすっかり立ち直っています。その後のキャリアでは同じ過ちを繰り返さないよう意識して歩んできました。そうしているうちに、長期にわたってイノベーションへの熱量を保つために欠かせないいくつかの要素が、自分の中で整理されてきたのです。
幸いにも今は、その経験を共有し、他の方の挑戦を後押しできる立場にいます。
これはあくまで私の物語、私の経験です。皆さんにもそれぞれの物語があるはずで、ぜひ聞かせていただきたいと思っています。
理想のチーム像
5〜10人規模のソフトウェア開発チームを思い浮かべてみてください。そのチームは——
- 新機能を継続的に、しかも遅れずリリースしている
- ダウンタイムを顧客より先に検知している
- バグは素早く修正される——ここで言う「修正」とは、gitのmainブランチではなく本番環境で直っていることを指します
- 新しい技術を調査し、取り入れる余裕もある
しかも、息切れすることなく何年もこの状態を保てているのです。
うますぎる話に聞こえるでしょうか。本当に実現できるのか、見ていきましょう。今、私が新規プロジェクトを立ち上げるとしたら、こう進めます。クラウドベースの案件で、私の場合はGCPを使う前提です。
1\. 土台を理解する
セキュリティはとかく後回しにされがちです。開発者は「難しい」と感じ、クラウドの初期設定を超えて開発に着手できる最低限だけを学んで済ませてしまいます。結局、顧客が買うのはセキュリティではなく機能であり、彼らから見ればプロダクトのセキュリティは「あって当たり前」のものだからです。
残念ながら、こうした姿勢はすぐに悪い習慣を生みます。「公開gitリポジトリにシークレットキーが入っていた」なんて話、聞いたことありませんか? : )
押さえておきたいポイント:
- 使う技術に応じて、チーム内でセキュリティの基準を維持しましょう。たとえば新しいクラウドを採用するなら、DevOps担当だけでなく全員が基本を理解している状態にします。GCPであれば、平均的な開発者は数時間でセキュリティモデルに馴染めます。
- セキュリティのベストプラクティスについて相談できるメンター役を確保しましょう。チーム内に適任者がいない場合、たとえばGoogleには、有償でこのテーマの相談相手を務めてくれるパートナーがいます。
クラウドの責任共有モデルを忘れずに——クラウド事業者が提供するのはセキュアな部品であって、それを安全に組み上げる責任はこちら側にあります。
セキュリティの土台が見えたら、いよいよアーキテクチャ設計に入れます。
2\. 設計はするが、現実を見据える
誰しも、自分の会社が次のTwitterやUber、Googleになることを夢見ます。しかし、彼らも最初からグローバル規模だったわけではありません。
難しいのは、小さく始めながらも成長の余地を常に残しておくことです。そのために私が勧めているのは、チーム内に小さなタスクフォースを設け、戦術的な発想にチームが引きずられすぎないよう見張る役を担わせることです。「機能を量産する」モードに入ると、どうしても短期視点に流れてしまうからです。このタスクフォースは、たとえば次のような場面で適切なトレードオフが行われているかを監視します。
- かつて私はElasticSearch向けのバッチジョブマネージャを開発しました。アクティブ/アクティブのスケールアウト構成で実装すれば10倍の工数がかかったでしょうが、シングルトン構成でも100倍の負荷増加に耐えられました。バグやメンテナンスで10〜15分オフラインになっても許容できる要件だったのです。
このケースでは、ビジネス要件に照らして十分だと判断したうえで、意図的にシングル・ポイント・オブ・フェイラーとなるコンポーネントを採用しました。
- 戦略上、クラウドに依存しないプロダクトを目指したい場合もあるでしょう。アプリケーション基盤としてKubernetes(K8s)を選べば、それだけで目標の約80%は達成できます。ただし、ロードバランサやメッセージキューなど、Kubernetes外のクラウドサービスとの連携は依然として必要です。納期に追われる中で特定プロバイダ固有のサービスを安易に選んでしまわないよう見張るのが、アーキテクチャタスクフォースの役目です。やむを得ず採用する場合でも、それが意識的かつドキュメント化された判断であることが重要です。
シニアなテックリードはこの役割を自然と引き受けますが、より公式に位置づけ、彼らの声がきちんと届くようにするのが理想です。「半年前に壊れるって言ったよね」で始まるポストモーテムが出てきたら、アーキテクチャタスクフォースの運用を見直すべきサインです。テーマは「チームを信頼すること」に尽きます。
アーキテクチャタスクフォースが機能し、チーム全体がその恩恵を受けるためには、プロダクトオーナーやマネジメントが「開発者は善意で動いている」と信頼することが欠かせません。責任感のある開発者ほど機能追加のプレッシャーに屈しやすく、放っておくとあっという間に「だから言ったでしょ……」モードに陥ってしまうのです。
3\. コーディングはCI/CDから始める
少し過激に聞こえるかもしれませんが、ここで耳を傾けていただけたなら幸いです。よくある落とし穴は「機能が先、テストは後」というアプローチ。納期に追われると、テストは真っ先に削られる項目になりがちです。
個人的なエピソードを一つ。あるプロジェクトで、私たちは素晴らしいアーキテクチャを組み上げていたのですが、たった一つだけ抜け落ちていたものがありました——プロダクトをどうテストするかをまったく検討していなかったのです。5回目のスクラムスプリントでようやく、毎回スプリントレビューの2〜3日前に「統合作業の修羅場」を繰り返していること、そしてそれがスプリントごとに悪化していることに気づきました。2つ目の過ちは、機能開発の渦から抜け出して「設計に立ち戻り、CIを見直す必要がある」とプロダクトマネジメントに明確に伝えられなかったことです(前章のチームを信頼することの話を思い出してください)。プロジェクト開始から1年後、後付けで作ったCIは動かないことの方が多く、開発者にとって絶え間ないストレス源になっていました。この「巨大な泥団子」はあまりに膨れ上がり、修正のための投資が白紙委任で認められたとしても、再設計の最善策についてチーム内で合意することすらできない状態だったのです。
CIとCDの両方を中核アーキテクチャに組み込むと、2つのことが起こります。
- 「システムが本当に動いているとどう判断するのか?」と自問するようになります。これが自然と、運用メトリクスを取得するためのコード計装へとつながります。
- 夜間のCI失敗をデバッグするうちに、すでに起きた問題を分析・修正するためのスキルとツール(適切なログやトレーシングデータの収集など)が身につきます。これらはまさに、本番障害をデバッグする際にチームが必要とするスキルそのものです。
副次的な効果として、本番投入前の段階でロギングと監視の仕組みが整っていることになります。ただし監視・ロギング基盤については一つ警告しておきたいことがあります。SaaSであれ自前構築であれ、運用コストは想像以上に早く膨らみ得るということです。事前に潜在的なコストを見積もり、判断材料に含めることを強くお勧めします。
CIの運用が満足のいく水準に到達して初めて、ときには「機能が先、テストは後」を許容してもよくなります。ここで言う「後」とは、フィーチャーフラグで無効化できる使い捨てのデモ機能でない限り、「次のスプリント」を意味します。
各クラウドにはネイティブな監視・ロギングサービスがありますが、それが最適解とは限りません。Kubernetes(K8s)ならCDはほぼ整備されていますが、CI側は自分たちでしっかり調査が必要です。3大ハイパースケーラーはいずれもCIツールを提供していますが、正直に言って、それが彼らの本業ではないのです。
4\. 作った者が運用する
「You build it — you run it(作った者が運用する)」は、小規模な会社では当たり前のことです。そもそも、SREという「壁」の向こうに投げ込む先がないからです。問題は誰が本番を抱えるかではなく、本番ワークロードを抱える体験がどのようなものかにあります。
嬉しいことに、これまでのステップをきちんと踏んでいれば、本番運用の当事者になることはチームにとってごく自然な流れになります。順に見ていきましょう。
- セキュリティから着手しているので、境界分離はすでに整っているはずです。どこを見れば問題が分かるかも把握できており、開発用のコードが誤って本番データベースに繋がって大惨事になるような事態は、そもそも起こり得ません。
- アーキテクチャは現実的で、チームの実力に見合っています。シンプルで理解しやすい。たとえば、本当に必要で使いこなせる場合以外は複雑な分散システムを抱え込みません。観測性ツールを武器にすれば、問題の原因も素早く突き止められます。
- CIは安定していて信頼でき、CDはスムーズです。だからバグ修正の際も、テストとデプロイは速く、簡単で、認知負荷も精神的負担もありません。これでバグによる消耗が最小限に抑えられ、継続的メンテナンス状態に陥るのを防げます。
アプリケーションには既に観測性が組み込まれているので、あとは監視ダッシュボードを構成し、アラートを設定するだけです。
本番運用の当事者であることには、特に開発者の間ではあまり意識されない側面があります。それは、プロダクトの運用コストです。私自身は、開発者もクラウドの課金モデルを理解し、自分の書いたソフトウェアがどれだけのコストを消費しているかを把握すべきだと考えています。理由は以下の通りです。
請求額の不意打ちを避ける
システムを正しく設計するには、クラウドの課金モデルを理解する必要があります(請求額ショック、心当たりはありませんか?)——アプリケーションアーキテクチャも、CI/CDアーキテクチャも同様です。仕上げに、請求予算とアラートを設定しておきましょう。
ユニットコストを把握する
「ユニットコスト」を把握しておくことは、プロダクト担当者やセールス担当者にも有益です。たとえばエッジデバイスを監視するプロダクトなら、「ユニットコスト」とは監視対象デバイス1台あたりがクラウド請求額に与える影響額のことです。この数値はビジネスモデルの設計に非常に役立ち、キャパシティプランニングから当て推量を排除してくれます。
アプリケーションメトリクスが揃っていれば、課金データを深掘りしてアプリのロジックとコストを紐づけるのは、たいてい難しくありません。
5\. 人は人のために働く
私たちはビットやバイト、形式論理を愛してやみませんが、結局のところ、私たちは共に働く人間の集まりです。
スタートアップに飛び込むのは、何かを信じ、それに情熱を抱いているからです。
マネージャーの皆さん、開発者は善意で動いていると信じてあげてください。彼らのアイデアに健全な議論をぶつけつつ、イノベーションの余白は残しておきましょう。開発者が守りに入る兆候には注意が必要です——そこから先は、どの機能もにわかに「実装が難しい」ものに見え始め、プロジェクトは停滞していきます。
クラウドや技術の話は一日中でもできますが、仕事そのものが楽しくなければ、イノベーションは長続きしません。