Aprenda a gerar OpenAPI a partir do comportamento com Claude Code, comparar com sua implementação e criar exemplos simples de validação cliente e servidor.

Um contrato OpenAPI é uma descrição compartilhada da sua API: quais endpoints existem, o que você envia, o que recebe de volta e como são os erros. É o acordo entre o servidor e qualquer coisa que o chame (um web app, app móvel ou outro serviço).
O problema é o drift. A API em execução muda, mas o spec não. Ou o spec é “limpo” para ficar mais apresentável do que a realidade, enquanto a implementação continua retornando campos estranhos, códigos de status faltantes ou formatos de erro inconsistentes. Com o tempo, as pessoas deixam de confiar no arquivo OpenAPI, e ele vira só mais um documento ignorado.
O drift geralmente vem das pressões normais: um conserto rápido é enviado sem atualizar o spec, um novo campo opcional é adicionado “temporariamente”, a paginação evolui, ou equipes atualizam fontes diferentes de verdade (código backend, uma coleção do Postman e um arquivo OpenAPI).
Manter honestidade significa que o spec corresponde ao comportamento real. Se a API às vezes retorna 409 por conflito, isso pertence ao contrato. Se um campo é nullable, diga isso. Se auth é exigido, não deixe vago.
Um bom fluxo de trabalho deixa você com:
Esse último ponto importa porque um contrato só ajuda quando é aplicado. Um spec honesto mais checagens repetíveis transforma “documentação de API” em algo que as equipes possam confiar.
Se você começar lendo código ou copiando rotas, seu OpenAPI descreverá o que existe hoje, incluindo esquisitices que talvez não queira prometer. Em vez disso, descreva o que a API deve fazer para um chamador, depois use o spec para verificar se a implementação corresponde.
Antes de escrever YAML ou JSON, colecione um pequeno conjunto de fatos por endpoint:
Então escreva o comportamento como exemplos. Exemplos forçam você a ser específico e facilitam rascunhar um contrato consistente.
Para uma API de Tasks, um caminho feliz pode ser: “Cria uma task com title e devolve id, title, status e createdAt.” Adicione falhas comuns: “Falta title retorna 400 com {\"error\":\"title is required\"}” e “Sem auth retorna 401.” Se você já conhece edge cases, inclua-os: se títulos duplicados são permitidos e o que acontece quando um ID de task não existe.
Capture regras como sentenças simples que não dependam de detalhes de código:
title é obrigatório e tem 1–120 caracteres.”limit seja definido (máx 200).”dueDate é date-time ISO 8601.”Finalmente, decida o escopo da v1. Se estiver inseguro, mantenha a v1 pequena e clara (create, read, list, update status). Guarde busca, atualizações em lote e filtros complexos para depois, assim o contrato permanece crível.
Antes de pedir ao Claude Code para gerar um spec, escreva notas de comportamento em um formato pequeno e repetível. O objetivo é tornar difícil “preencher lacunas” com suposições.
Um bom template é curto o bastante para ser usado, mas consistente o suficiente para que duas pessoas descrevam o mesmo endpoint de forma semelhante. Mantenha o foco no que a API faz, não em como é implementada.
Use um bloco por endpoint:
METHOD + PATH:
Purpose (1 sentence):
Auth:
Request:
- Query:
- Headers:
- Body example (JSON):
Responses:
- 200 OK example (JSON):
- 4xx example (status + JSON):
Edge cases:
Data types (human terms):
Escreva pelo menos uma requisição concreta e duas respostas. Inclua códigos de status e bodies JSON realistas com nomes de campo reais. Se um campo for opcional, mostre um exemplo onde ele está ausente.
Destaque edge cases explicitamente. São esses pontos que fazem o spec ficar falso mais tarde porque cada um assumiu algo diferente: resultados vazios, IDs inválidos (400 vs 404), duplicatas (409 vs comportamento idempotente), falhas de validação e limites de paginação.
Também anote tipos de dados em termos humanos antes de pensar em schemas: strings vs numbers, formatos date-time, booleans e enums (lista de valores permitidos). Isso evita um schema “bonito” que não bate com payloads reais.
Claude Code funciona melhor quando você o trata como um escriba cuidadoso. Dê suas behavior notes e regras estritas sobre como o OpenAPI deve ser formado. Se você só disser “escreva um spec OpenAPI”, ele geralmente vai chutar nomes, ser inconsistente e esquecer casos de erro.
Cole suas behavior notes primeiro, depois adicione um bloco de instruções rígidas. Um prompt prático fica assim:
You are generating an OpenAPI 3.1 YAML spec.
Source of truth: the behavior notes below. Do not invent endpoints or fields.
If anything is unclear, list it under ASSUMPTIONS and leave TODO markers in the spec.
Requirements:
- Include: info, servers (placeholder), tags, paths, components/schemas, components/securitySchemes.
- For each operation: operationId, tags, summary, description, parameters, requestBody (when needed), responses.
- Model errors consistently with a reusable Error schema and reference it in 4xx/5xx responses.
- Keep naming consistent: PascalCase schema names, lowerCamelCase fields, stable operationId pattern.
Behavior notes:
[PASTE YOUR NOTES HERE]
Output only the OpenAPI YAML, then a short ASSUMPTIONS list.
Depois de receber o rascunho, leia as ASSUMPTIONS primeiro. É aí que a honestidade é ganha ou perdida. Aprove o que está correto, corrija o que está errado e rode novamente com notas atualizadas.
Para manter nomenclatura consistente, declare convenções no começo e siga-as: por exemplo, um padrão estável de operationId, nomes de tags apenas substantivos, nomes de schema no singular, um Error compartilhado e um nome de auth usado em todos os lugares.
Se você trabalha num ambiente tipo Koder.ai, é útil salvar o YAML como um arquivo real cedo e iterar em pequenos diffs. Assim você vê quais mudanças vieram de decisões aprovadas versus detalhes que o modelo chutou.
Antes de comparar com produção, garanta que o arquivo OpenAPI seja internamente consistente. Esse é o lugar mais rápido para pegar wishful thinking e linguagem vaga.
Leia cada endpoint como se você fosse o desenvolvedor cliente. Foque no que um chamador deve enviar e no que pode esperar receber.
Uma revisão prática:
Respostas de erro merecem cuidado extra. Escolha uma forma compartilhada e re-use-a. Alguns times mantêm muito simples ({ error: string }), outros usam um objeto ({ error: { code, message, details } }). Qualquer um pode funcionar, mas não misture entre endpoints e exemplos. Se misturar, o código cliente acumula casos especiais.
Um cenário de sanidade rápida ajuda. Se POST /tasks exige title, o schema deve marcá-lo como required, a resposta de falha deve mostrar o body de erro que você realmente retorna, e a operação deve esclarecer se auth é necessário.
Quando o spec estiver como você pretende, trate a API em execução como a verdade sobre o que os clientes experimentam hoje. O objetivo não é “vencer” entre spec e código; é expor diferenças cedo e decidir claramente sobre cada uma.
Para um primeiro passe, amostras reais de request/response são a opção mais simples. Logs e testes automatizados também funcionam se forem confiáveis.
Fique de olho em incompatibilidades comuns: endpoints presentes em um lugar e não no outro, diferenças de nomes ou formatos de campo, diferenças de códigos de status (200 vs 201, 400 vs 422), comportamentos não documentados (paginação, ordenação, filtragem) e diferenças de auth (spec diz público, código exige token).
Exemplo: seu OpenAPI diz que POST /tasks retorna 201 com {id,title}. Você chama a API em execução e recebe 200 mais {id,title,createdAt}. Isso não é “quase igual” se você gera SDKs a partir do spec.
Antes de editar qualquer coisa, decida como resolver conflitos:
Mantenha cada mudança pequena e revisável: um endpoint, uma resposta, um ajuste de schema. É mais fácil revisar e retestar.
Quando você tiver um spec confiável, transforme-o em pequenos exemplos de validação. Isso é o que impede o drift de voltar.
No servidor, validação significa falhar rápido quando um request não bate com o contrato e retornar um erro claro. Isso protege seus dados e facilita achar bugs.
Uma forma simples de expressar exemplos de validação do servidor é escrevê-los como casos com três partes: input, output esperado e erro esperado (um código de erro ou padrão de mensagem, não texto exato).
Exemplo (contrato diz que title é obrigatório e tem 1 a 120 chars):
{
"name": "Create task without title returns 400",
"request": {"method": "POST", "path": "/tasks", "body": {"title": ""}},
"expect": {"status": 400, "body": {"error": {"code": "VALIDATION_ERROR"}}}
}
No cliente, validação é sobre detectar drift antes dos usuários. Se o servidor começar a devolver um formato diferente, ou um campo obrigatório desaparecer, seus testes devem sinalizar.
Mantenha checagens de cliente focadas no que você realmente precisa, como “uma task tem id, title, status.” Evite afirmar todo campo opcional ou ordem exata. Você quer falhas em mudanças breaking, não em adições inofensivas.
Algumas diretrizes para manter os testes legíveis:
Se você usa Koder.ai, pode gerar e manter esses casos de exemplo ao lado do arquivo OpenAPI, e atualizá-los como parte da mesma revisão quando o comportamento mudar.
Imagine uma API pequena com três endpoints: POST /tasks cria uma task, GET /tasks lista tasks e GET /tasks/{id} retorna uma task.
Comece escrevendo alguns exemplos concretos para um endpoint, como se estivesse explicando para um testador.
Para POST /tasks, o comportamento pretendido pode ser:
{ \"title\": \"Buy milk\" } e receber 201 com um objeto task novo, incluindo id, title e done:false.{} e receber 400 com um erro como { \"error\": \"title is required\" }.{ \"title\": \"x\" } (muito curto) e receber 422 com { \"error\": \"title must be at least 3 characters\" }.Quando o Claude Code rascunhar o OpenAPI, o trecho para esse endpoint deve capturar o schema, códigos de status e exemplos realistas:
paths:
/tasks:
post:
summary: Create a task
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateTaskRequest'
examples:
ok:
value: { \"title\": \"Buy milk\" }
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: '#/components/schemas/Task'
examples:
created:
value: { \"id\": \"t_123\", \"title\": \"Buy milk\", \"done\": false }
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
missingTitle:
value: { \"error\": \"title is required\" }
'422':
description: Unprocessable Entity
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
tooShort:
value: { \"error\": \"title must be at least 3 characters\" }
Uma incompatibilidade comum é sutil: a API em execução retorna 200 em vez de 201, ou retorna { \"taskId\": 123 } em vez de { \"id\": \"t_123\" }. Esse tipo de “quase igual” quebra clientes gerados.
Corrija escolhendo uma fonte de verdade. Se o comportamento pretendido está correto, altere a implementação para retornar 201 e a forma Task acordada. Se o comportamento de produção já é dependido, atualize o spec (e as behavior notes) para refletir a realidade, então adicione validação e respostas de erro faltantes para que os clientes não sejam surpreendidos.
Um contrato vira desonesto quando deixa de descrever regras e começa a descrever o que sua API retornou em um bom dia. Um teste simples: uma nova implementação poderia passar por esse spec sem copiar as esquisitices de hoje?
Uma armadilha é o overfitting. Você captura uma resposta e transforma em lei. Ex.: sua API atualmente retorna dueDate: null para toda task, então o spec diz que o campo é sempre nullable. Mas a regra real pode ser “obrigatório quando status for scheduled.” O contrato deve expressar a regra, não apenas o dataset atual.
Erros são onde honestidade costuma quebrar. É tentador specar só respostas de sucesso porque parecem limpas. Mas clientes precisam do básico: 401 quando falta token, 403 para acesso proibido, 404 para IDs desconhecidos e um erro de validação consistente (400 ou 422).
Outros padrões problemáticos:
taskId em uma rota, id em outra, ou priority string em uma resposta e number em outra).string, tudo vira opcional).Um bom contrato é testável. Se você não consegue escrever um teste que falhe a partir do spec, ele ainda não é honesto.
Antes de entregar o arquivo OpenAPI para outra equipe, faça uma revisão rápida: “alguém consegue usar isso sem ler sua mente?”.
Comece pelos exemplos. Um spec pode ser válido e ainda inútil se cada request e response for abstrato. Para cada operação, inclua pelo menos um exemplo realista de request e uma resposta de sucesso. Para erros, um exemplo por falha comum (auth, validação) geralmente basta.
Depois verifique consistência. Se um endpoint retorna { \"error\": \"...\" } e outro { \"message\": \"...\" }, clientes têm lógica ramificada por toda parte. Escolha uma única forma de erro e reutilize-a, junto com códigos de status previsíveis.
Um checklist curto:
Um truque prático: escolha um endpoint, finja que nunca viu a API e responda “o que eu envio, o que recebo de volta e o que quebra?” Se o OpenAPI não responder claramente, não está pronto.
Esse fluxo paga dividendos quando roda regularmente, não só em um scramble de release. Adote uma regra simples e cumpra-a: rode sempre que um endpoint mudar e rode de novo antes de publicar um spec atualizado.
Mantenha propriedade simples. A pessoa que muda um endpoint atualiza as behavior notes e o rascunho do spec. Uma segunda pessoa revisa o diff “spec vs implementation” como uma code review. QA ou suporte costumam ser ótimos revisores porque enxergam respostas e edge cases pouco claros.
Trate edições de contrato como edições de código. Se você usa um construtor baseado em chat como Koder.ai, tirar um snapshot antes de mudanças arriscadas e usar rollback quando preciso mantém a iteração segura. Koder.ai também permite exportar código-fonte, facilitando manter spec e implementação lado a lado no repositório.
Uma rotina que costuma funcionar sem atrapalhar times:
Próxima ação: escolha um endpoint existente. Escreva 5–10 linhas de behavior notes (inputs, outputs, casos de erro), gere um rascunho OpenAPI a partir dessas notas, valide, e compare com a implementação em execução. Corrija uma incompatibilidade, reteste e repita. Depois de um endpoint, o hábito tende a pegar.
OpenAPI drift é quando a API que você realmente roda não corresponde mais ao arquivo OpenAPI compartilhado. O spec pode estar sem campos novos, códigos de status ou regras de autenticação, ou pode descrever um comportamento “ideal” que o servidor não segue.
Isso importa porque clientes (apps, outros serviços, SDKs gerados, testes) tomam decisões com base no contrato, não no que o servidor “geralmente” faz.
Os problemas para times de cliente aparecem de forma aleatória e difícil de depurar: um app móvel espera 201 mas recebe 200, um SDK não consegue desserializar uma resposta porque um campo foi renomeado, ou o tratamento de erros falha porque as formas de erro são diferentes.
Mesmo quando nada quebra imediatamente, as equipes perdem confiança e param de usar o spec, o que elimina seu sistema de alerta precoce.
Porque o código reflete o comportamento atual, incluindo esquisitices acidentais que você talvez não queira prometer a longo prazo.
Um padrão melhor é: escreva primeiro o comportamento pretendido (entradas, saídas, erros) e depois verifique se a implementação corresponde. Assim você tem um contrato que pode ser aplicado, em vez de um retrato do estado atual das rotas.
Para cada endpoint, capture:
Se você conseguir escrever uma requisição concreta e duas respostas, já dá para rascunhar um spec verdadeiro.
Escolha uma forma de erro e reutilize em todo o spec.
Um padrão simples é:
{ "error": "message" }, ou{ "error": { "code": "...", "message": "...", "details": ... } }Depois use isso de forma consistente em endpoints e exemplos. Consistência importa mais que sofisticação, porque clientes vão codificar para esse formato.
Dê ao Claude Code suas notas de comportamento e regras estritas, dizendo para não inventar campos. Um conjunto prático de instruções:
TODO no spec e liste em ASSUMPTIONS.”Error) e faça referências a eles.”Após a geração, revise a seção primeiro — é aí que a honestidade começa a se perder se você aceitar suposições.
Valide o spec antes de comparar com produção:
201)Isso pega arquivos OpenAPI “wishful” antes mesmo de olhar o comportamento em produção.
Trate a API em execução como o que os usuários veem hoje e decida caso a caso:
Mantenha mudanças pequenas (um endpoint ou uma resposta por vez) para poder retestar rápido.
Validação no servidor significa rejeitar rapidamente requests que violem o contrato e retornar um erro claro e consistente (status + código de erro/shape). Isso protege dados e facilita achar bugs.
Validação no cliente significa detectar mudanças breaking antes dos usuários: verifique apenas no que você realmente depende:
Evite afirmar todos os campos opcionais para não quebrar testes por adições inocentes.
Uma rotina prática:
Se você usa Koder.ai, mantenha o OpenAPI junto com o código, faça snapshots antes de mudanças arriscadas e use rollback quando necessário.