Implement Issue
Polish済みのIssueを正本として、記載された課題、解決方針、期待する結果、検証方法に従って必要最小限の変更を実装し、課題が解決したことを検証する。
コードを書き終えることを目的にしない。
Issueで決定された判断を実装へ移し、期待する結果が成立したことを確認して完了する。
原則
- 一度に一つのIssueを対象とする
- Polish済みのIssueを実装判断の正本として扱う
- 課題、解決方針、期待する結果、検証方法のつながりを維持する
- Issueに書かれていない新しい設計判断を黙って追加しない
- 課題を解決するために必要な最小の変更を実装する
- 関連しているだけのリファクタリングや改善を混ぜない
- プロジェクト固有の規約を標準より優先する
- 既存の作業ツリーとユーザーの変更を維持する
- 既存の振る舞いを変更する場合、その変更がIssueの解決に必要であることを確認する
- コードには How、テストには What、コミットには Why、コードコメントには Why not を残す
- 実装中にIssueの前提が誤っていることが判明した場合、無理に実装を続けない
- 課題、期待する結果、互換性、変更範囲に影響する判断が必要になった場合はIssueへ戻す
- 実装の完了ではなく、Issueに定義された課題の解決をもって完了とする
- 検証できない変更を完了扱いにしない
入力
次を入力として扱う。
- Polish済みの一つのIssue
- Issue本文、コメント、関連Issue、変更履歴
- 対象プロジェクトのコード、テスト、仕様、履歴
- プロジェクト固有の実装、テスト、コミット規約
- 現在の作業ツリーと既存の未コミット変更
Issueには少なくとも、次の情報が含まれていることを期待する。
- 課題
- 解決方針
- 期待する結果
- 検証方法
これらが不足し、実装判断を安全に進められない場合は、推測で補わずIssueへ戻す。
出力
次のいずれかを出力する。
- Issueを解決する実装とテスト
- 実装結果と検証結果
- Issueの前提または解決方針に問題があり、実装を継続できない場合の根拠
- 人間の判断が必要な場合の確認済み事実と未決定事項
手順
1. 実装前の状態を確認する
実装を始める前に、Issueと作業環境の現在状態を確認する。
次を確認する。
- Issueが最新状態であること
- Issueが現在も実装対象であること
- Issueに実装開始の承認があること
- 対象ブランチと現在のブランチ
- 作業ツリーの変更状況
- ユーザーまたは他の作業による既存変更
- プロジェクト固有の実装、テスト、コミット規約
- Issueから参照されているコード、テスト、仕様
以前取得したIssueやローカルの控えだけを信頼しない。
既存の未コミット変更がある場合は、その内容とIssueとの関係を確認する。
Issueと無関係な既存変更は変更、削除、整形しない。
次が判明した場合は実装を開始せず、根拠を報告する。
- Issueがすでに解決されている
- Issueが閉じられている、または実装対象ではなくなっている
- Polish後に前提となる仕様やコードが変わっている
- Issueの解決方針が現在の状態と成立しない
- 必要な実装判断がIssueに残っている
- 既存変更と安全に分離して実装できない
2. Issueの実装契約を読み取る
Issueから、実装で維持すべき契約を明確にする。
少なくとも次を確認する。
- 解決すべき課題
- 採用された解決方針
- 変更対象の境界
- 期待する結果
- 維持すべき既存の振る舞い
- 検証方法
- 実装時に参照すべきコード、テスト、仕様、履歴
Issueに書かれた作業項目を、課題そのものとして扱わない。
実装中の判断は、課題と期待する結果から逆算して行う。
Issueに複数の実装候補が残っている場合は、その場で任意に選ばない。
実装方法の詳細がIssueに書かれていないこと自体は問題としない。 コードを読めば決定できる実装上の詳細は、既存実装とプロジェクト規約に従って決定する。
一方、次のような判断が必要な場合は、実装を進めずIssueへ戻す。
- 外部から観測される振る舞いを変更する
- Issueの変更対象を広げる
- 互換性に影響する
- データモデルや公開インターフェースを変更する
- 複数の方針に重要なトレードオフがある
- Issueの期待する結果そのものを変更する必要がある
3. 変更箇所を特定する
Issueの課題、解決方針、期待する結果から、変更が必要な箇所を特定する。
次を確認する。
- 課題が発生しているコードパス
- 変更によって影響を受けるコード
- 関連するテスト
- 関連する設定、スキーマ、インターフェース
- 類似する既存実装
- 変更によって維持すべき周辺の振る舞い
Issueに参照先が示されている場合も、その箇所だけを機械的に変更しない。
実際の依存関係とデータまたは制御の流れを確認し、期待する結果を成立させるために必要な変更範囲を決める。
変更範囲は、課題を解決するために必要な最小の範囲とする。
次の変更は、Issueの解決に必要でない限り含めない。
- 周辺コードの整理
- 命名の統一
- Issueと無関係なリファクタリング
- 依存ライブラリの更新
- フォーマットだけの変更
- 将来のためだけの抽象化
4. 検証を実装可能なテストへ落とす
Issueに定義された検証方法を、実装後に再現可能なテストまたは確認手順へ落とす。
可能な限り、実装前に次を明確にする。
- どの振る舞いを確認するか
- どの入力または条件を与えるか
- 何を期待結果とするか
- どの既存の振る舞いを維持するか
- どの境界条件または失敗条件を確認するか
テストには実装方法ではなく、外部または利用者から観測される期待する振る舞いを書く。
bug では、可能な限り問題を再現するテストを先に用意し、変更前に失敗することを確認する。
task では、新しく成立すべき振る舞いをテストとして表現する。
既存テストで十分に検証できる場合は、同じ目的のテストを重複して追加しない。
テストだけではIssueの解決を確認できない場合は、必要な実行確認、計測、ログ確認、または手動確認を検証方法として残す。
Issueに定義された期待する結果をテストへ落とせない場合は、その理由を確認し、必要に応じてIssueへ戻す。
5. 実装する
Issueの解決方針と期待する結果に従って、必要な変更を実装する。
実装では次を守る。
- 既存の設計、命名、構造、依存関係の流儀に従う
- 課題を解決するために必要な最小の変更にする
- Issueで維持するとした既存の振る舞いを壊さない
- Issueと無関係な変更を混ぜない
- 既存の未コミット変更を上書きしない
- 不要な抽象化や将来用途だけの拡張を追加しない
コードは、処理がどのように実現されているかを読める状態にする。
コードコメントは、コードから明らかな処理内容を説明するために使わない。
コメントを残す場合は、主に次のような情報を記録する。
- 一見自然な別の実装を採用しなかった理由
- 外部仕様や互換性による制約
- 通常とは異なる実装を維持する必要がある理由
既存コードとプロジェクト規約から一意に決められる実装詳細は、そのまま決定してよい。
6. 実装中に継続的に検証する
変更をまとめて実装して最後に確認するのではなく、意味のある単位ごとに検証する。
変更の内容に応じて、次を実行する。
- 対象テスト
- 関連する既存テスト
- 静的解析
- 型チェック
- Lint
- ビルド
- 必要な実行確認
失敗した場合は、失敗を回避するためにテストや検証条件を弱めない。
まず次を確認する。
- 実装が誤っているか
- Issueの前提が誤っているか
- 既存の振る舞いに想定外の依存があるか
- テストまたは検証方法自体が現在の仕様と一致しているか
変更によって新しい失敗が発生した場合は、その原因を特定してから次の変更へ進む。
Issueと無関係な既存の失敗を発見した場合は、今回の変更に混ぜて修正しない。
ただし、その失敗によってIssueの検証ができない場合は、実装結果と区別して報告する。
検証結果からIssueの解決方針では期待する結果を成立させられないことが判明した場合は、実装を広げて辻褄を合わせずIssueへ戻す。
7. 差分を見直す
実装と検証が完了したら、変更差分全体をIssueの目的に照らして見直す。
次を確認する。
- すべての変更がIssueの課題解決に必要か
- 解決方針から外れた変更が含まれていないか
- 期待する結果を満たしているか
- 維持すべき既存の振る舞いを壊していないか
- Issueと無関係な変更が混ざっていないか
- 不要なリファクタリング、抽象化、整形が含まれていないか
- テストが実装方法ではなく期待する振る舞いを確認しているか
- コメントがコードから明らかな処理内容を説明していないか
- 既存の未コミット変更を意図せず変更していないか
差分は、ファイル単位ではなく変更全体として確認する。
途中の実装判断によって変更範囲が広がっている場合は、それがIssueの課題解決に必要かを再評価する。
不要な変更は取り除く。
8. 最終検証を行う
差分の見直し後、Issueに定義された検証方法に従って最終検証を行う。
少なくとも次を確認する。
- Issueで期待された結果が成立している
bugでは、元の問題が再現しないtaskでは、新しい振る舞いが利用可能になっている- 関係する境界条件と失敗時の振る舞いが期待どおりである
- 維持すべき既存の振る舞いが保たれている
- 変更に関係するテストが成功している
- プロジェクトで必要とされる静的解析、型チェック、Lint、ビルドが成功している
可能な場合は、Issueに記載された検証方法をそのまま実行する。
実装途中に使用した限定的なテストだけで完了を判断しない。
検証できなかった項目がある場合は、成功したものとして扱わず、次を明確にする。
- 検証できなかった内容
- 検証できなかった理由
- 実施済みの代替確認
- 完了判断に残る不確実性
Issueの期待する結果を確認できない場合は、コードが完成していても完了としない。
9. 実装結果をまとめる
実装と最終検証が完了したら、Issueに対する実装結果を簡潔にまとめる。
少なくとも次を示す。
- 何を変更したか
- Issueの課題に対して、その変更がどのように作用するか
- どのように検証したか
- 検証結果
- 検証できなかった事項
- 残っている不確実性または人間の判断が必要な事項
変更したファイルや処理内容を機械的に列挙するだけにしない。
実装結果は、Issueに定義された次のつながりに沿って説明する。
課題 → 解決方針 → 実装 → 期待する結果 → 検証結果
Issueと無関係な既存の失敗や変更を発見した場合は、今回の実装結果と区別して記載する。
実装が完了していても、Issueの期待する結果を確認できていない場合は完了として報告しない。
10. 完了条件を確認する
次をすべて満たした場合に、Implement Issueを完了とする。
- Issueに定義された課題へ必要な変更が実装されている
- 採用された解決方針から外れた変更が含まれていない
- Issueに定義された期待する結果が成立している
- 必要なテストまたは検証が実施されている
- 維持すべき既存の振る舞いが保たれている
- プロジェクトで要求される検証が成功している
- Issueと無関係な変更が差分に含まれていない
- 検証できなかった事項と残る不確実性が明示されている
- 実装中に発生した重要な設計判断がIssueと矛盾していない
コードを書き終えたこと、テストを追加したこと、CIが成功したことだけでは完了としない。
最終的な完了判断は、Issueに定義された課題が解決したことを確認できるかで行う。
完了条件を満たせない場合は、未完了のまま次を報告する。
- 満たせていない条件
- その理由
- 確認済みの事実
- 次に必要な判断または対応