技術公開日 更新日

【Laravel→TS移行記】Next.jsのAIハーネスで責任のあるコードを生成する

はじめに結論:AI(Claude)に渡すルールファイルの構成

先に、実際のファイル構成を示します。

CLAUDE.md                     Claude Codeでセッション開始時に必ず読まれる「振る舞いの契約」
.claude/rules/
  architecture.md             層の責務と依存の向き
  naming.md                   ディレクトリ・ファイル・関数・型の命名
  database.md                 Prisma / スキーマ / マイグレーション
  security.md                 認証・認可・秘密情報(常時読み込み)
  testing.md                  何をテストし、何をテストしないか
docs/
  product.md                  何のためのサイトか・誰に向けたものか
  backlog.md                  やること / 決めることの一覧
  design.md                   カラー変数・コンポーネントスタイル

ポイント

  • CLAUDE.md は指針の概要。詳細は .claude/rules/ に分割する
  • .claude/rules/ は自動で読まれないので、「いつ読むか」をトリガー表で指定する
  • セキュリティだけは常時読み込む
  • 守らせたいものはテストに落とす

はじめに:責任のあるコードのためにハーネスは必須

前回、Next.js 単独開発のための規約を決めました。今回はそれをAIコーディングするためのルール(ハーネス)に落とし込んだものを公開します。

2026年9月現在の生成AIでのコーディング事情は、かなり信頼性のあるコードを生成できるようになってきたものの、ハーネスがしっかりしていなければまだ無秩序でエンジニアとして責任が持てないようなコードが生成される状況です。

つまり、AIによってかなり実装の速度や精度は上がってきたものの、自分が読み書きでき、いざとなれば自分で修正できるコードを生成するには一定のルール(ハーネス)を指定してしっかりとAIをコントロールしなければいけません。

AI に書かせたコードの責任を取るのは自分です。障害が起きたときに「AI が書いたので分かりません」とは言えません。そのために必要なのが、出力を自分の理解できる形に揃える仕組みでした。それが前回作った規約そのものだった、という話になります。

今回は、Claude Code利用時に実際に使っているファイルをそのまま公開します。

CLAUDE.md は指針の要約を書く

CLAUDE.md は指針の要約を書き、詳細は .claude/rules/ に分割します。
CLAUDE.md は命令毎に全文送られるので、内容を短くして参照先ファイルを指定しておきます。例えば CLAUDE.md には命名規則は .claude/rules/naming.md を参照する、などを書いておくわけですね。

.claude/rules/ 配下は自動では読み込まれません。 だから CLAUDE.md に「いつ読むか」のトリガー表を書いています。

読むファイル 読むタイミング(トリガー)
architecture.md 層をまたぐコードを書くとき
naming.md ファイル・関数・型を新規に命名するとき
database.md schema.prisma を触るとき / マイグレーションを作るとき
testing.md テストを書くとき
docs/product.md 機能や画面を作る前
docs/career.md 経歴ページの本文を書くとき(ここに無い経歴を創作しない)

あわせて、この2行を書いています。

実装を書き始めてから読むのでは遅い。
「だいたい分かっているから読まない」は禁止。トリガーに該当したら必ずファイルを開く。

AI は「だいたい分かっている」で進みがちです。明示的に禁止しておかないと、規約を読まないまま書き始めます。

なお、セキュリティ規約だけは読み忘れが事故に直結するので、トリガー方式にせず @.claude/rules/security.md と書いて常時読み込ませています。「今回の作業はセキュリティに関係ないから読まなくていい」という判断自体をさせない、という意図です。

要約と詳細が食い違ったときのルール

分割すると、CLAUDE.md の要約と .claude/rules/ の詳細がズレる可能性が出ます。これも先回りして書いておきます。

このファイルの記述は要約である。詳細ルールと食い違う場合は `.claude/rules/` 側を正とし、
食い違いを見つけた時点で報告すること。

どちらが正かを決めておけば AI は迷いません。そして「報告すること」と書いておくと、私が規約を直すきっかけにもなります。

AI向けに効いている書き方

迷ったら止めて確認させる

ファイルの先頭と作業方針の両方に書いています。

規約に反する実装をしそうになったら、実装を止めて確認を取ること。
「とりあえず動く」より「規約に沿って・境界を守って」を優先する。
破壊的変更や新技術導入の提案は、理由と代替案を添えて確認を取ること。

AI は良かれと思って勝手に判断し、そのまま突き進みます。判断が必要になったら止まらせるのが一番効きます。

技術スタックは「選定済み・変更しない」と断言する

新しいライブラリの追加や技術の差し替えを提案する前に、必ず理由を述べて確認を取ること。

これを書かないと、AI は良かれと思って別のライブラリを導入します。気づいたら知らない依存が増えている、という事態を防ぐためです。

一次情報の場所を指定する

不確かなライブラリのバージョン固有挙動(Prisma / Next.js / Better Auth 等)は、
憶測で書かず一次情報を確認すること。
インストール済みバージョンと一致した一次情報がローカルにある。Web より先にここを読む:
  - Prisma → `.agents/skills/prisma-*/`
  - Next.js → `node_modules/next/dist/docs/`

AI は学習データの知識で書くので、バージョンが古いコードを平気で出してきます。たとえば Prisma v7 では driver adapter が必須で、@prisma/client からの import ではなく生成先パスからの import が正しいのですが、指示しないと v6 の書き方が出てきます。

「Web を検索して」ではなく「ローカルのこのパスを読め」と書くのがポイントです。インストール済みバージョンと確実に一致した情報なので、ネット上の古い記事に引きずられません。

このシリーズで何度も出てきた「新旧ツールチェーンの噛み合わなさ」が、AI コーディングでも同じ形で現れます。人間が古い記事を読んで混乱するのと同じことを、AI も学習データでやるわけです。

規約の中身で特にAIに効くもの

入口は2本ある、と図で示す

前回の記事では「page.tsx は薄いコントローラ」と書きましたが、実際の規約では入口が2本ある構造として整理し直しました。

[入口 1] Server Component から          [入口 2] Client Component から
app/**/page.tsx                        app/**/*.tsx("use client")
    │                                       │
    │ 関数呼び出し                           │ ネットワーク越し(POST)
    │ (同一プロセス内)                      ↓
    │                                  modules/<feature>/*.action.ts
    │                                       │
    └──────────────┬────────────────────────┘
                   ↓
            *.usecase.ts
                   ↓
            *.repository.ts
                   ↓
              Prisma / DB

page.tsx は action を経由せず、usecase を直接呼びます。 理由も規約に書いています。

`"use server"` を付けたファイルの export は、単なる関数ではなく ID を持った POST エンドポイントになる。
`page.tsx` はすでにサーバー上で動いているので、エンドポイントを経由する理由がない。
経由しても得るものはなく、「サーバー内部だけの読み取りが外から叩ける」副作用だけが増える。

action は「層」ではなく「クライアントからサーバーへ入るときの入口」である、という整理です。ここを曖昧にすると、AI は読み取りまで action 経由にしたり、逆に Client Component から usecase を直接呼ぼうとしたりします。

命名で置き場所が自動的に決まる

層ごとに関数の語彙を変えると決めています。

層 語彙 例
Server Action Action サフィックス addTodoAction
usecase 業務の言葉 addTodo / listTodos
repository データ操作の言葉 create / findById
resource to + 型名 toTodoResource

これを渡しておくと、「Todo を追加する処理を作って」と頼んだだけで、addTodoAction(入口)→ addTodo(業務)→ create(DB)という構造が自動的に再現されます。判断の余地をなくすほど、出力が安定します。

「必ず作る」「作らない」を断言する

void を返さない usecase には、対応する Resource を必ず作る。
中身が既存の Resource と同一でも省略しない。
void を返す usecase(toggle / delete 等)には Resource を作らない。

「重複しても必ず作る」は DRY に反して見えます。でも AI コーディングでは、予測可能性(いつも同じ構造)のほうが重複の排除より価値がある場面が多いです。「usecase があれば Resource もある」と決まっていれば、私も「あるはず」の前提で読めます。

事故を構造で防ぐ

安全側に倒した規約もいくつか入れています。

`modules/<feature>/` に `.tsx` を置かない。サーバー層専用とする。
理由は縦切りの一貫性ではなく安全側の判断で、`todo.repository.ts` の隣にクライアントコンポーネントがあると、
Prisma をクライアントバンドルへ引き込む import を書きやすい。

Next.js はエラーで止めてくれますが、そもそも隣に置かないほうが安全です。AI は近くにあるファイルを参照しがちなので、物理的に離しておくのは有効でした。

文章で守らせるには限界がある

ここまでルールファイルの話をしてきましたが、正直に言うと文章の指示だけでは限界があります。

規約に「所有権チェックを必ず書け」と書いても、AI が書き忘れることはあります。長いセッションの途中で規約が薄れることもある。文章は「守ってほしい」というお願いであって、強制力がありません。

だから、絶対に守らせたいものはテストに落とします。

規約は「文脈」であり「強制」ではない。強制したいものはテストに落とす。

必ずテストで固定すると決めているのは、所有権チェック(IDOR 対策)とバリデーションです。ポイントは「拒否されること」だけでなく「副作用が発生していないこと」まで確認する点です。

it("他人の todo なら例外を投げ、DB は変更されない", async () => {
  const created = await prisma.todo.create({
    data: { text: "タスク", done: false, userId: "user_1" },
  });

  await expect(
    todoUsecase.toggleTodoDone("user_2", created.id)
  ).rejects.toThrow();

  // DB が変更されていないことまで確認する
  const unchanged = await prisma.todo.findUnique({ where: { id: created.id } });
  expect(unchanged?.done).toBe(false);
});

例外が投げられても、その前に update が走っていたら意味がありません。文章は破れますが、テストは破れません。

AIが書いたコードに責任を持つということ

最後に、冒頭に書いたことに戻ります。

規約を整備して実感したのは、規約があるとレビューが速くなるということでした。「この関数は usecase なのに DB を直接触っている」「Resource が無い」といった逸脱が、構造を知っているから一目で分かる。

逆に、規約なしで AI に書かせたコードは読むのに時間がかかります。毎回違う構造なので、毎回読み解く必要がある。それでは「速く実装できた」分を、読む時間で相殺してしまいます。

AI に速く書かせるためではなく、AI が書いたものを自分が速く読めるようにするために、規約がある。 これが実際に運用してみての実感です。

そして規約は一度作って終わりではありません。AI が「要約と詳細が食い違っています」と報告してきたり、「この場合はどうしますか」と止まってきたりするたびに、規約の穴が見つかります。AI に規約を渡すことは、自分の規約を検証してもらうことでもあると感じています。

まとめ

論点 結論
ファイル構成 CLAUDE.md は要約とルーター、詳細は .claude/rules/ に分割
読ませ方 自動で読まれないのでトリガー表で指定。セキュリティは常時読み込み
書き方 「最優先」「迷ったら止める」「変更しない」「一次情報はローカル」
命名規約 名前で置き場所が決まる=AI に判断させない
文章の限界 守らせたいものはテストに落とす(文章は破れる、テストは破れない)
責任 規約は AI の制御だけでなく、自分が速く読むための道具

規約と AI があれば、1人でも一貫した品質を保ったまま速く作れます。
このAIハーネスを作り、実際にAI駆動でシステムを作ってみて、これからのシステム開発や保守運用は、技術・ビジネスに明るいエンジニアが1人いれば十分に回ると実感しています。

みなさんもAIコーディングで無責任なコードを量産するのではなく、AIを使いこなして責任あるコードを生成できるエンジニアでありましょう。

← 記事一覧に戻る