Pular para o conteúdo
Blog AreaCli

Tecnologia

Desenvolvimento com IA na prática: o CRM implementado

Da especificação ao sistema no ar: a stack, cada critério de aceite como teste e o defeito de desempenho que só existia com os limites de produção.

Por Paulo Castilho 8 min de leitura

Gráfico de barras: tempo para conferir uma planilha de 200 mil linhas, com a meta da especificação de 2 minutos. No ambiente de testes, com 2 CPUs: 12,9 segundos. Com os limites da demonstração, antes da correção: mais de 10 minutos, quando a medição foi interrompida. Com os limites da demonstração, depois da correção: 23,8 segundos.
Neste artigo

O primeiro incremento do CRM de cobrança está no ar em crm.areacli.com. Ele importa a carteira por planilha, mostra devedores, títulos, faixas de atraso e o histórico de cada alteração, e exige login. Nesta parte estão as escolhas técnicas, feitas depois da especificação aprovada, e o que aconteceu quando o código encontrou o ambiente real. O código foi escrito pelo Claude Code, assistente de programação da Anthropic. As decisões, inclusive as que contrariaram a recomendação dele, foram minhas.

O resumo: as 29 regras e os 26 critérios de aceite da especificação funcional viraram testes automatizados, e todos passaram. Mesmo assim, três defeitos chegaram até a véspera da publicação, e um deles teria derrubado a demonstração na primeira planilha grande.

A stack, escolhida por último#

A linguagem, o banco e a arquitetura só foram discutidos depois de a especificação estar aprovada. A ordem não é capricho: as escolhas técnicas dependem do que o sistema precisa fazer. Login com perfis, vários credores mais adiante, importação de 200 mil linhas em até 2 minutos e telas acessíveis eram requisitos conhecidos antes de qualquer opção técnica aparecer.

DecisãoOpções apresentadasEscolha
Como as telas são montadasHTML gerado no servidor; aplicação JavaScript com API; framework JavaScript completoHTML no servidor, com htmx para atualizar partes da tela
Linguagem e frameworkPython com Django; Go; C# com ASP.NET; TypeScript com NodePython com Django
Linha do Django6.1, a mais nova; 5.2, de suporte longo6.1
Fila para tarefas em segundo planoProcrastinate, que usa o próprio PostgreSQL; um processo próprio; django-tasksProcrastinate

A favor do HTML gerado no servidor pesou a acessibilidade: HTML semântico funciona com leitor de tela e teclado sem esforço extra, e permite uma política de segurança de conteúdo (CSP) que proíbe qualquer script embutido na página. A favor do Django pesou o que ele já traz pronto: login, permissões, formulários e migrações de banco, que os próximos incrementos vão usar.

A primeira lista não tinha o Flask. A ferramenta de perguntas comporta quatro opções, e a vaga do Python foi para o Django sem aviso. Perguntei por quê. A comparação veio depois: o Flask é mais explícito, mas cada peça que o Django traz pronta viraria uma biblioteca a escolher, travar e auditar. Fiquei com o Django. A omissão ficou registrada na decisão técnica, porque uma lista de opções cortada sem aviso já embute uma escolha.

Duas regras do projeto em conflito#

O projeto tem uma regra antiga: só usar versões de software publicadas há pelo menos duas semanas. Ela protege contra o pacote adulterado ou defeituoso, que costuma ser descoberto e retirado nos primeiros dias.

Na hora de travar as versões, as mais novas do Django tinham 12 dias. Elas corrigiam quatro falhas de segurança, uma delas de negação de serviço. Seguir a regra à risca significaria publicar o sistema com falhas conhecidas e documentadas. A saída foi uma exceção escrita e com condições:

  • só vale para correção da mesma linha de versão (6.1.1 para 6.1.2), nunca para versão nova de recursos;
  • as notas oficiais precisam citar correção de segurança;
  • o pacote vem da fonte oficial, com hash conferido;
  • a adoção fica registrada no diário do projeto.

A regra foi posta à prova no mesmo dia. Pedi o Python 3.15. A verificação mostrou que ele tinha 9 dias e que o Django ainda não o declarava suportado. A exceção recém-criada não cobria o caso, porque é versão nova, não correção. Ficou o Python 3.14, e o 3.15 virou pendência com critério objetivo: entra quando o Django declarar suporte e a versão tiver duas semanas.

Cada critério de aceite virou um teste#

A especificação descreve cada critério no formato “dado, quando, então”. Na implementação, cada um virou um teste que abre um navegador de verdade (o Playwright), faz o que o usuário faria e confere o resultado na tela. O critério do batimento, por exemplo, diz que a conferência informa os títulos que serão retirados, com quantidade e valor, antes de qualquer gravação. O teste correspondente:

def test_c08_batimento_mostra_e_retira_os_ausentes(logado, live_server):
    importar(logado, live_server.url, [linha("A", "100,00"), linha("B"), linha("C", "50,00")])
    importar(logado, live_server.url, [linha("B"), linha("D")], modo="Batimento", confirmar=False)
    expect(linha_tabela(logado, "Títulos que serão retirados")).to_contain_text("2 · R$ 150,00")
    assert Titulo.objects.filter(situacao="retirado").count() == 0

Três cuidados tornaram essa correspondência confiável:

  • Os exemplos da especificação são os dados do teste. “Hoje = 18/10/2026, vencimento 10/09/2026, 38 dias de atraso” está no teste com esses números. Para isso, o sistema ganhou uma data de referência configurável, usada só nos testes. Sem ela, os testes passariam num dia e quebrariam no seguinte.
  • Acessibilidade verificada em toda tela. Cada tela visitada pelos testes passa pelo axe-core, que confere as regras da WCAG 2.2 nível AA. Qualquer violação reprova o teste.
  • A CSP também é verificada. Se o navegador bloquear algum recurso por violar a política de segurança, o teste falha.

No total são 94 testes: 64 de regras isoladas, 26 critérios no navegador, 2 dos próprios instrumentos e 2 das telas no tamanho de um celular.

Testar o teste#

A suíte inteira passou de primeira, sem nenhuma violação de acessibilidade. Isso é motivo de desconfiança, não de comemoração. Um verificador de acessibilidade que não roda, ou uma política de segurança que não está ativa, também produzem zero erros.

Por isso, foram acrescentados dois testes que só passam se os instrumentos funcionarem: um planta uma imagem sem texto alternativo e exige que o axe acuse a violação; o outro tenta executar um script embutido e exige que a política de segurança o bloqueie. Os dois instrumentos funcionavam. Sem esses testes, eu não teria como saber.

O defeito que só existia com os limites de produção#

A especificação pede que uma planilha de 200 mil linhas seja conferida em até 2 minutos. No ambiente de testes, com 2 processadores, levou 12,9 segundos.

O plano técnico mandava medir também com os limites reais da demonstração, num ambiente descartável idêntico: 1 processador para o banco, num servidor que divide a máquina com outros serviços. Ali, a mesma conferência passou de 10 minutos e foi interrompida.

A causa estava numa única consulta, a que procura títulos repetidos no arquivo. As linhas entram numa tabela de preparação por carga em massa, e uma tabela recém-carregada não tem estatísticas. Sem elas, o PostgreSQL estimou que a tabela tinha uma linha e escolheu um plano que compara cada linha com todas as outras. Com 200 mil linhas, são bilhões de comparações. No ambiente de testes o plano tinha saído bom, e o motivo não ficou esclarecido; é justamente o tipo de diferença que só uma medição no ambiente real revela.

A correção é conhecida e está na documentação do PostgreSQL: rodar ANALYZE depois de qualquer carga em massa. A consulta também foi reescrita para não depender do plano escolhido. Com os mesmos limites da demonstração, o resultado ficou assim:

PlanilhaConferênciaConfirmaçãoMeta
10 mil linhas2,0 s5,3 s30 s para conferir; lista em até 1 s (medido: 0,09 s)
200 mil linhas23,8 s41,5 s2 minutos para cada etapa

Este é o caso típico do defeito que passa batido num projeto comum. Os testes passam, a homologação usa uma planilha pequena, e o problema aparece quando o primeiro cliente grande envia o arquivo. Ele foi encontrado antes por duas razões: a especificação tinha uma meta de tempo que dava para medir, e o plano mandava medir com os recursos reais.

O que os testes não pegaram#

Dois defeitos passaram por todos os 94 testes e só apareceram em capturas de tela:

  • As barras do gráfico de faixas de atraso não apareciam. Em português, o framework formata o número da largura com vírgula (“4,7”), e o desenho vetorial não aceita vírgula. A página continuava acessível, porque a tabela ao lado tem os mesmos números. Só estava errada.
  • A ficha do devedor rolava para o lado no celular. Um texto escondido para leitores de tela escapava da tabela e alargava a página para 566 pixels numa tela de 390.

Os dois ganharam testes próprios. O segundo revelou ainda que as tabelas roláveis não podiam ser alcançadas pelo teclado, outro problema que o verificador automático só acusa na largura de um celular.

Dez lacunas devolvidas à especificação#

Uma especificação com 29 regras, revisada e aprovada, ainda deixou dez pontos sem definição. Alguns exemplos:

  • Linhas em branco no meio da planilha contam como recusadas ou são ignoradas?
  • Um telefone vazio na carga nova apaga o telefone que já estava cadastrado?
  • O valor de cada faixa no resumo soma os títulos daquela faixa ou o total dos devedores que estão nela?
  • A busca por “joão” deve encontrar “Joao”?

Num projeto comum, cada uma dessas perguntas seria respondida em silêncio por quem escreve o código. Aqui, o código implementou o comportamento mais conservador, e as dez voltaram para a especificação como decisões em aberto, com dono, prazo e a área que as decidiria numa empresa: operação, financeiro, jurídico ou TI.

Em números#

ItemResultado
Regras da especificação29
Critérios de aceite automatizados no navegador26 de 26
Testes no total94
Violações de acessibilidade (WCAG 2.2 AA) nas telas testadas0
Defeitos encontrados antes da publicação3, dos quais 2 visuais e 1 de desempenho
Lacunas devolvidas à especificação10
Memória em uso na demonstração, em repouso210 MB, somando aplicação, fila e banco

O que vem a seguir#

A demonstração está publicada, mas por enquanto o acesso é restrito. O próximo incremento traz os perfis de gestor e operador, a gestão de usuários pela tela e o usuário de demonstração público, com dados fictícios recriados todo dia. Antes dele, as dez lacunas precisam ser decididas.

Leia também