java-coding-standards について
Spring Boot サービスの保守しやすい Java 17+ コードの作成とレビューを支援するスキルです。型安全性、不変性、例外処理、プロジェクト構成、ログ、テストの指針を示します。
主な機能
- 01命名とパッケージ構成の規約
- 02不変性と Optional の使い方
- 03Stream とジェネリクスの指針
- 04ドメイン例外の処理
- 05ログと null の扱い
- 06JUnit 5 と Mockito によるテスト
活用シーン
- 01Spring Boot コードのレビュー
- 02Java サービスの構成
- 03例外処理の改善
- 04Java テストの確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ helloggx/ skill --skill java-coding-standardsインストール後、Claude Code で/java-coding-standardsを入力して読み込まれたか確認します。
詳細
Java コーディング規約
Spring Boot サービスで、読みやすく保守しやすい Java(17 以降)のコードを書くための規約です。
適用する場面
- Spring Boot プロジェクトで Java コードを記述またはレビューするとき
- 命名規則、不変性、例外処理の規約を徹底するとき
- レコード、sealed クラス、パターンマッチング(Java 17 以降)を使用するとき
- Optional、ストリーム、ジェネリクスの使い方をレビューするとき
- パッケージやプロジェクト構成を整理するとき
基本原則
- 巧妙さより明快さを重視します
- デフォルトで不変にし、共有される可変状態を最小限に抑えます
- 意味のある例外を使って、早期に失敗させます
- 命名とパッケージ構成に一貫性を持たせます
命名
// ✅ Classes/Records: PascalCase
public class MarketService {}
public record Money(BigDecimal amount, Currency currency) {}
// ✅ Methods/fields: camelCase
private final MarketRepository marketRepository;
public Market findBySlug(String slug) {}
// ✅ Constants: UPPER_SNAKE_CASE
private static final int MAX_PAGE_SIZE = 100;
不変性
// ✅ Favor records and final fields
public record MarketDto(Long id, String name, MarketStatus status) {}
public class Market {
private final Long id;
private final String name;
// getters only, no setters
}
Optional の使用
// ✅ Return Optional from find* methods
Optional<Market> market = marketRepository.findBySlug(slug);
// ✅ Map/flatMap instead of get()
return market
.map(MarketResponse::from)
.orElseThrow(() -> new EntityNotFoundException("Market not found"));
ストリームのベストプラクティス
// ✅ Use streams for transformations, keep pipelines short
List<String> names = markets.stream()
.map(Market::name)
.filter(Objects::nonNull)
.toList();
// ❌ Avoid complex nested streams; prefer loops for clarity
例外
- ドメインエラーには非検査例外を使用し、技術的な例外はコンテキストを付けてラップします
- ドメイン固有の例外を作成します(例:
MarketNotFoundException) - 広範な
catch (Exception ex)は、中央で再スローまたはログ記録する場合を除き、避けます
throw new MarketNotFoundException(slug);
ジェネリクスと型安全性
- 生の型を避け、ジェネリック型パラメーターを宣言します
- 再利用可能なユーティリティには境界付きジェネリクスを優先します
public <T extends Identifiable> Map<Long, T> indexById(Collection<T> items) { ... }
プロジェクト構成(Maven/Gradle)
src/main/java/com/example/app/
config/
controller/
service/
repository/
domain/
dto/
util/
src/main/resources/
application.yml
src/test/java/... (mirrors main)
書式とスタイル
- 2 個または 4 個のスペースを一貫して使用します(プロジェクトの標準に従います)
- ファイルごとに、トップレベルの public 型を 1 つだけ定義します
- メソッドは短く、目的を絞ります。ヘルパーを切り出します
- メンバーは、定数、フィールド、コンストラクター、public メソッド、protected メソッド、private メソッドの順に配置します
避けるべきコードの匂い
- 長い引数リスト → DTO/ビルダーを使用します
- 深いネスト → 早期リターンを使用します
- マジックナンバー → 名前付き定数を使用します
- static な可変状態 → 依存性注入を優先します
- 何もしない catch ブロック → ログを記録して対処するか、再スローします
ロギング
private static final Logger log = LoggerFactory.getLogger(MarketService.class);
log.info("fetch_market slug={}", slug);
log.error("failed_fetch_market slug={}", slug, ex);
null の処理
- やむを得ない場合に限り
@Nullableを受け入れ、それ以外では@NonNullを使用します - 入力には Bean Validation(
@NotNull、@NotBlank)を使用します
テストの要件
- 流れるようなアサーションには JUnit 5 と AssertJ を使用します
- モックには Mockito を使用し、可能な限り部分モックは避けます
- 決定的なテストを優先し、隠れた sleep は使用しません
覚えておきましょう:コードの意図、型、安全性、可観測性を保ちます。必要性が証明されていない限り、マイクロ最適化より保守性を優先します。
コメント
Markdown 対応