AIがコードを書く時代になりました。
Claude CodeやGitHub Copilotを使えば、CRUD実装やAPI作成は数分で完了します。
しかし最近、AIに実装を任せるほど強く感じることがあります。
「AIはコードは書けるが、正しいコードは人間が定義しなければならない」
ということです。
そして、そのために重要になるのが Design by Contract(契約による設計) と Invariant(不変条件) です。
AIはコードを書ける
例えばAIにこう依頼します。
会員登録APIを実装してください
するとAIは高い精度で実装します。
@PostMapping("/users")
public ResponseEntity<Void> register(
@RequestBody RegisterUserRequest request) {
userService.register(request);
return ResponseEntity.ok().build();
}
しかし、ここでAIには分からないことがあります。
- メールアドレスは重複可能か
- 退会済ユーザは再登録可能か
- 未成年は登録可能か
- 法人ユーザは対象外か
これらはコードから推測できません。
つまりAIは、
「どう実装するか」
は得意ですが、
「何を守るべきか」
は人間が定義しなければならないのです。
Design by Contractとは
Design by Contract(DbC)は、ソフトウェアを「契約」で設計する考え方です。
代表的な契約は3つあります。
| 種類 | 内容 |
|---|---|
| 事前条件(Precondition) | 呼び出し前に満たす条件 |
| 事後条件(Postcondition) | 処理後に保証される条件 |
| 不変条件(Invariant) | 常に成立し続ける条件 |
例えば銀行振込なら、
事前条件
振込金額 > 0
事後条件
残高が振込金額分減少している
不変条件
口座残高は常に0以上
です。
Invariant(不変条件)が最も重要
DDDでも重要視されるのがInvariantです。
なぜなら、
システムが壊れる原因の多くは不変条件の破壊だからです。
例えば返金システムを考えます。
返金状態は次のように管理されているとします。
public enum RefundStatus {
WAITING,
PROCESSING,
SUCCESS,
FAILED
}
しかし実際には、
返金成功なのに返金額が0円
という状態は許されません。
つまり本当に守るべきルールは
SUCCESSなら返金額 > 0
です。
これがInvariantです。
Spring Bootでの実装例
よくある実装はDTOバリデーションです。
@NotNull
@Positive
private BigDecimal amount;
もちろん有効です。
しかしこれは入力チェックでしかありません。
本当に重要なのはドメインモデル側です。
public class Refund {
private final RefundStatus status;
private final BigDecimal amount;
public Refund(
RefundStatus status,
BigDecimal amount) {
if (status == RefundStatus.SUCCESS
&& amount.signum() <= 0) {
throw new IllegalArgumentException(
"返金成功時は返金額が必要");
}
this.status = status;
this.amount = amount;
}
}
これなら、
どのAPIから呼ばれても、
どのバッチから呼ばれても、
Invariantが守られます。
業務システムでよくあるInvariant
実際の業務システムには大量のInvariantがあります。
例えばECサイト。
注文確定後は商品数を変更できない
会員管理。
退会済ユーザはログインできない
決済システム。
返金額 <= 決済額
銀行送金。
ロック中アカウントは送金できない
これらは単なるバリデーションではありません。
業務ルールそのものです。
そしてAIは業務ルールを推測できません。
AI時代ほどDesign by Contractが重要になる
従来は人間がコードを書いていました。
そのため、
実装者自身が暗黙的にルールを理解していました。
しかしAI時代は違います。
実装担当はAIになります。
AIは契約が与えられれば守ります。
逆に契約がなければ勝手に解釈します。
例えばClaude Codeへ依頼するとき、
/**
* Invariant:
*
* - SUCCESS時は返金額が必須
* - FAILED時は失敗理由が必須
* - 返金額は決済額を超えてはならない
*/
public class Refund {
}
と書いておけば、
AIはその制約を考慮して実装できます。
つまりDesign by Contractは、
人間同士のためだけでなく、
AIへ設計意図を伝えるための仕様書
にもなります。
まとめ
AIによってコードを書くコストは大幅に下がりました。
しかし、
何を守るべきかを定義する責任は人間に残ります。
そのため今後は、
- 事前条件(Precondition)
- 事後条件(Postcondition)
- 不変条件(Invariant)
を明確に定義できるエンジニアの価値が高まります。
AI時代に必要なのは、
コードを書く力だけではありません。
「システムが守るべき契約を定義する力」
です。
そしてその基礎となるのが、Design by ContractとInvariantなのだと思います。
※本記事は、2026年5月開催の JJUG CCC 2026 Spring で得た学びをもとに整理した内容です。