Skip to content

Instantly share code, notes, and snippets.

@leandronsp
Last active October 13, 2026 20:53
Show Gist options
  • Select an option

  • Save leandronsp/27b8e006c16aa31eb8b434522ecc9ede to your computer and use it in GitHub Desktop.

Select an option

Save leandronsp/27b8e006c16aa31eb8b434522ecc9ede to your computer and use it in GitHub Desktop.
Exemplo de CLAUDE.md e regras do Claude Code para um projeto Java com Spring Boot

Exemplo de CLAUDE.md

CLAUDE.md e regras do Claude Code para um projeto Java com Spring Boot. O domínio do exemplo (pedidos) é inventado; troque pelo seu.

O CLAUDE.md fica curto: comandos, regras gerais e o que o agente não deve fazer. As regras de cada assunto ficam em .claude/rules/, um arquivo por tema.

A regra de arquitetura também existe como teste. O ArquiteturaTest usa ArchUnit e quebra o build quando o pacote de domínio importa Spring, JPA ou Jackson, ou depende dos adaptadores.

Arquivos

Neste gist No projeto
CLAUDE.md CLAUDE.md
rules-arquitetura.md .claude/rules/arquitetura.md
rules-testes.md .claude/rules/testes.md
rules-java.md .claude/rules/java.md
ArquiteturaTest.java src/test/java/<seu/pacote>/ArquiteturaTest.java

Para usar

  • Adicione com.tngtech.archunit:archunit-junit5 como dependência de teste e troque o pacote com.exemplo.pedidos pelo do seu projeto.
  • O ./mvnw verify citado no CLAUDE.md supõe Checkstyle e SpotBugs no pom.xml. Se o projeto não usa, tire do texto.
  • docs/decisoes.md é opcional: uma linha por decisão de arquitetura, com o motivo.
package com.exemplo.pedidos;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
// Regra descrita em .claude/rules/arquitetura.md.
@AnalyzeClasses(packages = "com.exemplo.pedidos")
class ArquiteturaTest {
@ArchTest
static final ArchRule dominioNaoConheceFramework = noClasses()
.that().resideInAPackage("..dominio..")
.should().dependOnClassesThat()
.resideInAnyPackage("org.springframework..", "jakarta.persistence..", "com.fasterxml..");
@ArchTest
static final ArchRule dominioNaoDependeDosAdaptadores = noClasses()
.that().resideInAPackage("..dominio..")
.should().dependOnClassesThat().resideInAPackage("..adaptadores..");
}

Pedidos

API de pedidos em Java 21 + Spring Boot. Este arquivo é o mapa; as regras de cada tema ficam em .claude/rules/ e valem tanto quanto este.

Comandos

  • ./mvnw verify: o portão. Compila, roda testes, ArchUnit, Checkstyle e SpotBugs. Nada é commitado com ele vermelho.
  • ./mvnw test -Dtest=NomeDoTeste: durante o ciclo, só o teste que você mexeu.
  • docker compose up -d db: o Postgres local. Nunca aponte para homologação ou produção.

Regras de ouro

  1. Leia antes de escrever. Procure o padrão que já existe e siga-o. Não crie abstração nova sem um segundo uso concreto.
  2. O domínio não conhece framework. Detalhe em rules/arquitetura.md; um teste ArchUnit quebra se alguém violar.
  3. Teste que falha primeiro em regra de negócio e em bug. Detalhe em rules/testes.md.
  4. Mudança pequena. Um commit, uma mudança lógica. Se o diff passou de ~300 linhas, pare e divida.
  5. Na dúvida, pergunte. Não invente requisito, rota, coluna ou dependência. Se não conseguiu reproduzir um bug, pare e diga.

Nunca

  • Segredo, token ou dado de cliente em código, teste, log, commit ou comentário. Dados de teste são sintéticos.
  • Adicionar dependência sem dizer por quê e sem pedir.
  • Desligar teste, regra do Checkstyle ou do ArchUnit para "fazer passar".
  • Rodar migração, script ou chamada contra qualquer ambiente que não seja o local.

Onde ler mais

rules/arquitetura.md, rules/testes.md, rules/java.md, docs/decisoes.md (uma linha por decisão de arquitetura, com o porquê; procure pelo título, não leia inteiro).

Arquitetura

Hexagonal. A seta de dependência aponta sempre para dentro.

  • dominio/: entidades, objetos de valor, regras. Java puro: sem Spring, sem JPA, sem Jackson, sem LocalDateTime.now() (o relógio é um Clock recebido).
  • aplicacao/: um caso de uso por classe (CriarPedido, CancelarPedido), com um método público. Orquestra domínio e portas; não tem regra de negócio.
  • adaptadores/entrada/: controllers REST, consumidores de fila. Traduzem e delegam.
  • adaptadores/saida/: JPA, clientes HTTP, fila. Implementam as portas do domínio.

Regras

  • Porta só quando há fronteira. Interface existe para o que sai do processo (banco, HTTP, fila). Serviço interno com uma implementação é classe concreta.
  • Entidade JPA não é entidade de domínio. O adaptador mapeia; o domínio nunca vê @Entity.
  • Erro de negócio é exceção do domínio (PedidoJaCanceladoException), lançada no fundo e traduzida para HTTP só no adaptador de entrada.
  • Dinheiro em centavos (long) ou BigDecimal com escala fixa. Nunca double.

A guarda

ArquiteturaTest (ArchUnit) falha se dominio importar org.springframework.., jakarta.persistence.. ou com.fasterxml.., ou se dominio depender de adaptadores. Mudar a arquitetura é mudar esse teste junto, no mesmo PR, com o porquê em docs/decisoes.md.

Java

  • record para objetos de valor e DTOs; sealed interface para estados fechados (StatusPedido), não String nem boolean solto.
  • Optional só como retorno. Nunca em campo nem em parâmetro.
  • Injeção por construtor, campos final. Sem @Autowired em campo.
  • Sem Lombok em código novo: record resolve a maioria.
  • Imports explícitos, sem *. Formatação é a do Checkstyle; não discuta, rode.

Testes

Antes de escrever código, diga qual camada prova a mudança.

Camada O que prova Ferramenta
Unidade regra do domínio, sem Spring JUnit 5 + AssertJ
Integração adaptador contra o banco de verdade Testcontainers (Postgres)
API o contrato HTTP de ponta a ponta @SpringBootTest + MockMvc

Como

  • Bug: primeiro o teste que reproduz, rodando vermelho pelo motivo certo. Depois a correção mínima. Em cada passo, mude o teste ou o código de produção, nunca os dois.
  • Sem dublê do que é nosso. Dublê (mock) só na fronteira (cliente HTTP externo). Banco é Testcontainers, não H2.
  • Espere o efeito, não o tempo. Thread.sleep em teste é bug. Teste que passa na segunda tentativa é bug.
  • Nome diz o comportamento: cancelaPedidoAindaNaoFaturado, não testCancel2.
  • Rode só o teste que você tocou durante o ciclo; a suíte inteira é o ./mvnw verify.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment