A maioria das equipes de API trata o contrato como algo secundário: escreve o código, gera uma especificação e depois tenta manter os dois sincronizados. No design de API git-native, a ordem muda: o contrato da API vira código-fonte, fica versionado no Git e passa por revisão como qualquer outra alteração do sistema.
Este guia mostra como aplicar essa disciplina na prática: criar contratos em branches, revisar mudanças em pull requests e transformar uma especificação aprovada em mocks, testes, clientes e documentação. O objetivo é simples: fazer com que o histórico do Git seja também o histórico da sua API.
Se você já conhece ferramentas spec-first e quer uma visão mais orientada a produto, leia o artigo complementar sobre o fluxo de trabalho de API git-native. Aqui, o foco é implementação.
O que “git-native” significa para APIs
Git-native significa que a definição da API vive no repositório como texto simples: um arquivo .yaml ou .json ao lado do código, rastreado pelo mesmo controle de versão usado pela equipe.
Em vez de manter o contrato preso a uma plataforma em nuvem, o arquivo em main passa a ser a fonte da verdade. Qualquer interface visual, mock server, documentação ou cliente gerado é apenas uma representação desse arquivo.
Uma configuração git-native precisa de três propriedades:
- A especificação fica em um arquivo versionado no repositório.
- As alterações passam por branch, commit, pull request e merge.
- Mocks, testes, clientes e documentação são gerados a partir do arquivo confirmado.
Com isso, você ganha histórico, git blame, rollback e revisão de design para a superfície da API.
Por que projetar APIs no Git
Você já usa Git para proteger o código. O contrato da API deve receber o mesmo tratamento.
Histórico
Quando alguém pergunta “quando adicionamos o parâmetro cursor?”, o comando abaixo responde:
git log -p -- api/openapi.yaml
Você vê o commit, a data, o autor e a mensagem que justificou a mudança.
Blame
Para descobrir quem alterou um campo específico:
git blame api/openapi.yaml
Isso conecta uma linha da especificação ao PR original e à discussão de revisão.
Rollback
Se uma alteração de contrato foi ruim, reverta o merge:
git revert <merge_commit_sha>
Depois disso, codegen, mocks e documentação podem ser regenerados a partir da versão revertida.
Revisão
Um pull request é o melhor lugar para discutir uma API antes de implementá-la. Revisores podem comentar diretamente na linha que adiciona um campo obrigatório, remove um enum ou muda um status code.
Essa é a base de um fluxo de trabalho de especificação de API baseado em Git: uma única fonte de verdade para frontend, backend, QA e documentação.
Ciclo de design de API git-native
O ciclo prático é:
- Criar uma branch.
- Alterar o contrato.
- Fazer commit.
- Abrir um pull request.
- Revisar e fazer merge.
- Implementar usando a especificação aprovada.
Exemplo: adicionar um endpoint para listar faturas de um usuário.
git checkout -b feat/api-invoices-list
Edite o arquivo OpenAPI:
# api/openapi.yaml
paths:
/users/{userId}/invoices:
get:
operationId: listUserInvoices
summary: List invoices for a user
parameters:
- name: userId
in: path
required: true
schema:
type: string
format: uuid
- name: status
in: query
required: false
schema:
type: string
enum: [draft, open, paid, void]
responses:
"200":
description: A page of invoices
content:
application/json:
schema:
$ref: "#/components/schemas/InvoiceList"
"404":
description: User not found
Faça um commit pequeno e específico:
git add api/openapi.yaml
git commit -m "Add GET /users/{userId}/invoices contract"
Abra o PR. O diff mostra exatamente o que mudou: caminho, operação, parâmetros e respostas.
Antes de qualquer handler existir, a equipe pode revisar:
- O nome do endpoint.
- O
operationId. - Os valores do enum.
- O uso de
404. - A necessidade de paginação.
- O formato da resposta.
Depois do merge, a implementação fica limitada ao contrato aprovado. Essa é a lógica do desenvolvimento de API spec-first: o acordo vem antes do código.
Estratégia de branching para contratos de API
Use uma branch por unidade lógica de mudança. Evite PRs com dezenas de endpoints.
| Tipo de alteração | Prefixo da branch | Exemplo | Peso da revisão |
|---|---|---|---|
| Novo endpoint | feat/api- |
feat/api-invoices-list |
Padrão |
| Campo aditivo | feat/api- |
feat/api-invoice-currency |
Leve |
| Breaking change | break/api- |
break/api-remove-legacy-id |
Pesado, requer aprovação |
| Correção na especificação | fix/api- |
fix/api-status-enum-typo |
Leve |
| Refatoração semântica zero | chore/api- |
chore/api-reorder-schemas |
Leve |
O prefixo ajuda revisores e automações. Uma branch break/api- deve acionar revisão mais rigorosa e análise de consumidores afetados.
Trunk-based ou Gitflow?
| Modelo | Melhor para | Impacto na API |
|---|---|---|
| Trunk-based | Entrega contínua, equipes pequenas ou médias | Contrato evolui em passos pequenos; menos conflitos |
| Gitflow | Lançamentos agendados ou regulados | Especificação pode divergir em develop; merges maiores |
Para a maioria das equipes, prefira trunk-based development:
git checkout -b feat/api-new-field
# altera a spec
git commit -m "Add currency field to invoice"
git push origin feat/api-new-field
# abre PR e faz merge em main
Branches curtas reduzem conflitos em YAML e tornam a revisão mais objetiva.
Como revisar design de API em pull requests
Um PR de especificação não é apenas uma checagem de sintaxe. Ele é uma revisão de design.
Use este checklist:
1. A mudança quebra consumidores existentes?
Breaking changes incluem:
- Remover campo.
- Renomear propriedade.
- Remover valor de enum.
- Tornar campo opcional em obrigatório.
- Alterar tipo de dado.
- Remover endpoint.
- Mudar estrutura de erro.
Exemplo de alteração aditiva:
parameters:
- name: status
in: query
schema:
type: string
- enum: [draft, open, paid, void]
+ enum: [draft, open, paid, void, uncollectible]
Adicionar uncollectible é menos arriscado do que remover void. Se um valor existente desaparece, clientes podem quebrar.
2. A nomenclatura é consistente?
Verifique se o novo endpoint segue os padrões existentes:
GET /users/{userId}/invoices
GET /users/{userId}/payments
GET /users/{userId}/subscriptions
Evite misturar estilos:
GET /user/{userId}/invoice-list
3. O modelo de erro é o mesmo?
Se a API já usa:
{
"code": "USER_NOT_FOUND",
"message": "User not found"
}
não introduza outro formato sem motivo:
{
"error": "missing user"
}
4. O diff é legível?
Mantenha o YAML estável:
- Ordene chaves de forma consistente.
- Não reordene arquivos inteiros em PRs funcionais.
- Separe refatorações de mudanças semânticas.
- Adicione novos paths em locais previsíveis.
Um diff pequeno recebe revisão real. Um arquivo inteiro reformatado esconde a mudança importante.
Do design ao desenvolvimento
Depois que o contrato entra em main, ele deve alimentar o restante do fluxo.
1. Gere código
Use ferramentas como openapi-generator para gerar clientes ou stubs.
Exemplo:
npx @openapitools/openapi-generator-cli generate \
-i api/openapi.yaml \
-g typescript-fetch \
-o generated/api-client
Para stubs de servidor:
npx @openapitools/openapi-generator-cli generate \
-i api/openapi.yaml \
-g nodejs-express-server \
-o generated/server
A lógica de negócio ainda é implementada pela equipe, mas os formatos de request e response vêm da especificação.
2. Gere mocks
Um mock server permite que o frontend comece antes do backend.
Exemplo com Prism:
npx @stoplight/prism-cli mock api/openapi.yaml
Agora consumidores podem chamar endpoints simulados com base no contrato aprovado.
3. Rode testes de contrato
Testes de contrato validam se o servidor real respeita a especificação.
Fluxo típico:
- Sobe a aplicação.
- Envia requests reais.
- Valida responses contra o OpenAPI.
- Falha o CI se houver divergência.
Isso impede o problema clássico: a especificação diz uma coisa, mas o servidor faz outra.
4. Gere documentação
A documentação de referência deve ser renderizada a partir do OpenAPI. Assim, quando o contrato muda, a documentação muda junto.
O princípio é sempre o mesmo: gere artefatos a partir da especificação confirmada. Não mantenha clientes, mocks ou documentação manualmente quando eles podem derivar do contrato.
Convenções de equipe que escalam
Um fluxo git-native funciona melhor quando a equipe define regras explícitas.
1. Escolha a estrutura dos arquivos
Para APIs pequenas, um único arquivo pode bastar:
api/openapi.yaml
Para APIs maiores, divida por recurso:
api/
openapi.yaml
paths/
users.yaml
invoices.yaml
components/
schemas/
invoice.yaml
user.yaml
Depois, empacote na build:
npx swagger-cli bundle api/openapi.yaml -o dist/openapi.yaml
2. Versione deliberadamente
Atualize info.version em mudanças relevantes.
Exemplo:
info:
title: Billing API
version: 1.4.0
Regra prática:
- Campo ou endpoint aditivo: versão minor.
- Correção sem mudança de contrato: versão patch.
- Breaking change: versão major, geralmente com novo prefixo como
/v2.
3. Mantenha um changelog
Use um CHANGELOG.md ao lado da especificação:
# Changelog
## 1.4.0
- Added `GET /users/{userId}/invoices`.
- Added `currency` field to `Invoice`.
O Git é preciso, mas o changelog é mais fácil para consumidores humanos.
4. Proteja a especificação com CODEOWNERS
Exija aprovação dos responsáveis pela API:
# .github/CODEOWNERS
/api/openapi.yaml @api-stewards
/api/paths/ @api-stewards
/api/components/ @api-stewards
Assim, qualquer alteração no contrato passa por revisão especializada.
5. Faça lint no CI
Exemplo com Spectral no GitHub Actions:
# .github/workflows/api-lint.yml
name: API Lint
on:
pull_request:
paths:
- "api/**"
jobs:
spectral:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Spectral
run: npx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity warn
Com lint no CI e CODEOWNERS, cada alteração recebe duas camadas de proteção:
- Regras automáticas.
- Revisão humana.
Armadilhas comuns e como evitá-las
Divergência entre especificação e código
Problema: o contrato diz uma coisa, o servidor responde outra.
Como evitar:
- Gere stubs e clientes a partir do OpenAPI.
- Rode testes de contrato no CI.
- Falhe a build quando a resposta real não bater com o schema.
PRs grandes demais
Problema: uma branch adiciona muitos endpoints e ninguém revisa de verdade.
Como evitar:
- Um endpoint ou uma mudança semântica por PR.
- Refatorações em PRs separados.
- Commits pequenos e mensagens claras.
Artefatos manuais
Problema: cliente, mock ou documentação escritos manualmente ficam desatualizados.
Como evitar:
- Gere clientes.
- Gere mocks.
- Gere documentação.
- Trate artefatos manuais como exceção.
Conflitos de merge em YAML
Problema: duas branches longas alteram a mesma região do arquivo.
Como evitar:
- Branches curtas.
- Ordem estável de chaves.
- Especificação dividida em múltiplos arquivos.
- Trunk-based development sempre que possível.
Onde o Apidog se encaixa
Você pode executar um fluxo git-native com editor de texto, Git e CLI. Mas muitas equipes querem uma interface visual para desenhar APIs sem abandonar o Git como fonte da verdade.
O Modo Spec-First do Apidog cobre esse cenário. Ele mantém o arquivo OpenAPI no seu repositório Git e oferece sincronização bidirecional: você pode editar no designer visual do Apidog ou no editor de código, mantendo consistência com o arquivo versionado.
O ponto principal é que o Git continua canônico. Branches, PRs, histórico e revisão continuam funcionando como descrito neste guia. O Apidog vira uma interface sobre a especificação, não um substituto para o repositório.
Consulte a documentação do Modo Spec-First para detalhes de configuração.
FAQ
O design de API git-native é apenas para OpenAPI?
Não. A disciplina se aplica a qualquer contrato baseado em texto:
- OpenAPI.
- AsyncAPI.
- Arquivos
.protodo gRPC. - GraphQL SDL.
Se o contrato pode ser versionado, comparado com diff, revisado em PR e revertido, ele pode seguir um fluxo git-native.
Como lidar com breaking changes?
Torne a quebra explícita:
- Use branch com prefixo
break/api-. - Atualize a versão principal.
- Exija aprovação via
CODEOWNERS. - Documente a mudança no changelog.
- Quando possível, adicione a nova estrutura ao lado da antiga e deprecie a versão anterior.
Exemplo:
break/api-remove-legacy-id
O nome da branch já comunica que a revisão precisa ser mais rigorosa.
A especificação deve ficar no mesmo repositório que o código?
Geralmente sim, quando a mesma equipe mantém API e implementação.
Vantagens:
- Um PR pode alterar contrato e handler.
- Testes de contrato rodam na mesma pipeline.
- A equipe vê implementação e especificação juntas.
Use um repositório separado quando a API for compartilhada por muitas equipes e precisar de versionamento independente.
Como evitar que especificação e código se distanciem?
Adicione testes de contrato ao CI.
O fluxo mínimo é:
# 1. sobe o servidor
npm run start:test
# 2. executa testes que chamam endpoints reais
npm run test:contract
A build deve falhar se uma resposta não cumprir o schema OpenAPI. Combinado com codegen, isso transforma divergência em erro de pipeline, não em bug de produção.
Conclusão
Design de API git-native é uma disciplina: trate o contrato como código-fonte, evolua em branches, revise em pull requests e gere artefatos a partir da especificação confirmada.
Comece pequeno:
- Coloque a especificação no repositório.
- Adicione lint no CI.
- Proteja arquivos de contrato com
CODEOWNERS. - Faça PRs pequenos.
- Gere clientes, mocks e documentação.
- Adicione testes de contrato.
A partir daí, o fluxo escala naturalmente. Cada merge em main passa a registrar não só uma mudança de código, mas uma decisão formal sobre a evolução da sua API.


Top comments (0)