AI支援開発でも有効なJoel Spolskyのソフトウェアの真理
AIが速くコードを書けても、Joel Spolskyのソフトウェアの真理は今も有効です。テスト、採用、単純さで正確性を保つ方法を学びましょう。

AIが素早くコードを書くときでも、なぜこれらの真理が重要なのか
AIは数分で「動いているように見える」コードを生成できます。これはプロジェクトのペースを変えますが、ソフトウェアが成功するための本質は変わりません。Joel Spolskyの「ソフトウェアの真理」は単なるタイピング速度の話ではなく、判断、フィードバックループ、自己造成的な複雑さを避けることに関する教訓です。
変わったのはコード作成のコストです。3つのアプローチや5つのバリエーション、あるいは全面的な書き直しを即座に頼めるようになりました。しかし、正しいアプローチを選び、それを検証し、数か月間それと付き合うコストは変わっていません。書くことにかかる時間が減っても、意味を決めること、エッジケースを検証すること、今日の即席の勝利が明日の保守負債にならないようにすることに時間が移ります。
正確性、セキュリティ、保守性は依然として実時間を要します。なぜならそれらは自信ではなく証拠に依存するからです。ログインフローはコンパイルされたら終わりではありません。悪い入力を確実に拒否し、異常な状態を扱い、データを漏らさないときに初めて完成です。AIは確信を持って語ることがありますが、エンドポイントの権限チェックや支払い更新時の競合状態のような重要な細部を見落とすことがあります。
AIは高速な下書き生成機として扱うと最も力を発揮します。定型処理、反復パターン、素早いリファクタ、並べて比較できる選択肢の探索が得意です。うまく使えば「白紙状態」を圧縮してくれます。
しかし曖昧な目標を与えて出力をそのまま受け入れると失敗します。同じ失敗パターンが繰り返されます:隠れた前提(明示されていないビジネスルール)、テストされていない経路(エラーハンドリング、リトライ、空状態)、自信に満ちた誤り(もっともらしいが微妙に間違ったコード)、後で説明しにくい「賢い」解決策。
コードが安く作れるとき、新たに稀少になる資源は信頼です。これらの真理はその信頼を守るために重要です:ユーザー、チームメイト、そして未来の自分との信頼を。
テストが依然ボトルネックであり、それは良いこと
AIが機能を数分で生成できると、テストが遅くて取り除くべき部分だと考えたくなります。Spolskyの指摘は今も有効です:遅い部分に真実があります。コードは簡単に生成できますが、正しい振る舞いはそう簡単ではありません。
役立つ観点のシフトは、テストを実行できる要件として扱うことです。期待される振る舞いをチェック可能な形で記述できないなら、まだ考えが足りません。AI支援の作業では、モデルが少しだけ間違って自信満々に出すことがあるため、これはむしろより重要になります。
まずは壊れたときの影響が大きいものからテストを始めましょう。多くのプロダクトではそれがコアフロー(サインアップ、チェックアウト、保存、エクスポート)、権限(誰が見る・編集する・削除するか)、データ整合性(重複なし、合計が正しい、安全なマイグレーション)です。次に深夜のインシデントを起こしがちな境界ケースをカバーします:空の入力、長い文字列、タイムゾーン、リトライ、支払いやメール、ファイルアップロードのような外部の不安定な境界。
AIはテストケースの提案が得意ですが、実際にユーザーに何を約束したかは知りません。ブレインストーミングの相手として使い、欠けているエッジケース、悪用シナリオ、権限の組み合わせを尋ねます。その後に人間の仕事をしてください:カバレッジを実際のルールに合わせ、実装を単に「テストしているだけ」のテストは削除します。
失敗が実行可能な手がかりになるようにしましょう。失敗したテストは何が壊れたかを示すべきで、断片的な手がかりで探索に駆り立ててはいけません。テストは小さく、名前は文章のようにし、エラーメッセージは具体的にします。
簡単な例
AIの助けで簡単な「チームノート」アプリを作るとします。CRUD画面は素早く出てきます。正確性のリスクはUIではなくアクセス制御やデータルールです:ユーザーは他チームのノートを見てはいけない、編集が新しい変更を上書きしてはいけない、ノート削除で添付が孤立してはいけない。これらのルールを固定するテストはボトルネックに感じるかもしれませんが、安全網でもあります。
テストがボトルネックになると、明確さを強制します。その明確さが高速なコードを高速なバグに変えるのを防ぎます。
AIが入るときは賢さより単純さが勝つ
最も持続的な真理の一つは、単純なコードは賢いコードに勝つ、ということです。AIは洗練されていて迅速に提供されるため、派手な抽象化を受け入れたくなります。しかしコストは後で現れます:バグが隠れる箇所が増え、読まなければならないファイルが増え、「これ何してるの?」という瞬間が増えます。
コードが安価になると、支払うのは複雑さです。小さく退屈な設計はテストしやすく、変更しやすく、説明しやすい。最初の草案がモデル由来で微妙に間違って聞こえることがあるとき、これはさらに重要です。
実務的なルールは関数やコンポーネント、モジュールをチームメイトが数分でレビューできる程度の小ささに保つことです。もしReactコンポーネントが複数のカスタムフック、ローカルな状態マシン、汎用の「スマートレンダラー」レイヤーを必要とするなら、一旦止まってそれが実際の問題を解いているのか、AIが提供したから受け入れているだけか問うべきです。
いくつかの「単純さテスト」は反発に役立ちます:
- 新しいチームメイトが一回で主要な流れを理解できるか?
- この抽象を削除してプレーンなコードに置き換えても明確さを失わないか?
- バグを直すべき箇所は一か所か、それとも五か所か?
- 各部分の仕事を一文で言えるか?
プロンプトも重要です。「最高のアーキテクチャを教えて」と聞くと大抵過剰な設計が返ってきます。ファイル数を最小にする、3か所以上の重複を除かない限り新規抽象化を導入しない、明示的なコードを優先する、といった制約を与えて単純さに押し戻しましょう。
具体例:管理ページにロールベースのアクセスを追加してとAIに頼んだとします。賢いバージョンは権限フレームワークやデコレータ、設定DSLを導入します。単純なバージョンは1か所でユーザーロールをチェックし、ルートを一箇所で制御し、拒否をログに残すだけです。単純な方がレビューしやすく、テストしやすく、誤解されにくい。
チャットベースのツール(例えば Koder.ai)で作業するなら、単純さはスナップショットやロールバックをより価値のあるものにします。小さく明白な変更は比較、保持、取り消しが容易です。
採用:タイピストではなく編集者と意思決定者が必要
コードが簡単に作れるとき、希少なのは「何を作るべきか」を選び、それが正しいことを保証するスキルです。従来の「優れたプログラマを採用しろ」という助言は今も有効ですが、役割は変わります。速く入力する人を採るのではなく、判断し、洗練し、製品を守る人を採ります。
AI支援開発で最も価値がある人には次の4つの特性がよく見られます:判断力(何が重要か)、センス(良いものとは何か)、デバッグ力(根本原因を見つける力)、コミュニケーション(トレードオフを明確にする力)。彼らはAIが書いた「ほとんど動く」機能を信頼できるものに仕上げます。
より良い面接法:AIの変更を改善させる課題
完璧なソリューションをゼロから要求する代わりに、AI生成のPull Request(あるいは貼り付けたdiff)を与え、現実的な問題をいくつか混ぜます:不明瞭な命名、隠れたエッジケース、抜けているテスト、小さなセキュリティミス。
候補者にコードの意図を平易な言葉で説明させ、リスクが最も高い部分を見つけさせ、修正案と回帰を捕まえるテストを追加または概説させます。強いシグナルが欲しければ、次のAI試行をより良くするために指示をどう変えるかも聞いてください。
これは不完全なコード、限られた時間、優先順位を選ぶ必要があるという現実的な条件下で彼らがどう考えるかを示します。
スーパー力:"ノー"と言えること
AIはしばしば自信満々に見えます。良い採用候補者は抵抗できる人です。複雑さを増す機能にノーと言える、セキュリティを弱める変更にノーと言える、証拠なしで出荷することにノーと言える人が重要です。
具体的な信号は「これをマージするか?」という問いへの応答です。強い候補者は雰囲気で答えず、決断と必要な変更の短いリストを示します。
例:簡単なアクセス制御の更新を頼んだらAIがハンドラにチェックを散らす案を出しました。強い候補者はそのアプローチを却下し、1つの明確な認可レイヤーと管理者/非管理者経路のテストを提案します。
最後に、チーム全体でAI出力を同じ方法で編集するための基準を作ってください。簡潔に:1つの完了定義、レビュー期待の統一、テストベースライン。
仕様と計画:明確なプロンプトは明確な思考から始まる
AIが短時間で大量のコードを生成できると、考えることを省いて反復する誘惑があります。デモには通用しますが、正確性や予測可能な振る舞い、驚きの少ない結果が必要なときには破綻します。
良いプロンプトは隠れた短い仕様です。コードを依頼する前に、曖昧な目標をいくつかの受け入れ基準と明確な非ゴールに変えてください。これがAI(とチーム)がスコープを黙って拡大するのを防ぎます。
仕様は小さく具体的に保ちます。小説を書くのではなく、次を境界に定めます:
- 入力:入ってくるもの(フィールド、フォーマット、エッジケース)
- 出力:出すべきもの(例も含む)
- エラー:何が起こり得てどう応答するか
- 制約:パフォーマンス、プライバシー、依存関係、変更してはいけない箇所
- 非ゴール:今回やらないこと
生成前に「完了」を定義してください。「コンパイルする」「UIが見た目良い」以上のものにします。テスト期待値、下位互換性、リリース後に監視する項目を含めます。
例:"パスワードリセットを追加"したい場合、より明確な仕様は次のようになります:ユーザーはメールでリセットを要求する;リンクは15分で期限切れ;メールアドレスが存在するかどうかに関係なく同じメッセージを表示;IPごとにレート制限;トークンを平文で保存せずにリセット試行をログに残す。非ゴール:ログインページの再設計は行わない。こうすればプロンプトにガードレールが入り、レビューは簡単になります。
決定ログを軽く残してください。決定ごとに1段落で十分です。なぜそのアプローチにしたか、代替案をなぜ却下したかを記しておけば、2週間後に「なぜこうなっているの?」と聞かれても答えられます。
再現可能な実用的ワークフロー
AIで最大の変化はコード生成が簡単になったことです。難しいのはコードに何をさせるかを決め、それが期待通りに動くことを証明することです。
まずゴールと制約を平易な言葉で書きます。決して起こしてはいけないこと、遅くてもよいこと、今回の変更で対象外のことを含めます。テスト可能な制約を用いると良い:「他人のデータを見せないこと」や「合計が会計エクスポートと小数点以下まで一致すること」など。
コードを頼む前に、簡単な設計とトレードオフを求めます。AIに保存するもの、検証するもの、ログに残すものを示させ、判断できる形で理由を見せてもらいます。もし賢い案を出してきたら、制約を付けて単純なバージョンを要求してください。
繰り返せるループは次の通りです:
- 短い問題定義と3〜5個の明確な合否判定を作る。
- 最小の計画を求める:データモデル、主要関数、起こり得る問題。
- 一度に小さな変更を1つだけ生成する(1エンドポイント、1画面、1マイグレーション)。
- 編集者のようにレビューする:diffを読み、テストを実行し、失敗ケースを試し、修正を要求する。
- 安全にリリースする:フラグや限定ロールアウトを使い、ログと指標を監視し、必要ならロールバック用意。
小さなシナリオ:注文画面に「返金ステータス」を追加する場合。AIはUIを素早く生成できますが、正確性はエッジケースにあります。部分返金はどう扱うか?決済プロバイダがウェブフックをリトライしたら?これらのケースを先に書き、データベースカラムとバリデーションの1スライスを実装してテストで確認してから次に進みます。
Koder.aiを使うなら、planning mode、スナップショット、ロールバックはこのループに自然にフィットします:まず計画し、スライスで生成し、意味のある変更ごとに安全な復元ポイントを取る。
AIでコーディングが簡単になったと感じるときのよくあるミス
コード生成が速いと、コードそのものを成果物だと扱いたくなります。しかし成果物は振る舞いです:アプリが正しく動くこと、たとえ問題が起きても安全に失敗すること。
1) 自信ある出力を検証せずに信じる
AIは確信を持って話すことが多いですが、推測していることがあります。退屈な作業を飛ばすのが失敗です:テストを実行し、エッジケースを確認し、本物の入力で検証すること。
簡単な習慣:変更を受け入れる前に「どうやってこれが正しいと分かる?」と自問する。答えが「見た目問題なさそう」なら賭けをしています。
2) ツールにスコープを広げさせる
AIはキャッシュ、リトライ、設定、追加エンドポイント、より良いUIなどを提案しがちです。良いアイデアもありますが、リスクも増えます。多くのバグは誰も頼んでいない「便利機能」から生まれます。
厳格に:まず設定した問題を解き、それでやめましょう。価値があれば別タスクにしてテストを付けます。
3) レビューできないほど大きな変更をマージする
大きなAI生成コミットは無関係な決定を隠します。誰も全体を把握できず、レビューは形骸化します。
チャット出力を草案として扱い、小さな変更に分けて読んで実行し、戻せるようにします。スナップショットとロールバックは意味のある時点で取る必要があります。
簡単な制限:1変更セットにつき1機能、1マイグレーション、1つの高リスク領域(認証、支払い、データ削除)、同じ変更にテスト更新、検証方法の明記。
4) ライセンスやセキュリティリスクのあるコードをコピペする
AIは学習データからのパターンを再現したり、知らない依存を提案したりします。ライセンスの問題もありますが、もっと大きいのはセキュリティ:ハードコーディングされた秘密、弱いトークン処理、安全でないファイルやクエリ操作など。
スニペットの動作を説明できなければ出荷しないでください。簡易版を要求するか自分で書き直しましょう。
5) マイグレーション、制限、失敗モードを忘れる
多くの「ローカルでは動いた」バグはデータやスケールの問題です。AIがスキーマ変更を提案して既存行を考慮していないことがあります。
現実的な例:モデルがNOT NULLの新カラムを追加し、遅いループでバックフィルする。実運用ではテーブルロックやアプリの停止を招く可能性があります。百万行や遅いネットワーク、途中で失敗したデプロイを常に考えてください。
例:速度より正確さが重要な小さなアプリ
小さな内部リクエストトラッカーを想像してください:人がリクエストを出し、マネージャが承認・却下し、経理が支払い済みにする。AIの支援で画面やエンドポイントはすぐに生成できますが、遅くなるのはいつも同じ真実:ルールです。
まず正確にしておく最小限を書き出します。平易に説明できなければテストできません。
初版の厳密な定義は次のようになります:フィールド(title, requester, department, amount, reason, status, timestamps);役割(requester, approver, finance, admin);ステータス(draft, submitted, approved, rejected, paid)。重要な遷移を決める:submitted を approved/rejected にできるのは approver のみ;approved を paid にできるのは finance のみ、等。
AIは制御された順序で使って誤りを早く見つけられるようにします:
- まずデータベーススキーマとステータス列挙を定義する。
- 汎用のupdateルートではなく遷移(submit、approve、reject、pay)周りのエンドポイントを生成する。
- APIに基づいて最後にUIを生成する。
最も価値のあるテストは「ページが読み込めるか」ではなく権限チェックと状態遷移です。例:requesterが自分の申請を承認できないこと、approverが支払いをマークできないこと、rejectされたものは支払われないこと、(ルール次第で)金額は提出後に編集できないこと等を証明します。
時間がかかるのはエッジケースを明確にする作業です。承認者が却下後に意見を変えられるか?二人の承認者が同時にapproveを押したら?経理が部分的に支払う必要があるか?AIはどんな答えでもコード化できますが、答えを選ぶのはあなたです。正確さはその選択を行い、コードに守らせることから来ます。
AI生成変更を出荷する前の簡単チェックリスト
AIは大量のコードを速く出しますが、最後の一歩は人間の仕事です:期待どおり動くことを証明し、動かなかったときに安全に失敗すること。
まず、重要な「完了」の定義を最小限で決めてください。小さな機能ならハッピーパス1つ、失敗パス2つ、簡単な可読性チェックで十分かもしれません。支払いや認証なら基準を上げます。
5つのチェック
- 要件はすべて検証可能である。各要件に対してテストか一文でできる手動チェックがある。検証方法を説明できないなら理解不足の可能性あり。
- 失敗は扱われ説明されている。主要なエラーを試す(不正入力、権限不足、ネットワークエラー、空データ)。有用なメッセージを出し、機密を漏らさない。
- 設計は単純に保たれている。AIがヘルパーや抽象を追加したら、それを手で書いたら残すか?余分な層は削除。
- 新しいレビュワーが追える。チャット履歴を見ていない前提で、コードが物語のように読めること:明確な名前、短い関数、変更の理由を短く記す。
- 安全に元に戻せる。通常状態に戻る手順を知っていること。良いバージョンを確保し、素早くロールバックできるか確認する。
例
AIが管理画面に「一括招待」機能を追加したとします。ハッピーパスは動くかもしれませんが、リスクは重複メール、部分的失敗、レート制限です。堅実な出荷判断は重複用の自動テスト1つ、部分失敗メッセージの手動チェック1つ、ロールバック計画を持つことかもしれません。
次の一手:ガードレールを追加してプロセスを拡大する
コードが安価になるとリスクは意思決定の質に移ります:何を頼んだか、何を受け入れたか、何を出荷したか。AI支援作業でこれらの真理を活かす最速の方法は、“ほぼ正しい”変更が通り抜けないようガードレールを追加することです。
次の機能について1ページの仕様を書いて始めてください。内容は簡潔に:誰のためか、何をするか、何をしないか、日常語で書かれた受け入れテスト数個。これらの受け入れテストがAIの誘惑に対するアンカーになります。
プロセスを大きくしすぎないガードレール例:
- レビューが数分で終わるよう小さな変更に保つ。
- すべての振る舞い変更に対してテスト(またはテストノート)を必須にする。
- 標準のプロンプトテンプレートを使う:制約、コーディングスタイル、テスト期待値。
- すべてのデプロイに信頼できるロールバック経路を持つ。
- 未知の部分は明示的なTODOとして残し、隠れた前提にしない。
プロンプトはプロセスの一部です:使用可能なライブラリ、エラー処理方針、完了とは何か、合格すべきテストをチームで合意します。再利用できないプロンプトはおそらく曖昧すぎます。
チャットファーストでWeb、バックエンド、モバイルアプリを構築したいなら、Koder.ai(koder.ai)は planning mode、スナップショット、ソースコードのエクスポートといった機能でこれらのガードレールを支援する一例です。ツールは草案を速めますが、正確性を保つのは規律です。
よくある質問
コードをこんなに速く生成できるAIを安全に使う最も安全な方法は?
AIの出力を完成品ではなく素早い草案として扱いましょう。まず3〜5個の合格/不合格の受け入れ基準を書き、その後で1つのスライス(1つのエンドポイント、1画面、1つのマイグレーション)を生成して、テストと失敗ケースで検証してから次に進んでください。
AI生成コードでもテストがそんなに重要なのはなぜ?
テストはコードが「実際に何をするか」を明らかにする場所だからです。AIはもっともらしいロジックを出しますが、権限やリトライ、エッジケースの1つを見落とすことがあります。テストは期待を実行可能で繰り返せるものに変えます。
AI支援プロジェクトで最初に何をテストすべき?
まずは壊れたときに痛手が大きい部分から始めてください:
- コアフロー(サインアップ、チェックアウト、保存、エクスポート)
- 権限(誰が表示/編集/削除できるか)
- データ整合性(重複がない、合計が正しい、安全なマイグレーション)
- 失敗モード(タイムアウト、リトライ、空の状態、不正な入力)
高被害の振る舞いをまず固めてからカバレッジを広げていきます。
AIに過度に複雑なアーキテクチャを作らせないには?
「最も単純な方法」を明示的な制約で求め、不要な層は削除しましょう。良いルール:3箇所以上の重複を取り除かない限り新しい抽象化を導入しない、正確さを証明しやすくしない限りジェネリックなヘルパーは避ける、明示的なコードを優先するなど。
あいまいな機能要求を良いプロンプトにするには?
短い仕様を書きます:入力、出力、エラー、制約、非ゴールを含めます。具体例(リクエスト/レスポンスやエッジケース)を付け、完了条件を事前に定義してください。これでAIやチームがスコープを拡大しにくくなります。
誰もレビューできない大きなAI生成コミットを避けるには?
分割して進めること。各変更セットを数分でレビューできる大きさにしてください:
- 1つの機能を1つの変更セットに
- 1つのマイグレーションを1つの変更セットに
- テストは同じ変更で更新
- 何が変わったか、どう検証するか、何が壊れる可能性があるかを短く書く
これでレビューが実態を伴ったものになります。
AIの出力を鵜呑みにする最大のリスクは?
自信と証拠は違います。テストを実行し、壊れた入力を試し、権限境界を確認してください。またAIが陥りがちな落とし穴を探しましょう:認証チェックの欠落、安全でないクエリ、弱いトークン管理、エラーを無視するコードなど。
APIとビジネスルールはどう構成すればミスが起きにくい?
汎用の "update anything" より、明示的な遷移エンドポイントを好みます。例えば submit、approve、reject、pay のような個別のエンドポイントにして、誰がどの遷移を行えるかをテストで強制します。
AI支援開発のエンジニアをどう面接する?
AIが作った差分(diff)を与え、実際にありそうな問題:不明瞭な名前、抜けているテスト、エッジケース、小さなセキュリティ問題を含めます。候補者に意図を説明させ、リスクの高い部分を見つけさせ、修正案と追加するテストを示させてください。
スナップショットやロールバックはAI支援ワークフローにどう合う?
計画を先に立て、小さなスライスで生成し、リスクのある変更前にスナップショットを取り、検証に失敗したらロールバックする—このループが有効です。チャット型のツールである Koder.ai(koder.ai)は、planning mode、スナップショット、ロールバックなどと相性が良い例です。