Claude Code

AI開発で人間が勉強すべきはアーキテクチャだった|7,366行のファイルができた話

AI開発をしていて一番効いてきたのは、プロンプトの書き方ではなくアーキテクチャ(設計)の知識でした。

AIに任せて作っていたツールのファイルが、気づいたら7,366行になっていたんです。 動いてはいる。でも、ここまで大きくなると、直すのをAIに頼むのも難しくなってきます。

そこから「人間側が何を決めておくべきだったか」を考え直して、上限ルール・ディレクトリ構成・ADRの3つに落ち着きました。この記事はその記録です。


AIに任せたコードが、7,366行のファイルになっていた

SF6のリプレイを解析するツールを、Codexに書かせながら作っていました。 リプレイ動画からフレームを切り出して、コマンド入力やダメージを読み取るやつです。

やり方はシンプルで、「こういう判定を足して」「ここのバグを直して」と頼み続けるだけ。実際それで動くものができていました。

問題が見つかったのは、まったく別の作業をしていたときです。

6月の頭、READMEが古いまま放置されているのが気になって、ドキュメントのルールを整備していました。その流れで「コードにも一応ルール入れとくか」と思って、最大行数の上限だけ決めてみたんです。値はClaudeのおすすめをそのまま使いました。

その基準で自分のコードを見にいったら、これが出てきました。

step2_fix/builder.py : 7,366 行

git の履歴を追い直すと、こうなっていました。

時点builder.py の行数
2026-06-04(Git管理に入れた初日)6,902行
2026-06-057,366行(ピーク・ここで気づいた)
2026-07-17603行

Git に入れた初日から、すでに6,900行あった。つまりそれ以前がどうだったかは記録すら残っていません。

さすがにヤバいと思いました。

Kana
Kana

7000行って、開いた瞬間スクロールバーが消えるやつ

Lyze
Lyze

このあと決めた上限が1ファイル800行なので、その9倍ですね。しかも中身は判定ロジックの塊です

同じツールの別のファイルには、376行・引数14個の関数もありました。引数14個って、もう呼び出す側も何を渡しているかわからない状態です。


AIが悪いんじゃない。止める基準を渡していなかった

ここで大事なのは、Codexは指示通りに動いていたということです。

「この判定を足して」と言えば足す。「ここを直して」と言えば直す。 毎回きちんと動くコードを返してきました。

ただ、そのお願いを積み重ねた結果、1つのファイルが7,366行になった。それだけの話です。

AIは「そろそろこのファイル大きすぎませんか?」とは言ってくれません。言われていない基準で勝手に止まることはない。 止める基準を渡していなかったのは、こちら側でした。

git の履歴を見ると、そこから1ヶ月半のあいだに Extract... Move... Split... から始まるコミットが131件並んでいます(このツールの配下だけで数えた数です)。積み上げたのもCodexなら、削っているのもCodex。同じAIが、渡すルールを変えただけで正反対の仕事をしています。

この件をきっかけに、新規開発をやめて保守改善にシフトしました。 機能を増やすより、増やせる状態に戻すほうが先だと思ったからです。

AIに任せたコードが肥大化していく流れ

以下は、そのときに決めた3つのルールです。どれも大がかりなものではありません。


①コードの行数上限を決めて、機械に測らせる

まず数字を決めました。Claudeに聞いたおすすめの値を、そのまま採用しています。

対象上限
1ファイル800行
1関数150行
関数の引数8個

加えて600行を超えたら警告線として、そのファイルに新しい責務を足す前に分割方針を決める、というルールにしました。

ここで重要なのが、すでに違反しているコードをどう扱うかです。7,366行のファイルを抱えたまま「800行超えは禁止」にしたら、その日から何も触れなくなります。

なので、既存の違反は baseline というファイルに現在値を記録して、新しい違反だけを弾く方式にしました。一部を抜粋するとこんな形です。

{
  "limits": {
    "file_lines": 800,
    "python_function_lines": 150,
    "python_function_args": 8
  },
  "file_lines": {
    "_data-jobs/lol/scripts/database.py": 1214,
    "_pipelines/comfyui/ui.py": 857
  }
}

baselineの数値は常に現在値まで縮小するだけで、増やすことは禁止。つまり借金の残高表みたいなもので、減ることはあっても増えることはありません。

ルールは「読ませる」より「機械に測らせる」

ここが一番の学びでした。

ルールをドキュメントに書いても、AIが毎回きちんと読むとは限りません。なので見張りは機械に任せることにしました。ただし1段ではなく、2段構えです。

担当見るものタイミング
1段目Claude Code の hooks書き方の lint(未使用変数・import順など)AIがファイルを編集した直後
2段目CI(GitHub 側)行数・関数の長さ・引数の数コードを触った push のたびに全ファイル走査

1段目の hooks は、.claude/settings.jsonhooks.PostToolUse に設定します。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{ "type": "command", "command": "... hook_lint.py" }]
      }
    ]
  }
}

やっていることは単純で、AIがファイルを書き換えた直後に ruff(Pythonの静的チェックツール)を走らせるだけ。違反があれば exit code 2 で内容がAI側に差し戻され、その場で直させます(編集が取り消されるわけではなく、即時のフィードバックです)。スクリプト本体は53行しかありません。

そして行数の上限そのものを測るのは2段目のCIです。専用のチェッカーが全ファイルを走査して、baselineと比較する。push のたびに動くので、ここが最終防波堤になります。

分けている理由は単純で、編集のたびに全ファイルの行数を数えると重いからです。即時に効かせたいものと、まとめて確認すればいいものを分けました。

お願いベースをやめて、機械に測らせる。 これだけで守られ方がまったく変わりました。

ただ、機械で測れるのは行数のような数えられるものだけです。数えられない方針のほうは、rules として読ませています。編集するファイルの種類に応じて自動で読み込まれる規範です。うちでは6本運用していて、Pythonを触るときはこんな内容がロードされます。

- 手書きコードの上限は 1ファイル800行 / 1関数150行 / 引数8個
- 600行超は警告線。次の責務を追加する前に分割方針を決める
- 巨大な関数を別ファイルへ移すだけの分割は禁止する

3つ目の「移すだけの分割は禁止」は先回りで入れています。7,000行を2つに割っても、責務が混ざったままなら何も解決しないからです。


②ディレクトリ構成(レイヤード構造)を先に決める

次に、どこに何を書くかを決めました。

正直に言うと、ここは勉強してから知ったことがほとんどです。

自分はもともと仕事で簡単なスクリプトを書いて自動化するくらいの人間でした。Python、VBA、Power Automate。DBもSalesforceやAccessでQueryを叩けるくらい。オブジェクト指向とか層を分けるとか、なんとなく直感でやってはいたんです。

で、調べてみたらそれには「レイヤード構造」という名前がついていた。

レイヤード構造の4つの層(presentation / application / domain / infrastructure)と役割

層は4つ、守るルールは1つだけ

役割
presentation入出力の入口(CLI・画面・APIハンドラ)
applicationユースケースのフロー制御
domainそのツールの中心になるルール。どこにも依存しない
infrastructure外部API・DB・ファイル・OS

名前は4つありますが、絶対に守るルールは1つだけにしました。

domain/(中心のルール)は、他の層を import しない。

うちの場合だと、コマンド判定やダメージ計算のロジックがここに当たります。「このツールがやりたいことの本体」だけを置く場所、という感覚です。

この一方向さえ守られていれば、中心のロジックをDBもUIも無しで単体テストできます。逆に言うと、ここが崩れると「この計算を直したいだけなのにDB接続が要る」みたいな状態になります。

そしてもう一つ大事なのが、作っていない層は飛ばしていいということ。

新規で始めるときは domain/infrastructure/ と薄い入口ファイルだけ。application/ が無いなら入口が domain/ を直接呼んで構いません。層を飛ばさないためだけの素通しラッパーを作るのは、ただの遠回りです。

# 最初はこれだけでいい
src/<package>/
├── domain/           # 中心のルール(外部I/Oをimportしない)
├── infrastructure/   # 外部API・DB・ファイル
└── cli.py            # 入口(薄い。ロジックを書かない)

層を増やすのは、必要になってからです。1つの操作が複数の処理やデータをまたぎ始めたら application/ を足す。入口がCLIとAPIの2つになったら presentation/ を足す。先回りして空のフォルダを作らない。

DDDは面白かったけど、全部は入れていない

調べる過程でDDD(ドメイン駆動設計)にも触れました。これがすごく面白かった。

ただ、EntityとかValue ObjectとかRepositoryとか、戦術的なパターンまで全部入れるのは、自分の規模だと設計コストのほうが勝つと判断しました。

なので**「面白かったので簡易導入してみた」くらいの距離感**で止めています。使うのは複雑になってきた部分だけ。単純なところは素直に書く。

DDDで一番効いたのは、パターンそのものより**「境界をどこに引くか」を先に考える**という発想でした。


③ADRで「なぜ」を残すと、AIが壊しにこない

3つ目が地味に一番効いています。

AIはセッションが変わると、それまでの経緯を全部忘れます。だから前回さんざん議論して決めたことを、次のセッションで良かれと思って壊しにきます。

「こっちの方がシンプルなので変えましょう」と提案してくる。悪意はゼロです。ただ、なぜそうしなかったかを知らないだけ。

これを防ぐのが ADR(Architecture Decision Record) です。決定を短いMarkdownで残しておくだけ。

ちなみに、さっきの解析ツールは層ではなく処理のSTEPごとにフォルダを分けています(①に出てきた step2_fix/ がそれです)。そのSTEP境界について書いたADRを、一部抜粋するとこんな形です。

# ADR-0001: STEP0-3 の境界を維持する

## Status
Accepted

## Context
解析は「録画 → RAW抽出 → FIX補正 → 出力」を順に実行する。各STEPは
失敗箇所を切り分けるためのデバッグ境界でもある。AI支援で一括自動化や
統合リファクタを進めると、この境界が曖昧になり、どこで差分が出たか
追いにくくなる。

## Decision
STEP0〜3は個別に実行・検証できる境界として維持する。まとめ実行は
薄い呼び出しに留め、各STEPの実装責務を吸収しない。

## Consequences
- STEPごとの入力・出力・ログ・テストを個別に確認できる
- 保守リファクタでも責務を混ぜない判断基準になる

これがあれば「STEP を1つにまとめた方がシンプルですよ」という提案が来ても、AI自身がこのファイルを読んで判断できる——というのが狙いです。まとめた方が見た目はきれいになる。でも、そうするとどこで壊れたのかが分からなくなる。その理由が書いてあるかどうかの差です。

ポイントは置き場所で、docs/adr/ に置いただけでは読まれません。READMEの「構造」の節から docs/adr/ へリンクしておくのが大事です。AIは作業前にREADMEを読むので、そこから辿ってくれます。

書くべき決定の基準はシンプルです。

  • 後で「なぜ?」と聞かれそうなもの
  • 一度やると変えにくいもの
  • AIが次回、誤って変更提案してきそうなもの

3つ目が個人開発では一番実用的だと思います。うちは今6本あって、それで足りています。

Kana
Kana

未来の自分とAIに向けた置き手紙


エンジニアは諦めてた。でも設計がわかると、指示が変わった

ここからが、この記事で一番書きたかったことです。

仕事はPMっぽい業務がメインで、エンジニアになることは正直あきらめていました。独学でスクリプトは書けるけど、それは業務効率化の範囲で、システムを設計する側ではない。ずっとそう思っていました。

でも今回のことで感覚が変わりました。

エンジニアに指示するくらいの内容を把握できていれば、あとは全部ClaudeとCodexが手足として動いてくれる世界になっていた。

GitHubの運用ルールを決めて、ClaudeとCodexで分担して開発させる。ディレクトリ構成やDBの設計をどうするか考える。そういう構成をコードを書かずに考えて指示できるのが、単純に楽しい。

ただし、指示の仕方を間違えると、あんまりいい仕事をしてくれません。 7,366行のファイルが、まさにその結果です。

だからこそ基盤整備の大切さを痛感しました。ここがAI時代の人材の差になってくるんだろうなと思っています。

もうひとつ面白いのが、ClaudeやCodexと会話しながら進めると、自分自身の知識も増えていくこと。増えた知識で次はもっと的確な指示が出せるようになる。再帰的にレベルが上がっていく感じがあります。

独学で直感的にやっていたことに、ちゃんと名前と理論があった。それを知ったうえで指示すると、返ってくるものが変わる。この循環がいま一番面白いところです。

Lyze
Lyze

設計を学ぶと、AIへの指示が「作って」から「この境界で作って」に変わりますね


まとめ|AI開発で人間が勉強すべきこと

今回の件で痛感したのは、これです。

開発の進め方がある程度わかってこそ、AIを真に生かせる。

全部をAIに任せるのではなく、人間側がやりたいことを明確にして、ルールや規範を設定する。そこが要でした。

やったことを整理すると3つだけです。

  • ①行数の上限を決めて、機械に測らせる — 800行/150行/引数8個。既存違反はbaselineに記録して新規だけ弾く。lintはhooksで即時、行数の上限はCIで全走査
  • ②ディレクトリ構成を先に決める — 層は4つ、絶対ルールは「domain/ は他層をimportしない」の1つだけ。使わない層は作らない
  • ③ADRで「なぜ」を残す — 10〜15行。AIが次回誤って壊しにくるのを防ぐ

そしてこれらは、どれもコードを書く知識ではありません。 どう分けるか、何を禁止するか、なぜそうしたかを決める仕事です。プログラミングそのものより、こっちを勉強したほうがAI開発では効きました。

将来的には、このルール設定自体もAIがやる形になるのかもしれません。それでも「何を守らせたいか」を決めるのは、しばらく人間の側だと思っています。

よくある質問

Q. 最初からきれいな設計にすべきだった?

設計そのものを作り込む必要はないと思います。層をどう分けるかは、作りながら決めても間に合いました。

ただ、ルールづけだけは最初にやったほうがいいというのが今の結論です。少なくとも行数の上限は、最初に決めておくべきでした。決めるのは一瞬で済むのに、超えてから気づくと今回のように1ヶ月半かけてもまだ終わらない分割作業になります。設計を先に固めるのではなく、壊れたことに気づける線だけ先に引いておく、という順番だと思っています。

Q. AIに設計もやらせればいいのでは?

設計案を出させるのはすごく有効です。うちの上限値(800行/150行/引数8個)も、Claudeのおすすめをそのまま使っています。ただ、「その案を採用する」と決めるのは人間の仕事でした。決めずに進めると、AIは毎回違う最適解を出してきて、コードベースが少しずつバラバラになります。


なお、問題のファイルは603行まで落ちましたが、周辺のファイルはまだ途中です。いまもCodex側で削っている最中で、終わったら数字を更新します。

次はGitHubの運用側——ClaudeとCodexをどう分担させて、人間がどうマネジメントしているか——を書く予定です。

気になる人はXでも感想聞かせてください。

この記事は役に立ちましたか?

読みたい記事をリクエストする

シェア