メインコンテンツへスキップ
見出し画像

HANDOFFドキュメントを使い分ける:4 タイプの実例集とアンチパターン


    この記事で得られること

    • HANDOFF は「1 種類」ではなく、プロジェクトの状態によって 4 つに使い分けるべきと分かる

    • 4 つのタイプ(プロジェクト全体 / 機能単位 / 新機能追加 / バックエンド)の HANDOFF.md 実物を見ながら、何をどう書くか具体的にわかる

    • HANDOFF を成長させる運用と、よくあるアンチパターンが学べる


    HANDOFF 入門編の最小テンプレートでは足りない時が来る

    前回の記事「HANDOFFドキュメントとは何か」で、HANDOFF を書き始めるための最小テンプレート(6 要素)を紹介しました。

    正直なところ、新規プロジェクトの初期はあれで十分です。ですが、プロジェクトが進行するにつれて、こんな場面に出くわします。

    • 「機能 A の追加だけを Claude Code に頼みたいのに、プロジェクト全体の HANDOFF を渡すと文脈が広すぎる」

    • 「複数プラットフォーム(Desktop / iOS / Android)に同じ機能を実装したいが、共通仕様と差異の整理が混乱する」

    • 「バックエンドサービスは技術選定理由・DB スキーマ・運用コストまで含めて記録したいが、最小テンプレートだと粒度が粗い」

    これらは「HANDOFF を 1 種類で済ませようとした」ことが原因です。プロジェクトの状態と粒度によって、HANDOFF の型を使い分けるべきです。

    本記事では、私が運用している 4 つのタイプを、実プロジェクトの HANDOFF.md(すべて MIT 公開リポジトリ)を見ながら解説します。


    HANDOFF は 1 タイプじゃない:4 つの使い分けパターン

    私は HANDOFF を 4 つに分類しています。

    • ① プロジェクト全体型 ── 新規プロジェクトを 0 から立ち上げる

    • ② 機能単位型 ── 特定機能の仕様を集約する(複数プラットフォーム共通)

    • ③ 新機能追加型 ── 既存プロジェクトに機能を追加する

    • ④ バックエンド型 ── サーバーサイドサービスの設計を集約する

    それぞれ「書くべきこと」「強調すべきこと」「読み手(Claude Code)への伝え方」が違います。

    4 タイプの選び方

    判断フローはシンプルです。

    新規プロジェクトを立ち上げる?
      ├─ Yes → ① プロジェクト全体型
      └─ No  → 既存プロジェクト
                ├─ 機能を追加する?
                │   ├─ Yes(単一プラットフォーム) → ③ 新機能追加型
                │   └─ Yes(複数プラットフォーム共通) → ② 機能単位型
                └─ サーバーサイドサービスを作る?
                    └─ Yes → ④ バックエンド型

    迷ったら、HANDOFF を 1 ファイルに詰め込まずに分ける ことから始めるのが安全です。ファイル名で `HANDOFF_<対象>.md` のように対象を明示すると、Claude Code 側でも参照すべき HANDOFF が明確になります。

    実際、kazahana リポジトリには `HANDOFF_kazahana-bsky-image-spec.md`(仕様書系)と `design/HANDOFF_watermark.md`(機能単位)が共存しています。


    ここから先は 4 タイプそれぞれの実例詳細です。

    各タイプの HANDOFF.md 抜粋・設計判断・特に効く工夫・運用のコツ・アンチパターンを順に公開します。


    ① プロジェクト全体型:caelum HANDOFF

    新規プロジェクトを 0 から立ち上げるとき。Claude Code に「このプロジェクトを実装してください」と頼む状況。

    HANDOFF 構造(caelum 実例)

    caelum(Liber Caeli)は西洋占星術アプリです。私が組んだ HANDOFF はこの並びです。

    # HANDOFF: caelum(Liber Caeli)
    
    **作成日**: 2026-03-09
    **最終更新**: 2026-03-12
    **ステータス**: Phase 4 全完了 + v1.0.4 バグ修正
    **引き継ぎ先**: Claude Code
    
    ## プロジェクト概要
    | 項目 | 内容 |
    |---|---|
    | リポジトリ名 | caelum |
    | アプリ表示名 | Liber Caeli(リベル・カエリ)|
    | 用途 | 個人利用デスクトップ占星術アプリ |
    
    ## ゴール(Phase 1 MVP)
    1. 名前・生年月日時・出生地を入力フォームに入力する
    2. 「チャートを作成」ボタンでネイタルチャート円盤を SVG 表示する
    3. 「解釈を生成」ボタンで Claude API が日本語解釈をストリーミング表示する
    
    ## 技術スタック(確定)
    ### フロントエンド
    - Tauri v2 / React 18 + TypeScript 5 / Tailwind CSS 3 / D3.js v7
    
    ### バックエンド(Pythonサイドカー)
    - Python 3.10+ / FastAPI + uvicorn / kerykeion / anthropic SDK / PyInstaller
    
    ## ディレクトリ構造(作成すること)
    caelum/
    ├── src/
    │   ├── components/
    │   ├── hooks/
    │   └── lib/api.ts
    ├── sidecar/
    │   ├── main.py
    │   ├── routers/
    │   └── services/
    ├── src-tauri/
    └── ...
    
    ## 実装手順(Phase 1)
    ### Step 1: プロジェクトスキャフォールド
    ### Step 2: Pythonサイドカー環境構築
    ### Step 3: サイドカー骨格実装
    ...

    設計判断のポイント

    1. 「ゴール」を最初に置く

    技術スタックやディレクトリ構造より先に「動作する状態」を 3 行で書きます。Claude Code はゴールから逆算して実装を組み立てるので、ここが曖昧だと全体がブレます。

    「○○ができる」「○○ボタンを押すと○○が起きる」という人間の操作とアプリの応答で表現するのがコツです。

    2. 技術スタックは「確定」と「未決定」を分ける

    新規プロジェクトでは、最初から全てが決まっているわけではありません。caelum の場合、占星術設定の細部(黄道はトロピカル / ハウスはユーザー選択)は先に決めましたが、UI 詳細は実装中に決定しました。

    「確定」項目にはバージョン番号まで明記します(`React 18`、`Python 3.10+`)。「未決定」項目には `TBD` を書き、後で埋めます。これがないと Claude Code が独自に決めて、後で巻き戻しが発生します。

    3. ディレクトリ構造を先に書く

    caelum の HANDOFF は、まだ存在しないファイル群を含めたディレクトリ構造を最初に定義しています。これを書いておくと、Claude Code が新規ファイルを作る場所で迷いません。

    特に効く工夫

    フェーズ分割を明示する。 caelum は Phase 1(MVP)→ Phase 2(トランジット)→ Phase 3(シナストリー)→ Phase 4(月間カレンダー)の順で進めました。

    HANDOFF にこの並びを書いておくと、Phase 1 が終わったあと「次は Phase 2 に進んでください」だけで Claude Code が次のスコープを理解します。

    冒頭の `ステータス: Phase 4 全完了 + v1.0.4 バグ修正` の状態行は、HANDOFF を読む前に 「今どこまで進んでいるか」を 1 行で把握させる ためのものです。

    よくあるアンチパターン

    • ゴールを技術用語で書く —「Tauri v2 で SSE ストリーミングを実装する」では Claude Code が動作する完成像を描けません

    • ディレクトリ構造を後回しにする — 後から書こうとして書かないまま終わるパターンが多い

    • `TBD` を埋め続けない — 実装中に決まった事項を HANDOFF に反映しないと、後続セッションで Claude Code が古い `TBD` を独自に解釈し始めます


    ② 機能単位型:kazahana watermark HANDOFF

    特定機能の仕様を、複数プラットフォーム(Desktop / iOS / Android)にまたがって整理したいとき。

    HANDOFF 構造(kazahana watermark 実例)

    # HANDOFF: ウォーターマーク機能 — Kazahana 全プラットフォーム対応
    
    ## 概要
    画像投稿時にアカウント名・著作権表示・AI学習拒否文言を画像に合成する機能。
    
    ## 共通仕様
    
    ### 対応メディア
    | 種別 | 対応 | 備考 |
    |------|------|------|
    | JPEG | ✅ | 品質 0.92 で再エンコード |
    | PNG  | ✅ | JPEG 0.92 に変換 |
    | WebP | ✅ | JPEG 0.92 に変換 |
    | GIF  | ❌ | 対象外(アニメーション維持が困難) |
    
    ### 文言プリセット(全プラットフォーム共通)
    | ID | 表示文言 | タグ |
    |----|----------|------|
    | copyright | © @{handle} 無断転載禁止 | 標準 |
    | ai_ja | © @{handle} AI学習・転載禁止 | AI拒否 |
    | ai_en | © @{handle} No AI Training | EN |
    ...
    
    ### 設定スキーマ(JSON 表現・プラットフォーム間で共通)
    {
      "enabled": false,
      "preset": "copyright",
      "customText": "",
      "position": "br",
      "opacity": 70,
      ...
    }
    
    ## プラットフォーム別の差異
    
    ### Desktop(Tauri v2)
    - Canvas 2D で合成
    - compressImageFile() で 1MB 制限に収める
    
    ### iOS(Swift / SwiftUI)
    - Core Graphics で合成
    - UIImage.jpegData(compressionQuality: 0.92)
    
    ### Android(Kotlin / Compose)
    - Bitmap.compress(JPEG, 92)
    - Canvas で描画

    設計判断のポイント

    1. 「共通仕様」を 1 か所に集約する

    3 つのプラットフォーム別に HANDOFF を作ると、文言プリセット・設定スキーマ・配置モードが各ファイルで微妙にズレるリスクがあります。

    watermark の場合、「共通する仕様」を `## 共通仕様` セクションに集約しました。各プラットフォーム実装は「これに従って書いてください」と参照させる構造です。

    これで仕様のズレを防げます。

    2. JSON で設定スキーマを書く

    「ウォーターマークの設定項目」を文章で書くと曖昧になります。JSON シリアライズ可能な構造で先に固定すると、各プラットフォームの実装が同じ Save/Load 互換になります。

    3. 「対応 / 非対応」の表を先頭近くに置く

    JPEG / PNG / WebP / GIF / 動画それぞれに対応するか、対応するなら何を変換するか、を表形式で書きます。これで Claude Code が「動画も対応すべきか?」と迷う時間がなくなります。

    特に効く工夫

    プリセット ID を文字列で定義する。 UI 側では「標準」「AI拒否」と表示するけれど、内部 ID は `copyright`、`ai_ja` のように英数字で固定します。これでプラットフォーム間の参照も翻訳も両立します。

    watermark の HANDOFF では「`{handle}` は実アカウント名に置換」と注釈を入れています。これでプレースホルダ展開ロジックも各プラットフォームで同じになります。

    よくあるアンチパターン

    • プラットフォームごとに別 HANDOFF を作る — 仕様のズレが起きやすい。共通仕様を 1 ファイルに集めて、差異だけを別セクションに分けるのが正解

    • 設定項目を文章で書く — 「不透明度は 0〜100 の整数」と書くより、JSON で `"opacity": 70` と例示するほうが伝わりやすい

    • 対応/非対応の境界を曖昧にする — 「対応予定」「Phase 2 で検討」が乱立すると、Claude Code が判断を迷う


    ③ 新機能追加型:michi-navi HANDOFF

    既存プロジェクトに新機能を追加するとき。プロジェクト全体の HANDOFF とは別に、追加機能だけの HANDOFF を作る。

    HANDOFF 構造(michi-navi 実例)

    # HANDOFF — Michi-Navi 新機能実装
    
    > 作成日: 2026-04-04
    > 対象機能: ①市町村の花表示 ②カントリーサインスタンプラリー ③道の駅フォトアルバム
    > プラットフォーム: iOS (iPhone / iPad のみ、CarPlay対応不要)
    
    ## 0. 準備済みデータファイル一覧
    
    | ファイル | 説明 | 件数 |
    |---------|------|------|
    | municipalities/hokkaido_municipalities.json | 北海道全179市区町村マスタ | 179件 |
    | municipalities/hokkaido_country_signs.json | カントリーサインマスタ | 179件 |
    | schema/schema_definition.json | 全データのJSONスキーマ定義 | — |
    
    ### 🔴 Claude Codeで着手前に必要な手動作業
    
    1. 花の画像準備(6件が未制定)
       未設定: 01333 知内町 / 01393 黒松内町 / ...
    2. GeoJSON 取得
       国土数値情報DLサービスから北海道(01)のN03データをDL
    3. カントリーサイン画像収集
    4. 観光サイト名補完
    
    ## 1. 機能①:市町村の花 表示
    
    ### 配置場所
    道の駅詳細画面(StationDetailView)のフォトアルバムセクション直下
    
    ### UI 仕様
    (ASCII アート で UI を図示)
    
    ### データ取得ロジック
    (Swift コード)

    設計判断のポイント

    1. 「Claude Code 着手前の手動作業」を最初に明示する

    新機能追加では、Claude Code が触れない作業(画像素材の準備、外部データの DL、API キーの取得など)が必ずあります。これを HANDOFF の最初の方 に置きます。

    michi-navi の HANDOFF は `## 0. 準備済みデータファイル一覧` の中に 🔴 Claude Codeで着手前に必要な手動作業 セクションを置きました。

    Claude Code はここを読んで「この作業が完了していないと先に進めない」と判断します。

    2. 既存プロジェクトの構造を尊重する書き方

    新機能追加では「どこに何を配置するか」が重要です。

    michi-navi の HANDOFF は、各機能の `### 配置場所` で「`StationDetailView` のフォトアルバムセクション直下」と既存コンポーネント名を引用して指定しています。

    これがないと、Claude Code は新規ビューを勝手に作って既存の構造を壊します。

    3. UI を ASCII アートで図示する

    文章で UI を書くと「○○の下に○○を表示」のような曖昧な表現になりがちです。

    michi-navi の HANDOFF では `[花の写真 64×64pt] 町の花` のような ASCII で配置を示しています。これで Claude Code が SwiftUI のレイアウト構造を組み立てやすくなります。

    特に効く工夫

    未制定リストを明示する。 上記の「6 件が未制定」のような不完全状態を HANDOFF に書く ことで、Claude Code は「この件は触らない」と判断できます。書かないと、Claude Code は親切心で勝手にデフォルト値を入れてきます。

    よくあるアンチパターン

    • 「機能を追加してください」だけで済ませる — Claude Code は既存構造を読み取れません。配置場所を明示する

    • 着手前作業を後回しにする — 「画像はあとで入れます」を書くと、Claude Code が空のアセット参照を作ってビルドが落ちる

    • 既存コンポーネント名を書かない — 「適切な場所に追加してください」では Claude Code が新規ビューを作ってしまう


    ④ バックエンド型:kazahana-push-backend HANDOFF

    サーバーサイドサービスを作るとき。デスクトップアプリやモバイルアプリと違って、運用コスト・スケール特性・データ永続化が主役になります。

    HANDOFF 構造(kazahana-push-backend 実例)

    # HANDOFF: kazahana-push-backend
    
    ## 概要
    kazahana のプッシュ通知バックエンドサービス。
    Bluesky Jetstream を購読し、kazahana ユーザー全員に対して
    フォロー・いいね・リポストの通知を APNs / FCM 経由で配信。
    
    - 運用コストは月額 $10 程度、全ユーザー無償提供
    - 対象は iOS と Android のみ。デスクトップは Tauri ポーリング通知で対応済み
    
    ## プラットフォーム方針
    | プラットフォーム | 通知方式 | バックエンド |
    |---|---|---|
    | iOS     | APNs | 本サービス |
    | Android | FCM  | 本サービス |
    | macOS   | ポーリング | 不要 |
    
    ## 技術スタック
    | 要素 | 採用技術 | 理由 |
    |---|---|---|
    | ランタイム  | Bun | TypeScript-native、高速、SQLite 内蔵 |
    | Webフレーム | Hono | 軽量・型安全・Bun 対応 |
    | DB         | SQLite (bun:sqlite) | 追加コストゼロ、このスケールで十分 |
    | プッシュ iOS | APNs HTTP/2 (JWT) | node-apn ライブラリ |
    | プッシュ Android | FCM HTTP v1 | firebase-admin SDK |
    | ホスティング | Fly.io (nrt) | 月 $5〜10、既存インフラと統一 |
    
    ## データベース設計
    CREATE TABLE device_tokens (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        did TEXT NOT NULL,
        token TEXT NOT NULL,
        platform TEXT CHECK(platform IN ('ios', 'android')),
        UNIQUE(did, platform)
    );
    
    ## API 設計
    ### 認証
    全リクエストに Authorization: Bearer {API_SECRET}
    
    ### POST /devices
    デバイストークン登録

    設計判断のポイント

    1. 技術スタックに「採用理由」を併記する

    バックエンドは選択肢が広い領域です。「なぜ Bun を選んだか」「なぜ SQLite で十分か」を書かないと、Claude Code が勝手に PostgreSQL に置き換えたり、Express に変えたりします。

    `理由` カラムに 1 行で書くだけで十分です。「TypeScript-native」「このスケールで十分」のような短い表現で OK。

    2. 運用コストを HANDOFF に書く

    `月額 $10 程度、全ユーザー無償提供` のような運用前提が書いてあると効きます。

    Claude Code が「キャッシュを Redis に追加しましょう」と提案したとき、「Fly.io の Redis アドオンは月 $X 追加です」と相対化できます。

    コスト感が共有されていない HANDOFF だと、Claude Code は無遠慮にスケールアップ提案をしてきます。

    3. データベーススキーマを SQL で書く

    文章で「device_tokens テーブルに did, token, platform を持つ」と書くと、CHECK 制約や UNIQUE 制約が抜けます。SQL で先に書いておけば、Claude Code は migrations.ts に貼り付けるだけで済みます。

    特に効く工夫

    「対象外」を明示する。 kazahana-push-backend は明確に「macOS / Windows は対象外(Tauri ポーリングで対応済み)」と書いています。これがないと、Claude Code は親切心で全プラットフォーム対応を提案してきます。

    「やらないこと」を書くのは、HANDOFF 全タイプ共通で効きます。

    よくあるアンチパターン

    • 技術選定理由を書かない — 「Bun を使う」だけだと、Claude Code が Node.js に置き換える提案をしてくる

    • コスト感を共有しない — Redis / Postgres / Lambda のような高コスト選択肢が無遠慮に提案される

    • テーブル定義を文章で書く — SQL で書くだけで実装の手戻りが大幅に減る


    HANDOFF を「成長させる」運用:3 つのコツ

    4 タイプの HANDOFF はどれも、書いて終わりではなく 更新し続ける ことが前提です。私が運用しているコツが 3 つあります。

    1. コミット時の同時更新を強制する

    Claude Code Skills 編で紹介した `commit-push` Skill は、コミット前にドキュメント更新を確認するフローを持ちます。これを HANDOFF にも適用すると、「実装が進んだのに HANDOFF が古い」状態を予防できます。

    具体的には CLAUDE.md の `## Documentation` に `HANDOFF.md` を追加します。

    ## Documentation
    - docs_to_update:
      - README.md (EN)
      - README.ja.md (JA)
      - HANDOFF.md

    2. フェーズ完了時に HANDOFF を再構成する

    caelum のように Phase 分けされたプロジェクトは、Phase 完了時に HANDOFF の構造を見直します。具体的には:

    • 完了済み Phase の詳細手順を `## 履歴` に移動

    • 次の Phase の Step 1〜N を「次のタスク」に書き直す

    • 「ステータス」行を最新化

    これで HANDOFF が肥大化しすぎず、現在地が常に頭に来ます。

    3. 進捗サマリーを数値で書く

    タスクが多いプロジェクトでは、HANDOFF の冒頭に「完了 12 / 全 30」のような数値サマリーを置きます。`task-manage` Skill が自動更新するので、HANDOFF を見るだけで進捗が掴めます。

    数値サマリーがあると、Claude Code に「進捗を教えて」と聞いたときの精度が上がります。


    アンチパターン集:5 つの失敗

    最後に、HANDOFF が壊れる典型的なパターンを 5 つ。

    ❌ 1. 1 ファイルに詰め込みすぎる

    プロジェクトが大きくなると、HANDOFF.md が 2000 行を超え始めます。Claude Code が読むのに時間がかかり、本筋の指示も埋もれます。

    対策:機能単位で `HANDOFF_<feature>.md` に分割する。プロジェクト全体は `HANDOFF.md`、機能仕様は `design/HANDOFF_<feature>.md` のようにディレクトリで階層化する。

    ❌ 2. 古いまま放置する

    「あとで更新します」と書いた `TBD` が 3 ヶ月そのままに。実装は進んでいるのに HANDOFF が古い。Claude Code は古い HANDOFF を信じて、矛盾するコードを生成します。

    対策:commit-push Skill でドキュメント同時更新を強制(前述)。

    ❌ 3. 固有値をハードコードする

    「タスクファイルは `design/remaining-work.md` を参照」のような固有値が HANDOFF に直接書かれているとします。すると別プロジェクトに移植したときに即座に壊れます。

    対策:CLAUDE.md に固有値を分離する(Claude Code Skills 編で詳しく解説)。

    ❌ 4. 「次のタスク」が抽象的

    `Step 1: ログイン機能を作る` のような大きな粒度だと、Claude Code が途中で迷子になります。

    対策:1 ステップ = 1 コミット相当の粒度に分解する。`Step 1.1: src/auth/oauth.ts に Google OAuth フローのスケルトンを作成` のように具体的に書く。

    ❌ 5. 「やらないこと」を書かない

    Claude Code は親切心で機能を増やしてきます。「macOS は対象外」「動画は Phase 2」のような やらないこと・後回しにすること を明示しないと、スコープが膨張します。

    対策:`## 対象外` セクションを HANDOFF に置き、明確に書く。


    まとめと次のアクション

    3 行で振り返ります。

    1. HANDOFF は 1 種類ではない。プロジェクト全体型 / 機能単位型 / 新機能追加型 / バックエンド型の 4 つを使い分ける

    2. タイプごとに「特に効く工夫」がある。ゴール定義 / 共通仕様の集約 / 着手前作業の明示 / 採用理由の併記

    3. HANDOFF は更新し続ける。コミット時の同時更新・フェーズ完了時の再構成・進捗サマリーの数値化が運用の核

    今日すぐできるアクション: あなたの今動いているプロジェクトを 1 つ選び、本記事の 4 タイプから該当するものを当てて、HANDOFF を 30 分で書き直してみてください。「私のプロジェクトはどれにも当てはまらない」と思ったら、複数の HANDOFF に分割するサインです。


    関連記事・参考リンク

    この記事は全文無料で公開しています。もし役に立ったと感じたら、記事の下の「チップで応援する」から応援いただけると次の記事の励みになります。もちろん、スキやシェアだけでも十分うれしいです。


    本記事は Claude API の補助を受けて執筆しました。

    #ClaudeCode #HANDOFF #個人開発 #仕様駆動開発

     
     
    札幌のブリッジ SE。Tauri v2 / Rust / ESP32 / ATProto で個人開発する OSS の設計と裏話を、Claude Code との仕様駆動開発の記録として書いています。占星術アプリから防災プロトコルまで。 GitHub: osprey74

    あなたへのおすすめ