型へのフィールド追加で「コンパイルが通った」を網羅の根拠にできるのは、組み立て経路が1本に閉じているときだけ
Go
TypeScript
設計判断
コードレビュー
知識
判断
運用
既存の型(DTO・エンティティ・設定構造体)にフィールドを追加するとき、「ビルドが通ったのだから全箇所に反映された」と判断してしまうことがある。この推論が成立するのは、その型の値を作る経路がコンストラクタ(ファクトリ関数)に閉じている場合に限られる。
なぜコンパイルが証拠にならないか
- コンストラクタ経由の箱所は、引数を増やせば型検査が全呼び出し元を強制的に洗い出す。修正しなければビルドが通らないので、ここに漏れは残らない。
- 同じ型をリテラル(フィールド名を列挙する形式)でも組める言語では、リテラル側はフィールドを省略しても型が通る。追加したフィールドはゼロ値(空文字・0・null)のまま黙って残る。
- つまり「コンパイルが通った」は、強制力のある経路だけを検査した結果であって、未検査の経路が存在しないことの証明ではない。
実害が大きい場面
- API レスポンス DTO: キーは出るのに値が常に空になる。受け手は「未設定」と「抹け落ち」を区別できず、典型的には一覧や検索の経路だけ正しく、単体取得だけが空という間欠的な見え方になる。
- テストフィクスチャ・モック応答: 検体側だけゼロ値になり、テストが緑のまま実装の退行を見逃す。
- 設定構造体: 新しい設定項目が一部の初期化経路だけ既定値になる。
判断基準
- フィールド追加の変更マップを「ビルドエラーが出た箱所」で確定させない。値の組み立て経路を先に列挙し、コンストラクタ経由とリテラル経由を分けて数える。
- 列挙は人手ではなく機械的に行う。型名に続けて波括弧で始まるリテラルの出現箱所を検索し、コンストラクタの呼び出し件数と合計が合うかを見る。
- リテラル経路が残るなら、フィールド追加と同時にコンストラクタへ寄せるか、少なくともその経路を通る契約テスト(実レスポンスに非空値が載ることの検証)を追加する。型だけでは守れないと割り切る。
- 同じ型を複数経路で組む設計自体を、フィールドが増える見込みのある型では避ける。
落とし穴
- オプショナルフィールドや既定値付きフィールドは、コンストラクタ経由でさえ強制力が無い。引数を増やすのではなくオプション引数で受ける形にすると、同じ黙りの漏れがコンストラクタ側にも戻る。
- 埋め込み型や委譲によって自動追従する経路と、手で組む経路が混在すると、前者だけ見て「網羅されている」と誤認しやすい。
検証方法
追加したフィールドに非空値を入れた状態で、値を返す全経路を実際に叩いて値が乗ることを見る。一覧・検索・単体取得など経路ごとに分けて確認する(どれか1つだけ確かめて全体を推さない)。レビューでは「このフィールドを組み立てている箱所を全部数えたか」を明示の確認項目にする。