Enludus
お問い合わせ

人間に読みやすい設計は、AI にも変更しやすいのか ── 3 つの構成で 54 回試した

同じタスク管理 API を「1 ファイル」「レイヤー分割」「機能単位」の 3 つの構成で書き、同じ AI に 6 つの変更課題を 3 回ずつ、合計 54 回解かせました。差が出たのは要件の実装ではなく、整形と、私が作った評価の側でした。

AI にコードを直してもらうことが、ふだんの開発でも当たり前になりました。そこで気になったのが、人間のために分けてきた設計は、AI にとっても直しやすいのか ということです。

責務ごとにファイルを分けると、人間は「どこに何があるか」を名前で探せます。一方で AI からすると、読むべきファイルが増えて、つながりを追う手間も増えます。全部 1 ファイルにまとめたほうが、AI は迷わないのかもしれません。

感覚で語るのはやめて、同じ条件で試すことにしました。それが ai-readable-code-lab です。この記事では、何をして、何がわかって、何がわからなかったのかを書きます。

やったこと

題材は、小さな タスク管理 API です。タスクの作成・一覧・更新・完了・削除ができて、「タイトルは必須」「完了済みは二重に完了できない」といった業務ルールが少しあります。言語は TypeScript、フレームワークは Fastify で、データはメモリに置いています。

同じ API を、次の 3 つの構成で書きました。

構成 中身
monolithic 型・保存・業務ルール・HTTP をすべて app.ts 1 ファイルに書く
layered domain・repository・service・app(HTTP)の 4 ファイルに分ける
feature-based tasks/ フォルダに、タスク機能の型・保存・ルートをまとめる

3 つとも外から見た動きは同じで、同じ契約テストを通ります。

この 3 つに対して、6 つの変更課題を用意しました。

  1. タスクに category を足す
  2. 完了済みタスクは担当者を変えられないようにする
  3. 操作履歴を残し、取得 API を足す
  4. 完了済みなのに「期限切れ」と出るバグを直す
  5. 削除をやめて、アーカイブにする
  6. タスクを複製できるようにする(わざと指示を少なめにした課題)

3 構成 × 6 課題を、それぞれ 3 回ずつ。合計 54 回 です。順番はシード固定の乱数で混ぜて、同じ構成が続かないようにしました。

解かせたのは Codex CLI 上の gpt-5.6-terra、推論の設定は medium です。毎回、新しい clone と新しいセッションで始め、AI には対象の構成と課題文と公開テストだけを渡しました。

採点は、別の clone・別のセッションで行います。採点側だけが持つ 隠しテスト を流し、型検査・整形・lint・公開テスト・ビルドもまとめて確かめます。AI がテストを書き換えて通したように見せても、隠しテストでわかるようにするためです。

結果

結果は 2 つの数字に分けて見ています。

  • 隠しテスト通過:課題の要件を満たしたか
  • 完全検証通過:型検査・整形・lint・公開テスト・ビルド・隠しテストを、すべて通したか
構成 完全検証通過 隠しテスト通過 平均変更ファイル数
monolithic 15 / 18(83.3%) 18 / 18 2.28
feature-based 13 / 18(72.2%) 16 / 18 3.11
layered 12 / 18(66.7%) 18 / 18 4.06

数字だけ見ると、1 ファイルの monolithic がいちばん良く、レイヤー分割の layered がいちばん悪く見えます。

変更ファイル数の差は、ほとんど構成の形そのものです。monolithic は本体が 1 ファイルなので、本体とテストの 2 ファイルで済みます。layered は、項目を 1 つ足すだけでも型・サービス・HTTP の 3 か所に手が入ります。ファイル数が多いことと、間違えやすいことは、別の話として見る必要があります。

でも、中身を分けると話が変わります。

まず、要件そのものはほぼ全部できていました。54 回のうち 52 回が隠しテストを通っています。layered は 18 回すべて通過です。

layered が完全検証で落ちた 6 回のうち 3 回は、整形(Prettier)だけ で落ちていました。残りの 3 回は課題 5 です。

その課題 5 は、3 構成とも 9 回すべて 完全検証を通っていません。原因は AI ではなく、私の作った評価にありました。課題文は「削除の代わりにアーカイブして 200 を返す」と言っているのに、3 構成で共有している契約テストは「削除は 204 を返す」のまま残っていたのです。課題文に従えば共有テストで落ち、共有テストに合わせれば隠しテストで落ちる。どちらを選んでも満点にならない課題でした。

課題 5 を除くと、完全検証の通過率は monolithic 15 / 15、feature-based 13 / 15、layered 12 / 15 です。順番は変わりませんが、失敗の数にすると 0 回・2 回・3 回の違いでしかありません。統計的に確かめると、この程度の差は偶然でも十分に出ます(技術編に計算を載せています)。

目立った失敗

いちばん大きく壊れたのは、feature-based で課題 6(複製)を解いた 1 回目です。

AI は複製のルート POST /tasks/:id/duplicate を、同じファイルの中に 2 回 書いていました。category という項目も同じ場所に 2 回足していて、型検査で止まり、Fastify は「そのルートはもうある」と言って起動しませんでした。

関連するコードが 1 つのフォルダにまとまっていても、自分がさっき書いたものを見落とせば、こういうことが起きます。ただ、これは 54 回中 1 回の出来事です。feature-based の性質だと言える回数ではありません。

読み直して気づいたこと

この記事を書くために差分を読み直していて、もう 1 つ気づいたことがあります。隠しテストを通っていても、仕様どおりとは限らない ということです。

課題 4 の仕様には「期限切れの警告(warnings)の仕様は変えない」と書いてありました。ところが 9 回中 7 回が、期限切れの判定と一緒に、完了済みタスクの警告まで消していました。隠しテストはこれを確かめていなかったので、全部「通過」になっています。

課題 2 でも、monolithic の 1 回は、担当者がいる完了済みタスクのタイトルだけを直そうとすると 409 で拒否していました。仕様では「担当者以外の更新は禁止しない」です。

隠しテストは課題ごとに 1 つのテストケースで、そこに書いていない約束は、AI も守るとは限りませんでした。人間が書く変更でも、同じことは起きると思います。

自分のコードの書き方にどう効くか

54 回を見て、「AI のためには 1 ファイルにまとめるべきだ」とは思いませんでした。データはそこまで言っていません。そのかわりに、次のことを意識するようになりました。

完了の条件を、1 つのコマンドで確かめられるようにする。 layered の失敗の半分は整形で、どれもテストは通っていて、整形だけが残っていました。npm run validate のように、整形まで含めて 1 回で全部確かめられる入口があれば、AI にも人間にも同じ基準を渡せます。

変える場所が 1 つに決まるようにする。 layered では、エラーの種類を メッセージの文字列 で HTTP のステータスに振り分けていました。新しいエラーを足すと、service.ts と app.ts の 2 か所を揃えて直す必要があります。分けたこと自体より、分けた境目の約束が文字列で暗黙になっていたことのほうが、変更を重くしていたと思います。

仕様とテストを食い違わせない。 課題 5 では、AI は課題文と共有テストの板ばさみになりました。人間なら誰かに聞けますが、AI はどちらかを選んで進みます。食い違いは、書いた側が先に消しておくしかありません。

守ってほしい約束は、テストに書く。 課題 4 の警告のように、テストにない約束は、変更のついでに簡単に消えます。

この実験で言えないこと

言えることより、言えないことのほうが多い実験です。

  • 回数が少ない。 1 つの条件につき 3 回です。構成ごとの差は、偶然の範囲に収まっています。
  • コードが小さい。 monolithic の app.ts は 84 行、layered も 4 ファイル合わせて 170 行ほどです。AI が迷うほどの大きさではありません。
  • AI は 1 種類だけ。 gpt-5.6-terra・medium の結果で、ほかのモデルや設定では変わるかもしれません。
  • 課題もコードも評価も、私が作った。 課題 5 の食い違いがまさにそうで、作り手の癖が結果に入り込んでいます。
  • 設計の質は採点していない。 100 点満点の採点表を用意していましたが、54 回すべて未記入のままです。読んだファイル数やトークン数も記録できていません。採点した側も、AI のセッションです。

これから

次は、課題 5 のような食い違いを実行前に見つける仕組みと、隠しテストのケースを増やすところから始めます。そのうえで、もっと大きなコードと、人による設計の評価を足したいと考えています。

構成を固定して言語を変える(Python・TypeScript・Go・Rust)第 2 弾も計画していますが、まだ始めていません。


構成ごとのファイル、ハーネスの作り、54 回分の表、失敗の中身は、技術編の「AI に同じ変更を 54 回させて、設計ごとの差を測る」に書いています。