
14 年間のエンジニア経験の中で、私は多くの人がまったく新しいサービスに取り組む機会に基づいてキャリアを決定するのを見てきました。その決定には何ら問題はありません。しかし、今日は退屈な移行プロジェクトに取り組むという矛盾したケースを取り上げます。キャリアの初期には気付いていませんでしたが、私の基礎的なソフトウェア開発の学習のほとんどは移行プロジェクトから得られたものでした。たとえば、基盤となるデータ ストアを別のクラウドベースのテクノロジに移行する、モノリシック サービスを廃止して新しいマイクロサービスを採用するなどです。
これは、移行が本質的に困難であるためです。つまり、複数のエンジニアによって何年にもわたって構築され、磨かれてきた可用性、スケール、レイテンシー、顧客エクスペリエンスに関する既存の基準を満たすか、それを超えることを余儀なくされます。新しいシステムでは、これらの制約に直面することはありません。制約は自由に定義できるからです。それだけでなく、移行をどれだけ徹底的に行っても、システムの新しい部分に切り替えるときに対処しなければならない隠れた問題が存在します (Doordash のデータベース フィールドの Int から BigInt への移行がいかに多くの障害を伴っていたかについては、この興味深い記事をご覧ください)。
これらのプロジェクトでは、テスト方法、新しいシステムの結果の正確さ、ソフトウェアのロールアウト計画、ソフトウェアのロールバック計画などについて綿密に考える必要があります。そのため、サービス提供先の既存の顧客がいないために、常に新しいシステムで作業することにストレスを感じることはありません。最も退屈なのは、既存の顧客は、知らないうちに基盤となるシステムまたはコード ベースを実際に置き換えたことを知らないはずだということです。
新しいエンジニアが新しいテクノロジーを試して既存の機能を置き換えたい、またはコード ベースを完全にリファクタリングしたいと考えているのをよく見かけます。これが限定的な変更 (たとえば、十分にテストされたオープン ソース ライブラリを使用してサービスで小さな操作を実行するなど) であれば、私は気にしません。ただし、アーキテクチャの大幅な変更やコード ベース全体の作り直しの場合は、「以前のものを尊重する」という有名なエンジニアリングの信条を思い出すことが重要です (レガシー コードを伝説のコードと呼んでいるこのツイートは面白いと思いました)。
移行プロジェクトのポイントに戻ると、コードベースやアーキテクチャを大幅に見直すよりも、比較的少ない労力で同じ問題を解決できるかどうかを評価するのが常に賢明です。しかし、新しいテクノロジーや設計パターンを使用する魅力は常に魅力的です。では、この決定をどのように評価すればよいのでしょうか。移行の旅に着手する前に、始めるのに役立ついくつかの質問と考慮事項を次に示します。
この問題を解決せず、大規模な移行プロジェクトに着手せずにこの問題を解決するためのすべてのオプションをチームが試し尽くした場合、ビジネス (または顧客体験) に悪影響が及ぶか、将来的に影響が出るでしょうか。チームに所属していない、あなたの推論をプレッシャーテストするための代弁者として機能できる別の上級エンジニアによるレビューを選択してください。正当化の例としては、機能のリリースごとに 4 開発者月で敏捷性を向上させる、サービスごとに異なる技術スタックを使用して p99 レイテンシを 400 ミリ秒改善する、X TPS を超えるスケーリングのボトルネックを取り除くなどが挙げられます。このような状況では、確証バイアスを打破するために常に意見の相違を探してください。
移行にかかる労力と、移行によって得られるメリットを比較すると、プロジェクトのメリットを享受し始めるまでにどれくらいの時間がかかるかを見積もることができます。私が共有できる個人的な例は次のとおりです。
私のチームは、2 つの異なる顧客ベースにサービスを提供する 2 つの別個のシステムを所有しており、新機能をリリースするたびに、チームはこれらの別個のシステムに同様の (ただしまったく同じではない) 変更を加える必要がありました。全体として、重複により、機能ごとに毎月 1 人の開発者の追加作業が発生しました。私たちは毎年、このような機能を約 4 つリリースしたため、4 か月の開発作業が重複または無駄になりました。これはエンジニアにとってフラストレーションのたまるものでした。エンジニアの 1 人が、これら 2 つのシステムを統合する提案を思いつき、その作業は 24 か月の開発作業であると見積もりました。チームが移行のメリットを享受し始めるまでには、24 回の機能リリースと 6 年 (年間 4 つの機能をリリースすると仮定) かかります。私たちは移行を行わず、共有ライブラリを使用する代替アプローチに移行して重複作業を 50% 削減し、その後、3 年後にシステムを廃止して別のサービスを導入しました。
場合によっては、移行はより広範な目標( Amazon が Oracle から切り替えるなど)を達成するためのトップダウンのガイダンスであり、分析は引き続き実行できますが、プロジェクトを続行するために承認を得る必要はありません。
移行を実行する正当な理由を特定し、外部のエンジニアやリーダーとともにその理由を厳格にテストしたら、次のステップに進みます。
これは、システム設計の面接の準備をする時に行うことと似ています。機能要件と非機能要件が提示されたら、とりあえず既存のシステムについては忘れて、制約がない場合に新しいシステムをどのように構築するかを提示するのが賢明です。
この演習を行う理由は、既存のチーム メンバーの多くが、既存のシステムとあまり変わらない新しいシステムを構築しようとする無意識の偏見を持っており、多くの場合、移行の目的そのものが台無しになってしまうからです。私の過去の別の例を考えてみましょう。
既存のシステムで作業したことのない、より経験豊富な人物を関与させることで、よりスケーラブルでリアルタイムで、保守が容易なまったく異なるシステムを構築する方向に会話が進みました。これは常に可能であるとは限りませんが、この演習を試してみることは損にはなりません。
以前提案したような同等の移行 (オンプレミスの SQL DB をクラウド SQL DB に移行するなど) を行う場合は、非機能要件を満たすのが簡単になる可能性があります。ただし、エンド システムが現在のシステムと大幅に異なる場合は、少なくともシステムに組み込まれているアンチパターンを修正する試みを行う必要があります。たとえば、データベース内のキーの更新変更をポーリングする代わりに、 Pub/Sub サービスを使用してサブスクライバーに変更通知を発行できます。
ただし、分散システムのすべてのプロジェクトと同様に、移行には非機能要件に関してトレードオフがあり、それについて計画する必要があります。たとえば、2 つの別々のビジネス関連の計算 (納期見積りと配送料見積り) を処理する、可用性が 99.9% のモノリス サービスがあり、この責任を、それぞれ可用性が 99.9% の 2 つのマイクロサービス A (納期見積りサービス) と B (配送料見積り) に分割することにした場合、システム全体の可用性は次のようになります。
P(A) * P(B) = 0.999 * 0.999 = 99.8% の可用性
モノリスからマイクロサービスを作成すると、可用性が 99.9% から 99.8% に低下しました。
常に覚えておいてください。クライアントに応答を返すために「n」個のサービス呼び出し (順次または並列のサービス呼び出し) の結果が必要な場合は、「n」個の各サービスの個々の可用性を掛け合わせて、システムの最終的な可用性を算出します。
システムの本来の可用性 (つまり 99.9%) を満たすかそれを超えるには、キャッシュや再試行などの他の手法について検討する必要があります。ただし、これらのオプションにはそれぞれ欠点があります。たとえば、キャッシュは、場合によっては、システムが古いデータを許容できる必要があることを意味します。再試行は遅延を追加し、システムが再試行ストームの影響を受けやすくなる可能性があります。
ただし、この演習を行うことで、非機能要件に関する既存の基準を少なくとも満たしているかどうか、または顧客に提供する新しい非機能要件についてリーダーシップの承認が必要かどうかを確認できるようになります。
新しいシステムでは、顧客は新しいクライアント バージョンを採用することになります。移行プロジェクトでは、すべての顧客が新しいバージョンのクライアントに移行できない場合の問題に対処する必要がある場合があります (つまり、下位互換性を考慮する必要があります)。すべてのクライアントが社内のクライアントである場合、または社外での採用が限られている場合は、すべての顧客と協力して新しいバージョンのクライアントに移行できます。
他のケースでは、これは単純に不可能です。たとえば、業界で広く採用されている大規模なクラウド サービスを所有している場合、すべての顧客にクライアントの新しいバージョンへの移行を強制することはできません。これにより、チームのメンテナンス オーバーヘッドだけでなく、大きな障害が追加される可能性があります。場合によっては、2 つのバージョンのシステムを維持し、古いシステムをメンテナンス モードにして (つまり、このシステムに新しい顧客が追加されないようにする)、古い顧客に、顧客にとってのメリットが向上するため、システムの新しいバージョンに移行するインセンティブを与えることが解決策になります。
ただし、上で Doordash に共有したリンクのように、主キーのデータ型として Int を使用するとオーバーフローが発生する状況の場合は、全員に移行を強制する以外に選択肢はありません。
新しいシステムを構築する場合、ほとんどのエンジニアはほぼすべてのユースケースをカバーする素晴らしい仕事をします。しかし、移行の場合は逆のことが起こります。なぜなら、数十人、場合によっては数百人のエンジニアが開発、パッチ適用、保守したシステムを扱うことになるからです。すべてのユースケース、コード パス、システムのボトルネックについて学びたいと思っても、サービス全体を把握するのは困難です。
このような場合、最も簡単な方法は、同様の移行を実施したチームや上級エンジニアなどから、盲点を補うためにどのようなプロセスに従うべきかについて学びを得ることです。多くの企業は、より広範な組織レベルの設計と移行のレビューのプロセスに従います。アプローチと理解を固めるために、プロセスの神聖な一部として意見の相違を探してください。移行には、予期しない方法でつまずく地雷がいっぱいです。
移行の大部分は通常、以下の 2 つのカテゴリのいずれか、または両方の組み合わせに該当します。
サービスの移行:既存のサービスを廃止し、新しいアーキテクチャを採用します。新しいアーキテクチャでは、現在のサービスの一部と新しいサービスを使用したり、新しいマイクロサービスを起動して既存のシステムを置き換えたりします。
データストアの移行:既存のデータストアを廃止し、新しいデータストアに置き換えるか、イベント駆動型システムを利用します。
正確な移行例が見つからない場合でも、これらのバケットからより広範な学習を常に引き出すことができます。私の個人的な経験では、データ ストアの移行は、古いデータ ストアと新しいデータ ストア間の一貫性の問題によって影響を受けるデータの正確性に関する懸念があるため、最も困難でした。たとえば、伝播の遅延により、ユーザーは新しいデータ ストアからのデータの古いバージョンを見る可能性があります。
既存システムと新しいシステムを並行して実行し、既存システムからのデータのみを提供することで、両方のシステムの結果を実際の顧客リクエストと比較できます。これは、新しいシステムが正しく動作することを比較および検証するための最も有用かつ強力なステップです。
何年も前、私は新しい技術スタックへのサービス移行に取り組みました。古いサービスが顧客からのリクエストを受け取るたびに、バックエンドで新しいサービスへの並列非同期呼び出しを行っていました。既存および新しいサービスの結果を S3 の場所に記録していました。そして、1 日の終わりに AWS Athena クエリを実行して、矛盾点を見つけ出し、新しいサービスの問題を特定しました。これは、データ ストアで処理した別の厄介な移行と比較すると、まだある程度予測可能な作業でした。
私たちは、より信頼性が高く新しいデータ ソースから取り込まれた古い SQL データ ストアを新しい NoSQLデータ ストアに移行していました。しかし、古いデータ ストアと新しいデータ ストアはまったく異なる 2 つのシステムから取得されたため、特定のキーが更新されるタイミングは予測できませんでした。
古いデータ ストアと新しいデータ ストア間でデータを比較する複数のアプローチを試みたがうまくいかなかったため、上流チームと協力してデータ キーのバージョンをリリースし、両方のシステム間でバージョンを使用して特定のキーのデータ精度を比較できるようにしました。プロジェクト終了後はバージョンは必要なかったので、これは無駄な作業でしたが、これを処理する他の方法がありませんでした。
ステップ 5 を実行して、古いシステムと新しいシステムの結果を徹底的に正確に比較できた後でも、システムをほとんど使用しない少数の顧客からの特定の種類のリクエストにまったく対応できない可能性は十分にあります。私は、これらの移行プロジェクトに取り組んでいる間、「新しいシステムですべてがうまくいかなかったらどうしよう」と考え、眠れなかったことがあります。
これに対処する最も簡単な方法は、アラームが予期しないものをキャッチした場合、または手動でトリガーしてトラフィックを古いシステムに戻した場合に、新しいシステムへのオフ スイッチを用意することです。ただし、これは言うほど簡単ではありません。場合によっては、古いシステムに移行する方法がないかもしれませんが、このレバーがあれば、チームの負担が大幅に軽減されます。
これが不可能な場合は、ステップ 5 (古いシステムと新しいシステムを並行して実行する) を徹底的に実行することだけが頼りになります。その後、新しいシステムをゆっくりと段階的に展開します。トラフィックの小さな割合 (1% の後に 5%、10%、25%、50%、100%) を新しいサービスで処理するように移行したり、移行中に緊密に連携している少数の顧客を新しいサービスで処理するように厳選したりするなどの手法を使用して、ゆっくりと段階的に展開を定義できます。
また、問題が発生した場合にオペレーターが従うインシデント対応ランブックを幅広く確認することも重要です。すべてが失敗した場合、見逃されたエッジケースには手動介入が役立ちますが、影響を受ける顧客の数が数千に増えると、すぐに管理不能になる可能性があります。これが、ポイント 5 と 6 で説明したフェーズに十分な時間をかける理由です。
移行に取り組むことがこれらのスキルを磨く唯一の方法ではありませんが、たとえまったく新しい取り組みであっても、将来のプロジェクトに適用できる学習を間違いなくスピードアップできます。移行プロジェクトはそれほど華やかではありませんが、特に設計文書やその他の技術文書にフィードバックを提供するときに、私にとって実戦テストの対象となるプロジェクトでした。
したがって、もしあなたが何かに取り組む機会があれば、ぜひ試してみてください。失望することはなく、生涯にわたって役立つ学びが得られ、それを他の人に伝えて、回復力のあるシステムを構築できるようになるでしょう。