•
4 min de leitura
Plataforma de Finanças e Risco de Crédito Agrícola
Sistema privado de cliente. O cliente e os dados ficam em sigilo; posso mostrar o código e o pipeline em uma entrevista.
NestJS · TypeScript · Prisma · PostgreSQL · Redis · Next.js · Python · Jest · Playwright · GitHub Actions

Uma plataforma multi-tenant para uma consultoria do agronegócio. Ela projeta receita, custos, EBITDA, ponto de equilíbrio, fluxo de caixa e endividamento de uma propriedade rural, avalia o risco de crédito e gera os relatórios entregues ao cliente. Dois desenvolvedores trabalharam nela; escrevi cerca de 85% dos commits, incluindo todo o pipeline de CI.

O problema

Esses números alimentam decisões de crédito, e nada no sistema conseguia mostrar que estavam certos. Havia testes unitários, mas nenhum teste partia de uma fazenda cuja resposta fosse conhecida de antemão.

Um caso calculado à mão, antes de confiar no motor

Escrevi uma fazenda sintética com números simples o bastante para conferir no papel: 1.000 ha, 60 sacas/ha, R$ 120 por saca e duas categorias de custo. A receita esperada é R$ 7,2 milhões. A safra é 2099, para a fixture nunca colidir com dados reais.

Rodar esse caso contra a API revelou duas coisas.

Dois endpoints se contradiziam. Projeção de receitas, DRE e fluxo de caixa reportavam R$ 7,2 milhões de receita para a organização. Resumo de produção e indicadores financeiros reportavam zero em todos os KPIs. Esse segundo par escolhia a “safra atual” pelo ano do calendário, então qualquer organização com dados fora da safra corrente veria um dashboard zerado ao lado de relatórios cheios. Registrei isso como divergência conhecida dentro do próprio teste, para que quem corrigir o comportamento tenha que atualizar o teste de propósito.

Valores monetários em ponto flutuante. Medi em vez de supor: 142 de 192 combinações plausíveis divergiam da aritmética decimal exata, na ordem de 5,6 × 10⁻⁹ reais. Arredondado a centavos, o resultado estava certo. O risco real eram as comparações por igualdade exata, então isso entrou como mudança planejada, não como reescrita de emergência.

Testes que não conseguem tocar o banco errado

  • O setup de e2e se recusa a rodar se TEST_DATABASE_URL não apontar para um banco cujo nome termine em _test ou _audit.
  • Os testes e2e de API (Jest + Supertest) cobrem um cenário de negócio por suíte, em um PostgreSQL 16 descartável no CI: 13 suítes, 130 casos de teste.
  • O isolamento entre clientes é testado diretamente: outro cliente chamando a mesma rota recebe 403.

O pipeline como quality gate

Escrevi todos os 12 jobs do workflow no GitHub Actions:

  • qualidade de backend e frontend (tipos, testes unitários, build)
  • e2e de autenticação
  • finanças golden: verifica o checksum das fixtures e depois roda os contratos golden e shadow
  • contrato de relatório: refaz a planilha exportada e a reconcilia após o recálculo nativo
  • migrations, além de backup e restore
  • suíte Playwright do dashboard (projeto de setup, depois desktop, tablet e mobile), com traces e screenshots como artefatos
  • testes do serviço de relatórios em Python
  • lint ratchet: a dívida de lint legada pode diminuir, nunca crescer
  • teste de carga: cinco sessões por quinze minutos contra serviços isolados
  • uma verificação agregada obrigatória

O caso golden fecha no CI: produção de 60.000 sacas, receita de R$ 7,2 milhões, custo de R$ 4,5 milhões, EBITDA de R$ 2,7 milhões, ponto de equilíbrio em 37.500 sacas e diferença de R$ 0,00 entre caixa e balanço.

Trocar o motor financeiro sem big bang

O novo motor de projeção entra em três modos, controlados por FINANCIAL_ENGINE_MODE: legacy, shadow e canonical. Em shadow, a resposta continua vindo do motor antigo, e cada diferença vira log estruturado, correlacionado por X-Request-ID e sem dados pessoais.

A promoção para canonical exige fixtures com checksum, comparação em shadow com dados representativos de staging, classificação de cada diferença (correção esperada, diferença tolerada ou regressão) e aprovação explícita de finanças e do product owner. A versão antiga da API só pode ser removida depois de 30 dias seguidos sem nenhuma chamada legada. Qualquer chamada reinicia a contagem.

O que eu faria em seguida

  • Migrar os campos monetários para Decimal.
  • Tornar a verificação agregada um status check obrigatório na proteção de branch, que ainda não está configurada.