Gestão de Açaí
Desenvolvido integralmente por Paulo, para um estabelecimento real da família.
Sistema financeiro em uso diário por funcionários de um estabelecimento real da família — uma usuária de baixa familiaridade técnica. Uma regressão em produção não é um bug abstrato: atrapalha o dia da loja, o fechamento de caixa e o controle de estoque.
O sistema já tinha ~296 testes e backup diário configurado. Mas nada impedia que código quebrado fosse ao ar: o Render fazia deploy direto a cada git push, sem rodar teste nenhum. A proteção existia só no papel.
O fechamento de caixa levava cerca de uma hora porque era feito manualmente, conciliando registros de formas de pagamento diferentes sem visibilidade consolidada. Com o módulo de caixa, o mesmo processo passou a levar ~10 minutos.
Django com CBVs e arquitetura em camadas (View → Service → Selector → Model); PostgreSQL no Neon (serverless, sem servidor para gerenciar); hospedagem no Render; GitHub Actions orquestrando lint, testes e deploy; backup diário via workflow agendado no GitHub Actions — pg_dump do endpoint direto do Neon → gzip → rclone → Google Drive → rotação dos 15 mais recentes. Nenhum artefato persiste no runner.
Pipeline de CI/CD com trava de deploy
- Problema
- Código quebrado podia ir direto ao ar. Push na main disparava deploy imediato no Render, sem rodar lint nem testes. Com uma usuária de baixa familiaridade técnica dependendo do sistema, qualquer regressão visível virava um incidente.
- Alternativas
- Confiança manual: rodar os testes localmente antes de fazer push. Funciona até o dia em que não funciona. Deploy automático com toggle: desligar o auto-deploy e fazer deploy manual — troca uma automação por outra, mais frágil.
- Escolha
- Pipeline no GitHub Actions: push/PR → Ruff (lint) → pytest contra Postgres real → gate de cobertura 80% → só com CI verde o job de deploy chama o Deploy Hook do Render. Branch protection na main torna os checks obrigatórios e fecha o push direto. autoDeploy: false no render.yaml desliga o deploy automático por push. Regra central: deploy só acontece depois de CI verde.
- Resultado
- Nenhum deploy acontece com CI vermelho. A main está sempre em estado deployável. O pipeline virou registro no repositório — qualquer pessoa que olhar o código entende exatamente o que precisa passar para ir a produção.
PostgreSQL real na CI, não SQLite
- Problema
- Testes que passam em SQLite podem falhar em PostgreSQL por diferenças de tipo, comportamento de constraints, migrations específicas de Postgres ou SQL que o SQLite não suporta. Um bug desse tipo só apareceria em produção.
- Escolha
- Service container de PostgreSQL efêmero no runner do GitHub Actions. Sobe ao lado do job de testes, recebe um health check antes dos testes rodarem, e é destruído ao final. Espelha exatamente o banco de produção (Neon/Postgres).
- Resultado
- ~296 testes rodando contra PostgreSQL real a cada push. Nenhum bug de banco só-em-produção após a mudança. O CI valida o mesmo ambiente que o usuário usa.
Backup diário via GitHub Actions, não cron no servidor
- Problema
- O roadmap original previa cron + rclone num servidor sempre ligado. No Render free isso não existe: a instância hiberna quando não recebe tráfego, o filesystem é efêmero e não há suporte a cron. Uma queda de dados financeiros sem backup é inaceitável.
- Alternativas
- Migrar para plano pago no Render (custo recorrente sem benefício técnico). Backup manual periódico (confiança humana, não confiança de sistema). VPS separado só para o cron (infraestrutura para resolver falta de infraestrutura).
- Escolha
- Workflow agendado no GitHub Actions (cron: '0 3 * * *'): pg_dump com endpoint direto do Neon (sem pooler/PgBouncer) → gzip → validação de tamanho mínimo → rclone para o Google Drive → validação do upload → rotação mantendo os 15 mais recentes. Nada persiste no runner efêmero. Validado em produção em 17/07/2026.
- Resultado
- Backup diário automatizado, sem servidor extra para manter. Rotação automática de 15 arquivos. O próprio GitHub Actions registra cada execução — auditável, sem dependência de infraestrutura adicional.
Arquitetura em camadas documentada antes do código
- Problema
- Sem fronteiras claras entre camadas, regras de negócio migram para as views, os testes ficam acoplados à camada HTTP e qualquer mudança de lógica exige alterar em vários lugares.
- Escolha
- View → Service (escrita) / Selector (leitura) → Model. Views não contêm regra de negócio. Services encapsulam a lógica de escrita com transações. Selectors isolam queries complexas. Regras de nomeação, paginação, índices e validações escritas em PROJECT_RULES.md antes do primeiro modelo.
- Resultado
- Testes de serviço sem dependência de request/response HTTP. Refatoração de query sem tocar em view. Novos módulos seguem o mesmo padrão sem instrução adicional.
pg_dump 16 incompatível com Neon PostgreSQL 18
- Sintoma
- O workflow de backup falhava com erro de incompatibilidade de versão: o runner Ubuntu vinha com pg_dump 16, mas o Neon rodava PostgreSQL 18. Dumping de versão inferior para banco superior não é suportado.
- Causa
- O GitHub Actions usa a versão do pg_dump instalada no runner por padrão, que não acompanha automaticamente a versão do banco de destino.
- Correção
- Instalação do pg_dump 18 via repositório PGDG e adição do binário ao início do PATH com echo 'caminho' >> $GITHUB_PATH. A versão correta é encontrada primeiro, sem conflito com a versão pré-instalada.
Pooler do Neon incompatível com pg_dump
- Sintoma
- pg_dump falhava com erro de protocolo ao usar o endpoint padrão do Neon (com PgBouncer). A conexão era estabelecida, mas o dump não completava.
- Causa
- PgBouncer (pooler de conexões) não suporta o protocolo de comunicação que pg_dump usa para executar dumps. O pg_dump requer conexão direta ao banco.
- Correção
- Usar o endpoint direto do Neon (sem pooler) na variável de ambiente do workflow. O Neon disponibiliza os dois endpoints — o com pooler para a aplicação e o direto para operações de administração.
Secret multiline rejeitado pelo GitHub
- Sintoma
- A string de configuração do rclone para o Google Drive tem múltiplas linhas. Ao tentar cadastrar via interface do GitHub, o secret era corrompido ou rejeitado.
- Correção
- Cadastrar via CLI: echo 'conteúdo multiline' | gh secret set RCLONE_CONFIG. O pipe preserva as quebras de linha que a interface do GitHub não aceita diretamente.
Rotação apagando backup antes de confirmar upload
- Sintoma
- Na primeira versão do workflow, a rotação dos backups antigos rodava antes da validação de que o novo arquivo tinha sido enviado com sucesso ao Drive. Uma falha de upload silenciosa apagaria o histórico sem novo backup válido.
- Correção
- Reordenar as etapas: upload → validação do arquivo no Drive (tamanho e existência) → só então rotação dos mais antigos. Falha em qualquer etapa de validação interrompe o workflow antes de apagar qualquer coisa.
Fechamento de caixa diário reduzido de ~1 hora para ~10 minutos. Na primeira semana do módulo de pedidos: +115 pedidos e +R$ 4,6 mil registrados pelo sistema.
~296 testes automatizados rodando contra PostgreSQL real a cada push, com gate de cobertura de 80%. Backup diário automatizado, validado em produção desde 17/07/2026, com rotação de 15 arquivos no Google Drive. Nenhum deploy com CI vermelho desde a implementação do pipeline.
Ter testes sem trava de deploy é ter uma rede de segurança com buracos. O CI/CD não é complexidade extra — é o que transforma testes em garantia real. E backup sem validação do upload não é backup: é um ritual que dá sensação de segurança sem a segurança de fato.