開発を一時停止することはできない。それはまず受け入れなければならない事実だ。チケットは次々と届き、顧客は出荷を待ち、既存のコードは、ドキュメント化を決めたからといって止まってはくれない。開発を1ヶ月間凍結して、初日から存在しているべき仕様書を書かせるなんて、エンジニアリングマネージャーが許可するはずがない。OpenSpecは、グリーンフィールド(新規開発)の空想ではなく、現実のために構築された。すでに存在する仕組みや顧客がいる環境に、そのまま組み込むことで最大の効果を発揮する。
ここでの目的は書き換えではない。「誠実な考古学」だ。本番環境で実際に動いているものを掘り起こし、正確に記述し、コードの進化に合わせてその記述も進化させていくのだ。仕様がシステムと一致していれば、来期に加わるエンジニアにとっても、IDEに搭載されているAIツールにとっても、作業が容易になる。以下に、リリースを一つも逃すことなくこれを行う方法を示す。
実際に何を行っているかから始める
リポジトリを開くと、controllers、models、services、utilsといった名前のフォルダが見えるだろう。これらは技術的なレイヤーであり、あなたに嘘をついている。これらは、システムがビジネスに対して何を行うかを記述しているわけではない。JavaScriptファイルが詰まったフォルダを見ても、注文がどのように出荷に変わるのかは分からない。OpenSpecを後付け(レトロフィット)するには、「機能(capabilities)」の観点で考える必要がある。
スタック全体を別の言語で書き換えたとしても生き残るような、安定したビジネスオペレーションを探してほしい。ほとんどのプロダクト企業において、これらは繰り返し登場する。注文、請求、在庫、顧客、通知などだ。これらのコア機能から5〜8個を挙げてみてほしい。
それぞれの機能について、次の5つの具体的な質問に必ず答えるようにしてほしい。その機能は、現実世界のどのような問題を解決しているか? コードは実際にどこにあるか?(1つのサービスか、3つのマイクロサービスか、あるいは誰も触りたくないレガシーモジュールか?) 何がトリガーになるか?(ユーザーのクリック、スケジュールされたcronジョブ、インバウンドのwebhookか?) どのようなデータが入力され、どのようなデータが出力されるか? そして最後に、どの他のシステムがそれに依存しているか?(つまり、その部分が停止した場合、何が壊れるか?)
容赦なく正直になってほしい。もし「顧客」機能がRailsのモノリス、Node API、そして外部CRMにまたがって散らばっているなら、その通りに書き留めるのだ。地図は設計者の夢ではなく、実際の地形(テリトリー)と一致していなければならない。
願望リストではなく、真実を書く
ドキュメント作成において最も危険な一言は、「せっかく書くなら、ついでに直しておこう」だ。やめておけ。あなたはチェックアウトフローを再設計しているのではない。今まさに実際のクレジットカードで決済を行っているチェックアウトフローを記述しているのだ。
もし注文が即時の決済確定をトリガーし、その後バックグラウンドワーカーを通じてメールを送信する仕組みなら、その正確なシーケンスを記述せよ。来期に追加する予定のイベントキューを勝手に入れてはいけない。バリデーションが実際にはサービスクラスの深い場所に存在するのに、APIのエッジで行われているかのように装ってはいけない。志(あこがれ)よりも、正確さの方がはるかに重要だ。
不正確なドキュメントは、ドキュメントがないよりも質が悪い。新人が存在しない挙動を期待するように仕向けてしまうからだ。また、AIコーディングアシスタントを、願望に基づいた空想の道へと導いてしまう。仕様が本番環境と一致していれば、信頼できるベースラインが構築される。「意図された」フローを推測する必要がなくなるため、デバッグが速くなる。開始地点が現実のものであると分かっているため、リファクタリングがより安全になる。
APIからコントラクトを抽出する
APIエンドポイントはすでにルールを強制している。ただ、それらが暗黙的であるだけなのだ。OpenSpecを後付けするということは、それらのルールを明文化することである。
入力とバリデーションから始めよう。そのエンドポイントは実際に何を受け付けるのか? 型、必須フィールド、最大長、およびフィールド間の依存関係を記述する。次に、ビジネスロジックの挙動を記述する。その呼び出しはレコードを作成するのか、副作用を発生させるのか、それとも単に別のサービスに対して状態を検証するだけなのか? 具体的に書くこと。
最後に、レスポンスをカタログ化する。成功時には何が返されるのか? 正確なエラーコードは何で、どのような条件下で発生するのか? 「エラーを返す」と書くのではなく、「請求先住所が不足している場合は422を返し、他のプロセスによって在庫が既に確保されている場合は409を返す」と書くのだ。そのレベルの精度があれば、曖昧なルートが、フロントエンドチーム、QAエンジニア、および自動化ツールが信頼できる「コントラクト(契約)」へと変わる。
隠れたルールを追い詰める
システム内の極めて価値の高い知識のいくつかは、隙間に埋もれています。それはサービス・クラス内の条件分岐ブロックの中に埋め込まれていたり、データベース・トリガーの中に隠されていたり、あるいは2年以上誰も触れていないストアド・プロシージャの中に書き込まれていたりします。これらはあなたのビジネスルールであり、通常はシステム障害が発生した際や、創業当初から在籍している唯一のエンジニアを問い詰めた時に、改めて発見されることになります。
それらを可視化しましょう。まずは、すでに分かっているルールから始めます。「一定額以上の注文には、処理を進める前にマネージャーの承認が必要である」「無効なユーザーアカウントは新しい注文を作成できない」「返金は決済が完了する前のみ許可される」といった具合です。それぞれのルールを、それが制御する機能のすぐ隣に、プロダクトマネージャーが翻訳なしで読めるほど明確な言葉で書き留めてください。
これらのルールを集約することで、単なるドキュメント化以上の効果が得られます。重複が浮き彫りになり、矛盾が明らかになります。そして、誰かが存在を忘れていた制約をうっかり破ってしまうような「たった1行の変更」をコミットする前に、チーム全体がポリシーについて議論できる単一の場所を提供できるのです。
システムの仕組み(プランミング)をマッピングする
現代のシステムはイベントによって動いています。あるサービスでのアクションは、ユーザーに目に見える形で届く前に、他の半ダースものサービスへと波及していきます。その波及経路を記録する必要があります。コア・ワークフローにおける、あるイベントから次のイベントへの流れをマッピングしてください。「注文作成」が「在庫確保」につながり、それが「支払い確認」を待つ、といった具合です。たとえ一部のリンクが脆弱に感じられたり、異なるプロトコルを使用していたりしても、チェーン全体を描き出してください。
内部トラフィックだけで終わらせてはいけません。外部サービスは、それをシステムの一部とみなすかどうかにかかわらず、あなたのシステムの一部です。各インテグレーションについて、その目的、アプリケーションがどのように認証を行うか、そしてどのように失敗するかを記録してください。決済ゲートウェイは30秒後にタイムアウトして汎用的な500エラーを返しますか?配送APIは週末に不正な形式のJSONを返しますか?アイデンティティ・プロバイダーは、自身のドキュメントの記載よりも早くリフレッシュトークンを失効させますか?こうした詳細は些細なことに見えるかもしれません。
