Claude Code で Spring Boot を開発するときの要点は、「版とコマンドを CLAUDE.md に書き、ファイルの種類ごとの決まりは .claude/rules/ に分け、必ず起きてほしいことは settings.json の権限とフックで決める」の3段構えにすることです。 Java のプロジェクトは Controller・Repository・テスト・設定ファイルで決まりごとが違うため、1枚の CLAUDE.md に全部を書くより、分けたほうが短く保てて指示も通りやすくなります。

この記事では、Spring Boot 4.1 系・Java 21 を前提に、CLAUDE.md の全文(Gradle 用・Maven 用)、paths 付きのルール4本、権限とフックを入れた settings.json、整形フックのスクリプトをコピペできる形で載せます。あわせて、Java 開発の項目20件を「どこに書くか」で振り分けた表と、あいまいな指示を検証できる形に直した例10組もまとめました。

確認環境:Spring Boot 4.1.1 の公式リファレンス(docs.spring.io)と Claude Code Docs(code.claude.com/docs)を2026年10月7日に開いて確認。設定ファイルの構文とフックスクリプトの動作は、Linux 上の bash 5.2.21・jq 1.7・Python 3.11.15(PyYAML 6.0.3)で実行して確かめています。

設定一式の早見表

ファイル 書くこと 読み込まれる時
CLAUDE.md 版、コマンド、構成、禁止事項 毎回のセッション開始時
.claude/rules/*.md ファイルの種類ごとの決まり paths に合うファイルを読み書きした時
.claude/settings.json 許可・確認・拒否するコマンドやパス 常に(Claude Code が適用する)
.claude/hooks/*.sh 編集のたびに必ず走らせる処理 設定したイベントの時
.claude/skills/*/SKILL.md 手順の長い作業 呼び出した時
決めること この記事での設定
前提の版 Spring Boot 4.1.x、Java 21
テスト1件の実行 ./gradlew test --tests クラス名 / ./mvnw test -Dtest=クラス名
確認なしで許可 テスト、コンパイル、整形、ビルド
毎回確認 アプリの起動、git push
拒否 .env の読み書き、本番用 yml の編集
編集後の自動処理 .java を編集したら Spotless で整形

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Claude CodeでSpring Boot開発|CLAUDE.md例と設定一式」

置き場所の全体像

your-project/
├── CLAUDE.md                     プロジェクト全体の指示(200行以下)
├── build.gradle                  (Maven なら pom.xml)
├── .claude/
│   ├── settings.json             権限とフック
│   ├── hooks/
│   │   └── format-java.sh        編集後に整形するスクリプト
│   └── rules/
│       ├── controller.md         **/*Controller.java 用
│       ├── repository.md         **/*Repository.java 用
│       ├── testing.md            src/test/** 用
│       └── config.md             application*.yml 用
└── src/
    ├── main/java/com/example/inventory/...
    ├── main/resources/application.yml
    └── test/java/com/example/inventory/...

CLAUDE.md は、すべてのセッションの開始時に読み込まれます。公式の目安は 1ファイル200行以下で、長くなるほどコンテキストを使い、指示が守られにくくなります。コードベースの一部だけに関係する指示は、paths 付きのルールに移すと、該当するファイルを扱うときだけ読み込まれます。

CLAUDE.md に Spring Boot の版を明記する理由

Spring Boot は 3 系と 4 系で、パッケージ名や starter 名、テスト用アノテーションが変わっています。版を書いておくと、Claude が別の系列の書き方を混ぜずに済みます。4.0 の移行ガイドに載っている主な違いです。

項目 Spring Boot 4 系での扱い
土台 Spring Framework 7 系、Jakarta EE 11、Servlet 6.1
Java 17 以上
Web の starter spring-boot-starter-web は spring-boot-starter-webmvc に
AOP の starter spring-boot-starter-aop は spring-boot-starter-aspectj に
JSON Jackson 3 が標準。グループ ID は tools.jackson
テストのモック @MockBean・@SpyBean は削除。@MockitoBean・@MockitoSpyBean を使う
モジュール構成 小さなモジュールに分割。必要な starter を明示する
null 安全 JSpecify のアノテーションを採用
組み込みサーバー Undertow の対応を終了(Tomcat 11.0・Jetty 12.1)

サポート期限と前提(3.5・4.0・4.1)

サポート期限は Spring 公式 API(api.spring.io)の値、前提は各系列のリファレンスの System Requirements の値です。いずれも2026年10月7日に確認しました。

系列 OSSサポート終了 商用サポート終了 Java Servlet
3.5.x 2026-06-30 2032-06-30 17〜25 6.0
4.0.x 2026-12-31 2027-12-31 17〜26 6.1
4.1.x 2027-07-31 2028-07-31 17〜26 6.1
系列 確認時の版 Spring Framework Gradle Maven
3.5.x 3.5.16 6.2.19 以上 7.6.4 以上/8.4 以上 3.6.3 以上
4.0.x 4.0.8 7.0.9 以上 8.14 以上/9.x 3.6.3 以上
4.1.x 4.1.1 7.0.9 以上 8.14 以上/9.x 3.6.3 以上

spring.io のプロジェクトページに現行として表示されているのは 4.1.1 です。同じ API には、次の 4.2.x が 2026-11-30 の初回リリース予定として載っています。系列ごとに期限と前提が違うので、CLAUDE.md には「4.1.x」のように系列まで書くのがおすすめです。系列を上げたら、CLAUDE.md の版の行も同じコミットで直します。

CLAUDE.md の全文(Gradle 用)

架空の「備品管理 API」を題材にした全文です。プロジェクト名、パッケージ名、DB などを自分のものに書き換えて使ってください。

# 備品管理 API

社内の備品(PC・モニターなど)の貸出を管理する REST API。

## 技術スタック(版は必ずこのとおりに扱う)

- Java 21(`build.gradle` の toolchain で固定)
- Spring Boot 4.1.x(Spring Framework 7 系、Jakarta EE 11、Servlet 6.1)
- ビルド: Gradle(必ず同梱の `./gradlew` を使う。`gradle` コマンドは使わない)
- DB: PostgreSQL 16、マイグレーションは Flyway(`src/main/resources/db/migration`)
- テスト: JUnit Jupiter、AssertJ、Mockito(`spring-boot-starter-test` に含まれるもの)
- 整形: Spotless(google-java-format)

Spring Boot 4 系で書き方が変わった点。3 系の書き方を混ぜない。

- import は `jakarta.*`。`javax.persistence` や `javax.validation` は使わない
- Web の starter は `spring-boot-starter-webmvc`
- JSON は Jackson 3。パッケージは `tools.jackson`(`com.fasterxml.jackson.databind` ではない)
- テストのモックは `@MockitoBean` / `@MockitoSpyBean`(`@MockBean` は 4.0 で削除済み)
- `@WebMvcTest` は `org.springframework.boot.webmvc.test.autoconfigure` にある

## コマンド

| 目的 | コマンド |
|---|---|
| コンパイルだけ | `./gradlew compileJava compileTestJava` |
| テスト1クラス | `./gradlew test --tests EquipmentServiceTest` |
| テスト1メソッド | `./gradlew test --tests EquipmentServiceTest.rejectsWhenAlreadyLent` |
| パッケージ単位 | `./gradlew test --tests 'com.example.inventory.service*'` |
| テスト全体 | `./gradlew test` |
| 整形 | `./gradlew spotlessApply` |
| 整形の確認 | `./gradlew spotlessCheck` |
| ビルド一式 | `./gradlew build` |
| ローカル起動 | `./gradlew bootRun --args='--spring.profiles.active=local'` |

- 変更のたびに回すのは「テスト1クラス」。全体のテストは作業の最後に1回だけ回す
- テストが失敗したら、失敗したテスト名とメッセージをそのまま引用して報告する

## パッケージ構成

ルートパッケージは `com.example.inventory`。

- `web`: Controller と、リクエスト/レスポンスの record
- `service`: 業務ロジック。トランザクションの境界はここ
- `domain`: エンティティと値オブジェクト
- `repository`: Spring Data JPA のリポジトリ
- `config`: 設定クラス、`@ConfigurationProperties`

- 依存の向きは web → service → repository。逆向きの import を作らない
- Controller から Repository を直接呼ばない。必ず Service を通す
- エンティティを API のレスポンスにそのまま返さない。`web` の record に詰め替える

## コーディング規約(既定と違うものだけ)

- DI はコンストラクタインジェクション。フィールドへの `@Autowired` は使わない
- DTO は `record`。Lombok はこのリポジトリでは使っていない
- `Optional` は戻り値にだけ使う。引数とフィールドには使わない
- 日時は `java.time`。`java.util.Date` は使わない。タイムゾーンは UTC で保存する
- 例外は `service` で業務例外(`BusinessException` の子クラス)にして投げ、`web/GlobalExceptionHandler` で HTTP ステータスに変換する
- ログは SLF4J。`System.out.println` は使わない。個人を特定できる値をログに出さない

## 禁止事項

- `.env` と `application-prod*.yml` を編集しない(権限設定でも拒否している)
- 適用済みの Flyway マイグレーション(`V*.sql`)を書き換えない。変更は新しい番号のファイルを足す
- `build.gradle` に依存を追加する前に、理由を説明して確認を取る
- テストを通すためにテストを削除・無効化(`@Disabled`)しない
- `git push`、`git reset --hard`、ブランチの削除は実行しない

## 完了の条件

次の3つを実行し、コマンドと結果を示してから「完了」と報告する。

1. 変更したクラスのテスト: `./gradlew test --tests <テストクラス名>`
2. 整形の確認: `./gradlew spotlessCheck`
3. 全体: `./gradlew build`

失敗が残っている場合は、完了とせず、失敗しているテスト名と原因の見立てを報告する。

CLAUDE.md の全文(Maven 用)

Maven を使うプロジェクト向けです。コマンドの表と、完了の条件、.mvn/ の扱いが Gradle 用と違います。

# 備品管理 API

社内の備品(PC・モニターなど)の貸出を管理する REST API。

## 技術スタック(版は必ずこのとおりに扱う)

- Java 21(`pom.xml` の `java.version` で固定)
- Spring Boot 4.1.x(Spring Framework 7 系、Jakarta EE 11、Servlet 6.1)
- ビルド: Maven(必ず同梱の `./mvnw` を使う。`mvn` コマンドは使わない)
- DB: PostgreSQL 16、マイグレーションは Flyway(`src/main/resources/db/migration`)
- テスト: JUnit Jupiter、AssertJ、Mockito(`spring-boot-starter-test` に含まれるもの)
- 整形: Spotless(spotless-maven-plugin、google-java-format)

Spring Boot 4 系で書き方が変わった点。3 系の書き方を混ぜない。

- import は `jakarta.*`。`javax.persistence` や `javax.validation` は使わない
- Web の starter は `spring-boot-starter-webmvc`
- JSON は Jackson 3。パッケージは `tools.jackson`(`com.fasterxml.jackson.databind` ではない)
- テストのモックは `@MockitoBean` / `@MockitoSpyBean`(`@MockBean` は 4.0 で削除済み)
- `@WebMvcTest` は `org.springframework.boot.webmvc.test.autoconfigure` にある

## コマンド

| 目的 | コマンド |
|---|---|
| コンパイルだけ | `./mvnw -q test-compile` |
| テスト1クラス | `./mvnw test -Dtest=EquipmentServiceTest` |
| テスト1メソッド | `./mvnw test -Dtest=EquipmentServiceTest#rejectsWhenAlreadyLent` |
| 複数メソッド | `./mvnw test -Dtest=EquipmentServiceTest#rejects*+returns*` |
| テスト全体 | `./mvnw test` |
| 整形 | `./mvnw spotless:apply` |
| 整形の確認 | `./mvnw spotless:check` |
| ビルド一式 | `./mvnw verify` |
| ローカル起動 | `./mvnw spring-boot:run -Dspring-boot.run.profiles=local` |

- 変更のたびに回すのは「テスト1クラス」。全体のテストは作業の最後に1回だけ回す
- テストが失敗したら、`target/surefire-reports/` の該当クラスの `.txt` を読む

## パッケージ構成

ルートパッケージは `com.example.inventory`。

- `web`: Controller と、リクエスト/レスポンスの record
- `service`: 業務ロジック。トランザクションの境界はここ
- `domain`: エンティティと値オブジェクト
- `repository`: Spring Data JPA のリポジトリ
- `config`: 設定クラス、`@ConfigurationProperties`

- 依存の向きは web → service → repository。逆向きの import を作らない
- Controller から Repository を直接呼ばない。必ず Service を通す
- エンティティを API のレスポンスにそのまま返さない。`web` の record に詰め替える

## コーディング規約(既定と違うものだけ)

- DI はコンストラクタインジェクション。フィールドへの `@Autowired` は使わない
- DTO は `record`。Lombok はこのリポジトリでは使っていない
- `Optional` は戻り値にだけ使う。引数とフィールドには使わない
- 日時は `java.time`。`java.util.Date` は使わない。タイムゾーンは UTC で保存する
- 例外は `service` で業務例外(`BusinessException` の子クラス)にして投げ、`web/GlobalExceptionHandler` で HTTP ステータスに変換する
- ログは SLF4J。`System.out.println` は使わない。個人を特定できる値をログに出さない

## 禁止事項

- `.env` と `application-prod*.yml` を編集しない(権限設定でも拒否している)
- 適用済みの Flyway マイグレーション(`V*.sql`)を書き換えない。変更は新しい番号のファイルを足す
- `pom.xml` に依存を追加する前に、理由を説明して確認を取る
- テストを通すためにテストを削除・無効化(`@Disabled`)しない
- `.mvn/` 以下(Maven Wrapper の設定)を編集しない
- `git push`、`git reset --hard`、ブランチの削除は実行しない

## 完了の条件

次の3つを実行し、コマンドと結果を示してから「完了」と報告する。

1. 変更したクラスのテスト: `./mvnw test -Dtest=<テストクラス名>`
2. 整形の確認: `./mvnw spotless:check`
3. 全体: `./mvnw verify`

失敗が残っている場合は、完了とせず、失敗しているテスト名と原因の見立てを報告する。

2つのファイルの行数です。どちらも200行の目安に対して余裕があります。

$ wc -l CLAUDE.md CLAUDE.maven.md
  78 CLAUDE.md
  79 CLAUDE.maven.md
 157 total

このテンプレートの組み立て方

公式のベストプラクティスにある「含める/除外する」の考え方に沿っています。

含める 除外する
Claude が推測できないコマンド コードを読めばわかること
既定と違うコードスタイル Claude がすでに知っている言語の標準規約
テストの指示と使うランナー 詳しい API ドキュメント(リンクで足りる)
ブランチ名や PR の決まり 頻繁に変わる情報
プロジェクト固有の設計判断 長い説明やチュートリアル
必須の環境変数など環境の癖 ファイルごとのコードの説明
よくある落とし穴 「きれいなコードを書く」のような自明なこと

Spring Boot のプロジェクトで特に効くのは次の3点です。

  • 単体テスト1件だけを実行するコマンドを書く。 Gradle は ./gradlew test --tests クラス名、Maven は ./mvnw test -Dtest=クラス名 です。メソッド単位は、Gradle が クラス名.メソッド名、Maven が クラス名#メソッド名 と区切りが違います。
  • 「3 系の書き方を混ぜない」箇所を具体名で書く。 jakarta.*、@MockitoBean、tools.jackson のように、検索できる名前で書きます。
  • 完了の条件をコマンドで書く。 「テストする」ではなく、実行するコマンドと、結果を示すことまでを書きます。

各行について「この行を消すと Claude が間違えるか」を考え、間違えないなら消す、というのが公式の勧める見直し方です。

.claude/rules に分ける4本(paths 付き)

ルールファイルは、先頭のフロントマターに paths を書くと、そのパターンに合うファイルを Claude が読み書きするときだけ読み込まれます。Claude Code がルールのフロントマターから読むのは paths だけです。

Controller 用(.claude/rules/controller.md)

---
paths:
  - "**/*Controller.java"
---

# Controller のルール

- クラスには `@RestController` と `@RequestMapping("/api/v1/...")` を付ける。URL は複数形の名詞・ケバブケース
- 依存は `final` フィールドとコンストラクタで受け取る。Repository を直接注入しない
- リクエストボディは `record` で受け、引数に `@Valid` を付ける。検証アノテーションは `jakarta.validation.constraints` のものを使う
- 戻り値はレスポンス用の `record`。エンティティをそのまま返さない
- 作成は `201 Created` と `Location` ヘッダー、削除は `204 No Content` を返す
- Controller に業務ロジックと `try-catch` を書かない。例外は `GlobalExceptionHandler`(`@RestControllerAdvice`)に任せる
- 一覧系は `Pageable` を受け取り、件数の上限をコード側で決める
- メソッドを足したら、同じ名前の `*ControllerTest`(`@WebMvcTest`)にテストを1件以上足す

Repository 用(.claude/rules/repository.md)

---
paths:
  - "**/*Repository.java"
---

# Repository のルール

- Spring Data JPA のインターフェースとして書く(`JpaRepository<エンティティ, ID の型>` を継承)
- メソッド名から作るクエリは条件2つまで。それを超えるときは `@Query` に JPQL を書く
- `@Query` のパラメータは名前付き(`:name` と `@Param("name")`)。文字列の連結で JPQL・SQL を組み立てない
- 一覧を返すメソッドは `Pageable` を引数に取り、`Page` か `Slice` を返す
- 関連を一緒に読む必要があるときは `@EntityGraph` か `join fetch` を使い、繰り返しの中で関連を1件ずつ読み込む形にしない
- 更新系の `@Query` には `@Modifying` を付け、呼び出す Service のメソッドに `@Transactional` があることを確かめる
- Repository に業務上の判断(在庫の可否など)を書かない。判断は Service に置く
- メソッドを足したら `*RepositoryTest`(`@DataJpaTest`)にテストを足し、発行される SQL をログで確かめる

テスト用(.claude/rules/testing.md)

Maven のプロジェクトでは、実行コマンドの行を ./mvnw test -Dtest=<テストクラス名> に置き換えます。

---
paths:
  - "src/test/**"
---

# テストのルール

- テストクラス名は `対象クラス名 + Test`。メソッド名は「何をしたら・どうなる」が読める英語にする(例: `rejectsWhenAlreadyLent`)
- 構成は 準備・実行・検証 の3段。検証は AssertJ(`assertThat`)で書く
- 使い分け: Service は Spring を起動しない素の単体テスト、Controller は `@WebMvcTest`、Repository は `@DataJpaTest`、全体の結合だけ `@SpringBootTest`
- Spring の Bean を差し替えるときは `@MockitoBean`。`@MockBean` は使わない
- 現在時刻は `Clock` を注入して固定する。テストの中で `LocalDateTime.now()` を直接呼ばない
- テストデータはテストメソッドの中で作る。ほかのテストが作ったデータに頼らない
- `Thread.sleep` で待たない
- 実行は1クラスずつ: `./gradlew test --tests <テストクラス名>`。失敗したら、テストではなく実装側を先に疑う
- テストを `@Disabled` にしたり、検証を弱めて通したりしない。直せないときは理由を報告する

設定ファイル用(.claude/rules/config.md)

---
paths:
  - "src/main/resources/application*.yml"
  - "src/test/resources/application*.yml"
---

# 設定ファイルのルール

- 共通の値は `application.yml`、環境ごとの差分だけを `application-{profile}.yml` に書く
- プロファイルは `local`・`test`・`prod` の3つ。新しいプロファイルを足す前に確認を取る
- `application-prod.yml` は編集しない。変更が必要なときは、変更内容の案を文章で示す
- パスワード・API キー・接続文字列の実値を書かない。`${DB_PASSWORD}` のように環境変数を参照する
- 独自の設定は `app.` で始め、`config` パッケージの `@ConfigurationProperties` の record で受ける。`@Value` を各所に散らさない
- キーはケバブケース(`app.lending.max-days`)
- 設定を足したら、`application.yml` に既定値とコメントを書き、`src/test/resources/application-test.yml` にテスト用の値を置く

paths の書き方で気を付ける点

パターン 合うファイル
**/*Controller.java どのディレクトリにあっても、名前が Controller.java で終わるファイル
src/test/** src/test/ 以下のすべてのファイル
src/main/resources/application*.yml そのディレクトリの application.yml、application-local.yml など
application*.yml プロジェクトのルート直下にあるものだけ
src/**/*.{java,kt} 波かっこで複数の拡張子をまとめて指定

ディレクトリを含まないパターン(*.md や application*.yml)は、プロジェクトのルート直下のファイルに合います。 Spring Boot の設定ファイルは src/main/resources/ にあるので、上の config.md ではディレクトリから書いています。

paths の無いルールは、CLAUDE.md と同じようにセッション開始時に読み込まれます。4本とも paths を付けているのは、必要なときだけ読ませてコンテキストを節約するためです。

.claude/settings.json の全文(権限とフック)

CLAUDE.md とルールは「Claude への指示」で、守るかどうかは Claude の判断に委ねられます。これに対して 権限ルールは Claude Code 自身が適用するので、確実に止めたい操作はこちらに書きます。

Gradle 用

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(./gradlew test *)",
      "Bash(./gradlew compileJava *)",
      "Bash(./gradlew spotlessApply *)",
      "Bash(./gradlew spotlessCheck *)",
      "Bash(./gradlew build *)"
    ],
    "ask": [
      "Bash(./gradlew bootRun *)",
      "Bash(git push *)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Edit(./.env)",
      "Edit(./.env.*)",
      "Edit(/src/main/resources/application-prod*.yml)"
    ]
  },
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format-java.sh",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Maven 用

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(./mvnw test *)",
      "Bash(./mvnw -q test-compile)",
      "Bash(./mvnw spotless:apply *)",
      "Bash(./mvnw spotless:check *)",
      "Bash(./mvnw verify *)"
    ],
    "ask": [
      "Bash(./mvnw spring-boot:run *)",
      "Bash(git push *)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Edit(./.env)",
      "Edit(./.env.*)",
      "Edit(/src/main/resources/application-prod*.yml)"
    ]
  },
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format-java.sh",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Maven 用では、フックのスクリプト(後述)の整形コマンドも ./mvnw -q spotless:apply に置き換えます。

権限ルールの読み方

種類 この設定での内容 動き
allow テスト、コンパイル、整形、ビルド 確認なしで実行
ask アプリの起動、git push 毎回確認が出る
deny .env の読み書き、本番用 yml の編集 実行されない

押さえておきたい決まりは4つです。

  • 評価の順番は deny → ask → allow で、最初に合ったものが結果になります。狭い allow で広い deny に例外を作ることはできません。
  • Bash(./gradlew test *) のように、末尾の * の前に空白を入れると、./gradlew test 単体にも、./gradlew test --tests EquipmentServiceTest にも合います。空白が無い Bash(./gradlew test*) は ./gradlew testClasses のような別のタスクにも合います。
  • && などでつないだコマンドは、部分ごとに照合されます。 ./gradlew test && git push は、git push の部分が ask に合うので確認が出ます。
  • ファイルの権限は Read(パス) と Edit(パス) で書きます。 Edit のルールは、ファイルを編集する組み込みツールすべてに適用されます。Write(パス) という形で書いたルールは参照されません。

パスの書き方は gitignore と同じ形式で、先頭の記号で起点が変わります。

書き方 起点 例
./path または path 現在のディレクトリ Read(./.env)
/path 設定ファイルの置き場所(プロジェクトのルート) Edit(/src/main/resources/application-prod*.yml)
~/path ホームディレクトリ Read(~/.ssh/**)
//path ファイルシステムのルート Edit(//tmp/scratch.txt)

Read の deny は、同じパスへの編集と新規作成も止めます。上の設定では意図がわかるように Edit の行も並べています。本番用の yml は「読めるが編集できない」状態にしてあり、Claude は内容を参照したうえで、変更案を文章で出せます。

なお、Read・Edit の deny が効くのは、Claude の組み込みのファイルツールと、cat・sed・tee など Claude Code が認識するコマンド、リダイレクト先です。スクリプトの中でファイルを開くような間接的なアクセスまで止めたい場合は、公式ではサンドボックスの利用が案内されています。

Maven のプロジェクトでは、.mvn ディレクトリは Claude Code の保護対象のパスに含まれており、書き込みは自動では承認されません。.git や .idea、.vscode、.claude も同じ扱いです。

編集後に整形を走らせる PostToolUse フック

「編集したら必ず整形する」のように、例外なく毎回起きてほしいことはフックにします。 フックは決まったタイミングでシェルコマンドとして実行され、Claude の判断に左右されません。

上の settings.json の hooks の部分が、Edit か Write のツールが成功した後に次のスクリプトを呼ぶ設定です。

.claude/hooks/format-java.sh

#!/bin/bash
# format-java.sh : Claude が .java を編集した直後に Spotless で整形する

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# .java 以外(yml、md など)は何もしない
case "$FILE_PATH" in
  *.java) ;;
  *) exit 0 ;;
esac

cd "$CLAUDE_PROJECT_DIR" || exit 0

# 出力はすべて stderr へ。stdout は空のままにする
if ! ./gradlew spotlessApply --quiet 1>&2; then
  echo "spotlessApply が失敗しました: $FILE_PATH" >&2
  exit 1
fi

exit 0

置いたあと、実行権限を付けます。

chmod +x .claude/hooks/format-java.sh
行 していること
INPUT=$(cat) Claude Code が標準入力に渡す JSON を受け取る
jq -r '.tool_input.file_path // empty' 編集されたファイルのパスを取り出す
case ... *.java .java のときだけ先へ進む
./gradlew spotlessApply --quiet 1>&2 整形を実行。出力は stderr に回す
exit 1 整形が失敗したことを通知する(作業は止めない)

終了コードの意味は次のとおりです。PostToolUse はツールが実行された後に呼ばれるので、編集そのものを取り消すことはできません。

終了コード 動き
0 問題なし。成功時は会話に何も表示されない
2 ブロックの扱いになる。stderr の届き先はイベントによって異なる
その他 ブロックしないエラー。トランスクリプトにフックのエラーと stderr の1行目が出る

このスクリプトは Spotless(Gradle では spotlessApply、Maven では spotless:apply)がビルドに入っている前提です。整形にかかる時間を見込んで、フックの timeout を120秒にしています。

フックの動作を確かめた結果

フックは、JSON を標準入力に流し込めば手元で試せます。ここでは、受け取った引数をファイルに記録するだけの代役の gradlew を置いて、スクリプトの分岐を確かめました(bash 5.2.21、jq 1.7)。

$ export CLAUDE_PROJECT_DIR="$PWD"

$ # 1) .java を編集したとき
$ echo '{"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"'$PWD'/src/main/java/com/example/inventory/web/EquipmentController.java"}}' | .claude/hooks/format-java.sh; echo "exit=$?"
exit=0

$ # 2) application.yml を編集したとき
$ echo '{"hook_event_name":"PostToolUse","tool_name":"Write","tool_input":{"file_path":"'$PWD'/src/main/resources/application.yml"}}' | .claude/hooks/format-java.sh; echo "exit=$?"
exit=0

$ # 3) 整形コマンドが失敗したとき(代役の gradlew を終了コード 1 にして実行)
$ echo '{"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"src/main/java/com/example/inventory/service/EquipmentService.java"}}' | .claude/hooks/format-java.sh; echo "exit=$?"
spotlessApply が失敗しました: src/main/java/com/example/inventory/service/EquipmentService.java
exit=1

$ cat hook-test.log
gradlew spotlessApply --quiet
gradlew spotlessApply --quiet

記録が2行なのは、1) と 3) の .java のときだけ整形コマンドが呼ばれ、2) の yml では呼ばれていないためです。

設定ファイルとルールのフロントマターも、構文を確認しておきます。settings.json は厳密な JSON で、コメントや末尾のカンマがあると読み込み時にエラーになります。

import json, pathlib, yaml

s = json.load(open(".claude/settings.json", encoding="utf-8"))
print("settings.json: OK")
for k in ("allow", "ask", "deny"):
    print(f"  {k}: {len(s['permissions'][k])} 件")
h = s["hooks"]["PostToolUse"][0]
print("  PostToolUse matcher:", h["matcher"], "/ timeout:", h["hooks"][0]["timeout"])

for p in sorted(pathlib.Path(".claude/rules").glob("*.md")):
    front = yaml.safe_load(p.read_text(encoding="utf-8").split("---")[1])
    print(f"{p.name}: paths = {front['paths']}")
$ python3 check_config.py
settings.json: OK
  allow: 5 件
  ask: 2 件
  deny: 5 件
  PostToolUse matcher: Edit|Write / timeout: 120
config.md: paths = ['src/main/resources/application*.yml', 'src/test/resources/application*.yml']
controller.md: paths = ['**/*Controller.java']
repository.md: paths = ['**/*Repository.java']
testing.md: paths = ['src/test/**']

どこに書くかの振り分け表(Java 開発の項目20件)

迷ったときの基準は次の4つです。

  • 毎回のセッションで必要な事実 → CLAUDE.md
  • 特定の種類のファイルを触るときだけ必要 → .claude/rules/(paths 付き)
  • 手順が長く、たまにしか使わない → skills
  • 例外なく必ず起きてほしい・止めたい → hooks と権限(settings.json)
項目 置き場所 理由
Spring Boot・Java の版 CLAUDE.md 全作業の前提になる
ビルド・起動のコマンド CLAUDE.md 推測できず、毎回使う
テスト1件だけ実行するコマンド CLAUDE.md 毎回の確認に使う
パッケージ構成と依存の向き CLAUDE.md 設計上の判断で、全体に関わる
DI はコンストラクタで行う CLAUDE.md どのクラスにも当てはまる
完了の条件(実行するコマンド) CLAUDE.md すべての作業の終わりに使う
Controller の URL・戻り値の決まり rules Controller を触るときだけ必要
入力検証(@Valid)の付け方 rules Controller と DTO に限った話
@Query と名前付きパラメータ rules Repository を触るときだけ必要
関連の読み込み方(@EntityGraph) rules Repository に限った話
テストの命名と使うアノテーション rules src/test/ に限った話
プロファイルと設定キーの決まり rules yml を触るときだけ必要
Flyway マイグレーションの追加手順 skills 手順が複数あり、たまに行う
API を1本追加する一連の流れ skills Controller からテストまでの長い手順
依存ライブラリの更新手順 skills 調査と確認を伴う定型作業
リリースノートの下書き skills 手動で呼び出す作業
編集後の整形 hooks 毎回、必ず実行したい
適用済みマイグレーションの書き換え防止 hooks 既存ファイルだけを確実に止めたい
.env・本番用 yml の保護 権限(deny) Claude Code 側で確実に止める
テスト・ビルドの確認省略 権限(allow) 繰り返し実行するので確認を省く

「適用済みマイグレーションの書き換え防止」を権限ではなくフックにしているのは、Edit の deny はパターンに合うパスへの新規作成も止めるためです。新しい番号のファイルは作れるようにしつつ、すでにあるファイルの編集だけを止めたい場合は、PreToolUse フックで「そのファイルが存在するか」を見て、存在すれば終了コード 2 で止める形にします。

あいまいな指示を検証できる形に直した例10組

公式ドキュメントは、CLAUDE.md の指示を「検証できるほど具体的に」書くことを勧めています。Java・Spring Boot の場面に置き換えた10組です。

あいまいな指示 検証できる指示
テストを書く ./gradlew test --tests <テストクラス名> を実行し、結果を示す
コードをきれいに整形する ./gradlew spotlessApply を実行し、spotlessCheck が通ることを示す
新しい版の書き方で書く import は jakarta.* を使い、javax.persistence と javax.validation を使わない
DI を正しく使う 依存は final フィールドとコンストラクタで受け取り、フィールドに @Autowired を付けない
レイヤーを守る Controller から *Repository を import しない。Service を通す
例外を適切に処理する Service で BusinessException の子クラスを投げ、GlobalExceptionHandler で HTTP ステータスに変換する
設定は安全に扱う 接続情報の実値を yml に書かず、${DB_PASSWORD} の形で環境変数を参照する
DB の変更は慎重に行う 既存の V*.sql を編集せず、次の番号の V<番号>__<説明>.sql を追加する
SQL の発行に気を付ける 一覧取得のテストで SQL のログを出し、発行された本数を報告する
動くことを確認する ./gradlew build を実行し、終了時の結果と失敗したテスト名(あれば)を示す

右側に共通しているのは、コマンド名・クラス名・パターンのように、後から機械的に確かめられる言葉で書いてあることです。

設定が読み込まれたかを確かめる手順

確かめたいこと コマンド 見るところ
CLAUDE.md が読み込まれたか /context Memory files の一覧
指示ファイルの場所と編集 /memory 一覧から選んで開く
設定ファイルが読み込まれたか /status Setting sources の行
権限ルールの一覧 /permissions ルールと、その出どころのファイル
フックが登録されたか /hooks PostToolUse の下に表示される
指示の矛盾や重複の点検 /doctor prompt-audit 提案された修正(v2.1.283 以降)

paths 付きのルールは、合うファイルを Claude が読み書きした時点で読み込まれます。*Controller.java を1つ開いてもらってから、ルールに書いた内容について質問すると確かめやすくなります。

これから CLAUDE.md を作る場合は、/init を実行すると、Claude がコードベースを調べてビルドやテストのコマンドを含む下書きを作ります。そこに、この記事のテンプレートから「版」「禁止事項」「完了の条件」を足していく進め方が手軽です。

AGENTS.md を使っているリポジトリの場合

Claude Code v2.1.277 以降は、作業ディレクトリとその上位に CLAUDE.md(と CLAUDE.local.md)が無い場合、AGENTS.md をプロジェクトの指示として読み込みます。両方がある場合、既定で読まれるのは CLAUDE.md だけです。1つの内容を共有したいときは、CLAUDE.md の先頭で取り込みます。

@AGENTS.md

## Claude Code

- `src/main/resources/db/migration/` 以下の変更にはプランモードを使う

.claude/rules/ のファイルは、AGENTS.md と一緒に読み込まれます。

よくある質問

Q. CLAUDE.md に書いた禁止事項は、必ず守られますか? A. CLAUDE.md はコンテキストとして渡される指示で、強制する設定ではありません。確実に止めたい操作は、settings.json の permissions.deny か PreToolUse フックで設定します。この記事では、.env と本番用 yml を両方に書いています。

Q. ルールを分けると、CLAUDE.md の @ インポートと何が違いますか? A. @path で取り込んだファイルは、CLAUDE.md と一緒に起動時に読み込まれるため、コンテキストの量は減りません。paths 付きのルールは、合うファイルを扱うときだけ読み込まれます。

Q. Spring Boot 3.5 系のプロジェクトでも、このテンプレートは使えますか? A. 構成はそのまま使えます。「技術スタック」と「4 系で書き方が変わった点」の節を、3.5 系の内容(Spring Framework 6.2 系、Servlet 6.0、starter 名など)に書き換えてください。版の行を実際のプロジェクトに合わせることが、いちばん大事な点です。

Q. 整形フックで毎回 Gradle が動くのは重くありませんか? A. 編集のたびに整形コマンドが1回走ります。気になる場合は、整形はフックから外して CLAUDE.md の「完了の条件」に spotlessCheck を残す形でも運用できます。確実さと速さのどちらを優先するかで選んでください。

まとめ

  • CLAUDE.md には版・コマンド・構成・禁止事項・完了の条件を書く。目安は200行以下
  • Spring Boot は系列(4.1.x など)まで明記する。3 系と 4 系で starter 名やアノテーションが違う
  • テスト1件のコマンドを書く:./gradlew test --tests クラス名、./mvnw test -Dtest=クラス名
  • ファイルの種類ごとの決まりは .claude/rules/ に paths 付きで分ける
  • 確実に止めたい操作は permissions.deny、毎回起きてほしい処理はフックにする
  • 評価の順番は deny → ask → allow。Bash(./gradlew test *) は末尾の * の前に空白を入れる
  • 指示は検証できる言葉で書く。コマンド名・クラス名・パターンで表す

参考・出典

いずれも2026年10月7日に確認しました。

  • Claude Code Docs: Claude があなたのプロジェクトを記憶する方法(CLAUDE.md・rules・AGENTS.md) https://code.claude.com/docs/ja/memory
  • Claude Code Docs: Claude Code のベストプラクティス https://code.claude.com/docs/ja/best-practices
  • Claude Code Docs: hooks でアクションを自動化する https://code.claude.com/docs/ja/hooks-guide
  • Claude Code Docs: Configure permissions https://code.claude.com/docs/en/permissions
  • Claude Code Docs: Choose a permission mode(保護対象のパス) https://code.claude.com/docs/en/permission-modes
  • Spring 公式 API: Spring Boot のサポート期間 https://api.spring.io/projects/spring-boot/generations
  • Spring Boot プロジェクトページ https://spring.io/projects/spring-boot
  • Spring Boot 4.1.1 System Requirements https://docs.spring.io/spring-boot/system-requirements.html
  • Spring Boot 4.0 / 3.5 System Requirements https://docs.spring.io/spring-boot/4.0/system-requirements.html / https://docs.spring.io/spring-boot/3.5/system-requirements.html
  • Spring Boot 4.0 Migration Guide https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide
  • Spring Boot リファレンス: Testing Spring Boot Applications https://docs.spring.io/spring-boot/reference/testing/spring-boot-applications.html
  • Spring Boot Gradle Plugin: Running your Application https://docs.spring.io/spring-boot/gradle-plugin/running.html
  • Spring Boot Maven Plugin: Running your Application https://docs.spring.io/spring-boot/maven-plugin/run.html
  • Spring Data JPA リファレンス: JPA Query Methods https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html
  • Gradle User Manual: Testing in Java & JVM projects https://docs.gradle.org/current/userguide/java_testing.html
  • Maven Surefire Plugin: Running a Single Test https://maven.apache.org/surefire/maven-surefire-plugin/examples/single-test.html
  • Spotless(Gradle・Maven プラグイン) https://github.com/diffplug/spotless

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「Claude CodeでSpring Boot開発|CLAUDE.md例と設定一式」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?