Contratos antes do código
Escreva a interface, o schema e os casos de falha antes da primeira linha de implementação. É a revisão de design mais barata que existe.
A hora mais produtiva de qualquer projeto que liderei é aquela em que ninguém escreve código de implementação. É a hora em que escrevemos o que um componente vai aceitar, o que vai devolver, o que vai emitir e como vai falhar, e depois discutimos isso antes de existir o corpo de uma única função.
Isso não é sobre documentação e não é sobre cascata. É sobre fazer o design onde as mudanças são baratas, num arquivo de texto, em vez de onde são caras, em três serviços e uma migração.
O que é um contrato
Um contrato é o comportamento externo completo de um componente, escrito de forma que duas pessoas que nunca conversam consigam construir os dois lados. Para uma API HTTP, é um documento OpenAPI. Para um evento, é o schema do payload com o significado de cada campo. Para um módulo dentro de um monólito, é a interface TypeScript da superfície pública mais um parágrafo por função dizendo o que acontece em caso de falha.
As partes que mais importam são as que os times pulam. Quais erros isso pode devolver e o que cada um significa para quem chama? Quais campos são opcionais agora e quais sempre estarão presentes? Qual é o comportamento de idempotência quando a mesma requisição chega duas vezes? Quais são os limites: tamanho de página, tamanho de payload, taxa? Qual é a garantia de ordem, se houver? Um contrato sem isso é um caminho feliz com nome.
Por que antes do código
Três motivos, e já vi os três pouparem semanas.
Primeiro, o contrato expõe desacordos enquanto são baratos. O frontend esperava uma lista; o backend planejou um cursor paginado. O time mobile precisava do total; o backend não planejava calculá-lo. Descoberto numa revisão de contrato, é uma conversa de dez minutos. Descoberto depois de os dois lados entregarem, é uma nova versão e um pedido de desculpas.
Segundo, o contrato destrava o trabalho em paralelo. Uma vez que o formato está acordado, o frontend constrói contra um mock, o backend constrói contra um teste, e o teste de integração pode ser escrito por quem estiver livre. Sem o contrato, todo mundo espera pelo lado mais lento.
Terceiro, o contrato é o teste. Um schema pode ser validado em tempo de execução. Um documento OpenAPI pode gerar um cliente e um conjunto de testes de contrato que falham o build quando a implementação desvia. O artefato de design vira a coisa que mantém a implementação honesta, permanentemente.
A revisão de contrato
A revisão é onde está o valor, e deveria levar menos de uma hora para a maioria dos componentes. Faço um conjunto fixo de perguntas.
- O que acontece quando cada entrada obrigatória está faltando, vazia ou malformada?
- O que quem chama vê quando a dependência por trás disso está fora?
- Qual é a resposta para a mesma requisição duas vezes?
- Quais campos serão adicionados depois, e como os clientes antigos vão sobreviver a eles?
- Qual é o maior payload, a lista mais longa, o caso mais lento, e o que acontece nesse limite?
- Quem consome isso, e essa pessoa leu?
A última pergunta é a mais pulada. Um contrato que ninguém do lado consumidor leu é um chute. Peço que um consumidor nomeado aprove, mesmo informalmente, antes de a implementação começar.
Onde os contratos dão errado
A primeira falha é escrever o contrato depois do código e chamar de documentação. Isso produz uma descrição precisa do que a implementação por acaso fez, incluindo seus acidentes, e nenhuma pressão de design.
A segunda é especificar demais. Um contrato que dita estrutura interna, colunas de banco ou escolhas de implementação não é um contrato, é um documento de design fantasiado, e prende quem implementa à primeira ideia de quem revisou. O contrato deveria descrever comportamento visto de fora e nada mais.
A terceira é deixar apodrecer. Um contrato que não é imposto por testes desvia em um mês. Se o schema não valida requisições em tempo de execução e o CI não compara a implementação com a especificação, o contrato é um desejo.
Escreva a interface primeiro. Discuta por uma hora. Faça um consumidor ler. Gere os testes a partir dela. Depois escreva o código, que será menor e mais claro do que seria, porque as decisões difíceis já foram tomadas.