TL;DR
- 仕様書ゼロのコードを引き継いだとき、AIに読ませて仕様を逆算する手法(仕様考古学)
- Claude Codeの
/sfad reverseで既存コードからExample Map形式の仕様書を自動生成 - 引き継ぎ初日の「最初の4時間」でやるべきことをステップバイステップで解説
- Dead Code・暗黙のルール・セキュリティ懸念を自動検出
- テスト生成で「現状を正として固定」→ 安全にリファクタリングできる状態を作る
引き継ぎ資料は、READMEに書かれた3行だけだった。
環境構築: docker-compose up
デプロイ: ./deploy.sh
何かあったら田中さんに聞いて
田中さんは先月退職した。
Slackで「認証周りの仕様書ありますか?」と送った。既読がついた。返信が来なかった。もう一度聞いた。「前の担当が全部把握してたんですが、先月辞めまして……」。
そしてコードを開いた。
if failed_count >= 5:
lock_account(user_id)
なぜ5回なのか。コミットログは「認証機能実装」。PR説明は空。担当者は退職済み。仕様書もドキュメントも存在しない。あるのはコードだけ。
引き継ぎ初日、あなたが最初に見たのがこのコードだったとしたら、何をしますか。
「なぜこの値なのか」を誰にも聞けない
引き継ぎで最も怖いのは、バグではありません。バグなら直せます。怖いのは「これはバグなのか仕様なのか、判断できない」状態です。
failed_count >= 5 でロックする。なぜ5回か? 3回にしたら怒られるのか。10回に変えたらセキュリティ上問題があるのか。コードを読んでも、答えが出てきません。
言語を変えても、同じ問題は起きます。
# Python
MAX_LOGIN_ATTEMPTS = 5 # なぜ5か? コミットログ: 「認証機能実装」
if failed_count >= MAX_LOGIN_ATTEMPTS:
lock_account(user_id)
// TypeScript
const TOKEN_EXPIRY_SECONDS = 300; // なぜ300秒か? PR説明: なし
const PRE_AUTH_TIMEOUT = 1800; // なぜ1800か? 担当者: 退職済み
// Go
const (
MaxRetries = 3 // なぜ3か? git blame: 5年前、当人不在
CacheExpiry = 300 // なぜ300か? テスト: カバーなし
)
マジックナンバーは数字だけの問題ではありません。「変えてよいのかどうかわからない」という状態が問題です。変えたら何かが壊れるかもしれない。でも変えなかったら、要件が変わったときに対応できないかもしれない。判断する材料がない。
引き継ぎが地獄になる構造
なぜ引き継ぎはこれほど消耗するのか。問題の構造を整理してみます。
コードには3種類の情報があります。
| 情報源 | 教えてくれること | 教えてくれないこと |
|---|---|---|
| コード | 何をしているか(事実) | 何をすべきか(意図) |
| ユーザー | 使い方・期待値 | 実装の根拠 |
| 仕様書 | 合意された振る舞い | — |
引き継ぎ地獄は、この3つのうち「仕様書」がない状態で始まります。
コードは「failed_count >= 5 のときにロックする」という事実を教えてくれます。でも「5回でロックするのが正しい要件か」は教えてくれません。ユーザーに聞いても「なんとなくそうなってる」という答えが返ってくるか、そもそも知っている人がいません。
結果として、引き継いだエンジニアは以下の問いに答えられないまま開発することになります。
- この挙動はバグか仕様か
- この値を変えると何が壊れるか
- 新メンバーに「正しい動作」を説明できるか
- 変更の影響範囲はどこまでか
これは技術力の問題ではありません。情報の欠如の問題です。
仕様考古学という選択肢
「仕様書がない」状態への対処として、仕様考古学というアプローチがあります。
考古学のメタファーです。遺跡(コード)を発掘して、そこに暮らしていた文明(仕様)の記録を復元する作業。完璧な答えは得られません。でも「何がわかっていて、何がわかっていないのか」は明確にできます。
SFADの /sfad reverse コマンドは、この仕様考古学を7つのPhaseで体系的に実行します。
Phase 1: 対象コードの特定 → import/依存関係を辿って境界を定める
Phase 2: 振る舞い抽出 → 事実(what)と意図(why)を分離する
Phase 3: Example Map生成 → BDD構造で「決まっていること」を整理する
Phase 4: 不明点・矛盾の検出 → 6カテゴリで問題を分類する
Phase 5: ユーザーバリデーション → 人間が承認・修正する
Phase 6: 仕様ファイル生成 → 承認結果を文書化する
Phase 7: テスト生成 → 現状の振る舞いを固定する
各Phaseの詳細については、「仕様書がないコードをAIに読ませたら9ルール・11問題が出てきた」で書いています。ここでは引き継ぎの文脈で重要な部分に絞って見ていきます。
reverseが出力するもの
冒頭の認証コードをreverseで分析すると、何が出てくるか。実際の結果の概要です。
9つのRuleと確信度
コードから抽出した振る舞いルールに、確信度を付与します。
| Rule | 内容 | 確信度 |
|---|---|---|
| ログイン | email + password で認証、JWTトークン発行 | 高 |
| アカウントロック | 5回失敗でロック | 高 |
| OTP | 6桁、5分有効 | 中 |
| パスワードポリシー | 10文字以上、3カテゴリ以上 | 高 |
| ユーザーキャッシュ | 5秒TTL | 高 |
確信度は「コードとテストの両方で確認できたか」で決まります。
- 高: コードと対応するテストが両方存在する
- 中: コードにあるがテストが不十分
- 低: 意図不明、またはテストと矛盾
高確信度のRuleは、そのまま仕様として使えます。低確信度のRuleこそ、関係者に確認すべき箇所です。
4つのQuestion
AIが「わからない」と言った箇所。これが引き継ぎで確認すべき事項の一覧になります。
Q1: OTPは常時有効だが、これは一時的な実装か?
Q2: パスワードリセットにレート制限はあるのか?
Q3: pre_auth_tokenの有効期限は何秒か?
Q4: サインアップに確認メール送信フローはあるのか?
Questionの数は、仕様の曖昧さの指標です。Questionが多いほど、引き継ぎ前の仕様整理が必要です。
11の問題(6カテゴリ)
reverseは6カテゴリで問題を検出します。
| カテゴリ | 意味 | 例 |
|---|---|---|
| SECURITY | セキュリティ上の問題 | レート制限なし、トークン露出 |
| DEAD CODE | 使われていないコード | 重複関数、no-opエンドポイント |
| INCONSISTENT | テストとコードの矛盾 | 引数の不一致 |
| MISSING | 存在すべき機能の欠落 | ブルートフォース対策なし |
| IMPLICIT | 暗黙の前提・マジックナンバー | ロック回数5回の根拠不明 |
| UNDOCUMENTED | 文書化されていないロジック | 計算式にコメントなし |
認証コードの分析では、Criticalが3件(パスワードリセットのレート制限なし、セキュリティ上危険な実装、本番ログへのトークン露出)、Highが1件(OTPブルートフォース対策なし)が出てきました。
引き継いだコードにCriticalが3件含まれていた。でも引き継ぎ前は誰も気づかなかった、という話は珍しくありません。
引き継ぎ初日チェックリスト
reverseを実行する前に、まず目視で確認できる兆候があります。これが多いほど、コードベースの「引き継ぎリスク」が高い。
5つの危険信号
1. エラーの握りつぶし
# Python: 何が起きても空を返す
def parse_config(data: str) -> dict:
try:
return json.loads(data)
except: # bare except: 全例外を握りつぶす
return {}
// TypeScript: エラーが虚空に消える
async function fetchUser(id: string) {
try {
return await api.get(`/users/${id}`);
} catch (e) {} // 何もしない
}
// Go: エラーを明示的に捨てる
data, _ := os.ReadFile(path) // _ = err: エラーを無視
エラーを握りつぶすコードがある場合、本番で何が起きているか誰にもわからない状態が続いています。
2. マジックナンバーの集中
根拠のない数値定数が複数ある。定数として切り出されていても、コメントがなければ同じ問題です。
3. テストカバレッジが低い(または存在しない)
テストがなければ、変更したときに何が壊れるかわかりません。引き継ぎコードで最も恐ろしいのは「変えられない」コードです。
4. コミットメッセージが「修正」「対応」「fix」のみ
変更の理由が記録されていない。なぜその変更をしたのかが、永遠にわからなくなっています。
5. 型が曖昧(Any、interface{}、anyの多用)
def process(data: Any) -> Any: # 何が来て何を返すかわからない
...
型が曖昧なコードは「どんな値が来るか」の前提が仕様になっていない証拠です。
最初の1週間でやること
引き継ぎ初日から1週間、以下の順序で動くと混乱が少なくなります。
Day 1: スコープの把握
- コードベース全体の構造を把握する(ディレクトリ、主要ファイル)
- 上記5つの危険信号を目視でチェックする
- テストカバレッジを計測する
Day 2-3: reverseで仕様を抽出する
- 最も重要な機能(認証、決済、コアロジック)を対象にreverseを実行する
- SECURITYカテゴリの問題を優先的に確認する
- Questionリストを作成する(これが引き継ぎ元への確認事項になる)
Day 4-5: 確認と文書化
- Questionリストを元に、残っている関係者に確認する
- 確認できた内容を仕様ファイルとして保存する
- 確認できなかった内容を「未確認の仕様」として記録する
Day 6-7: 安全網を作る
- カバレッジの低い箇所に「現状の振る舞いを記録するテスト」を追加する
- このテストは「正しさの証明」ではなく「変更時の検知」が目的
「仕様を復元する」と何が変わるか
reverseを実行した後、何が変わるか。
「バグか仕様かわからない」問いへの答えが変わります。
仕様ファイルがない状態では、failed_count >= 5 の「5」が正しいかどうか判断できません。reverseを実行して確信度付きのRuleを作り、人間が「OWASPの推奨に基づいて5回に設定した」と確認したとき、初めて「5回は仕様である」と言えます。
新メンバーへの説明も変わります。「なんとなくこう動いています」ではなく、「このルールに従って動いています、Questionの項目はまだ未確認です」という説明ができます。
変更時の判断も変わります。「何が壊れるかわからないから触れない」ではなく、「このRuleに影響する変更なので、このテストが壊れたら要確認」という判断ができます。
深掘りルート
この記事では入口として全体像を示しました。各テーマの詳細は以下の記事で扱っています。
仕様考古学の7 Phase完全版
→ 「仕様書がないコードをAIに読ませたら9ルール・11問題が出てきた」(Art.6)
reverseの7 Phaseを、認証コードの実例で一つひとつ解説しています。確信度スコアリングの基準、Example Mapの生成方法、6カテゴリの分類の詳細を知りたい場合はこちら。
引き継ぎコードで最初に見つかる問題
→ 「エラーを『握りつぶす』コードの見抜き方」(Art.5)
Python / TypeScript / Go / Rustの4言語で、エラーの握りつぶしパターンを横断的に解説しています。引き継ぎコードで最初に確認すべき5つの兆候の詳細と、lintで機械的に検出する方法。
reverse設計思想の理論
→ 「なぜ逆算するのか ― reverse設計思想と7つの原則」(Art.8・有料)
なぜ仕様をコードから逆算するアプローチが有効なのか。AIにコードを読ませるときの限界と、人間が判断すべきポイントの理論的背景。
引き継ぎ後のレビュー手順をチームに定着させたい方は
→ 「Claude Code Skillの作り方」(Art.9)
仕様考古学の手順やレビューフローをSFAD Skillとして定義すれば、次の引き継ぎ担当者が同じ道を踏まずに済む。Skillの書き方と実用パターンを解説。
あなただけではありません。仕様書ゼロ・ドキュメントゼロの引き継ぎは、どの言語でも、どの規模のプロジェクトでも起きます。
コードは「何をしているか」を教えてくれます。でも「何をすべきか」は教えてくれない。その溝を埋めるのが仕様考古学です。
完璧な仕様書は得られません。でも「何がわかっていて、何がわかっていないのか」は明確にできます。それだけで、引き継ぎの地獄の深さは変わります。
この記事はSFAD(Spec-First AI Development)シリーズの一部です。シリーズ全体の一覧はこちら。
SFADシリーズ 読み方ガイド
| あなたの状況 | おすすめ記事 |
|---|---|
| AIと開発してバグが多い | Art.1 → Art.2 → Art.5 |
| 引き継いだコードに仕様書がない | Art.6 → Art.5 → Art.8 |
| チームに品質基盤を作りたい | Art.1 → Art.7 → Art.4 |
| Claude Code Skillを作りたい | Art.8 → Art.9 |
| シリーズの全体像を知りたい | Art.10 |