Voltar para Cases
CRM & Vendas

Plannus

CRM para equipes de vendas, com WhatsApp integrado, pipelines e negócios, automações, agenda, assinatura e um assistente de IA.

A virada

Plannus: o antes e o depois

O Plannus chegou à Bootcom em setembro de 2026 como um CRM em produção, com clientes usando, e uma API de 36 mil linhas coberta por 4 arquivos de teste. Antes de qualquer mudança, nosso time auditou o sistema inteiro e registrou 33 falhas, 7 delas críticas, cada uma com evidência. Em 10 dias de rodadas de correção, os bloqueadores foram fechados, cada lição virou uma guarda executável que reprova o build, e o sistema saiu de uma VM com deploy manual pro Cloud Run, com toda a infraestrutura em código. Os números abaixo foram apurados nos próprios repositórios do projeto e na auditoria registrada neles.

33

Falhas encontradas pela auditoria antes de qualquer mudança, 7 delas críticas, cada uma com evidência.

4 → 72

Arquivos de teste na API: de 11 casos de teste a 487, com e2e contra Postgres real.

10 dias

Da primeira à última rodada de correção, cada lote registrado com o que mudou e por quê.

0

Arquivos da API acima do teto de 500 linhas hoje. O maior tinha 2.698.

Arquitetura, antes e depois

Deploy manual com PM2 e nginx numa única VM
Cloud Run atrás de um balanceador global, tudo em Terraform
Schema alterado pelo ORM; 53 migrations que não criavam o banco do zero
synchronize desligado em todo ambiente; baseline e migrations que sobem um banco vazio
Sessão de 7 dias num token só, sem refresh nem revogação
Access token de 15 min e refresh rotativo, os dois em cookie httpOnly
Trabalho em segundo plano em memória, perdido a cada reinício
Outbox transacional: o trabalho é gravado antes da resposta e sobrevive a reinício
CORS aberto, sem cabeçalhos de segurança nem limite de requisições
CORS por allowlist, helmet e rate limit por rota
4 arquivos de teste pra 36 mil linhas de API
72 arquivos e 487 casos de teste, com e2e contra Postgres real
Nenhum CI, nenhum hook, nenhuma regra automatizada
CI com lint, tipos, testes, e2e, build, auditoria de dependências e 6 guardas
Angular 18 e 951 cores escritas à mão nos estilos
Angular 21 e design system com tokens; cor crua reprova o build
16 pontos

O que encontramos

Uma senha-mestra abria qualquer conta
O login aceitava uma senha fixa no lugar do hash de qualquer usuário, inclusive das contas que administram a plataforma inteira, sem registro nem alerta.
Cinco gateways de tempo real sem autenticação
Os WebSockets aceitavam qualquer origem e entravam na sala do workspace que o cliente pedisse, sem validar token nem vínculo.
Segredo do JWT com fallback público
Em quatro pontos, a ausência da variável de ambiente caía num literal conhecido. O boot seguia normal, assinando tokens que qualquer pessoa conseguiria forjar.
CORS aberto e sem cabeçalhos de segurança
A API refletia qualquer origem com credenciais, e a variável que listava as origens não era lida em lugar nenhum. Sem helmet, sem HSTS, sem nosniff.
Nenhum limite de requisições
Login, cadastro e recuperação de senha aceitavam tentativas ilimitadas, sem bloqueio nem backoff.
Token de 7 dias sem refresh nem revogação
Um único token valia uma semana. Trocar a senha, bloquear ou arquivar o membro não invalidava nada já emitido.
Quatro rotas públicas serviam arquivos de qualquer cliente
Anexos de negócios, mídias de conversas e avatares saíam sem checar sessão nem workspace. Bastava ter o identificador.
Schema do banco mudado pelo ORM, com 53 migrations inúteis
O synchronize reescrevia o schema a partir das entidades, e nenhuma das migrations criava a tabela base. Era impossível subir um ambiente novo a partir do repositório.
Uma transação em todo o código
O cadastro gravava usuário, workspace e vínculo em operações separadas. Uma falha no meio deixava um usuário órfão, que não conseguia nem logar nem se recadastrar.
Erro do banco ia direto pro cliente
Sem filtro de exceção global, um erro do driver escapava como 500 com nome de tabela, coluna e constraint, e cada rota devolvia um formato diferente.
Entidades devolvidas cruas
Os 29 controllers devolviam a entidade inteira, com relações. A lista de usuários do workspace entregava o hash de senha de todos os membros.
O maior service tinha 2.698 linhas e 64 métodos
Acumulava CRUD, kanban, merge de duplicados, upload, notas, envio de WhatsApp e automações, e era o ponto de acoplamento de quase todo o domínio. Havia 44 métodos com mais de 80 linhas.
Escopo de tenant lembrado à mão em 83 pontos
O padrão buscava o registro por id e só depois comparava o workspace. Uma omissão num método novo viraria vazamento silencioso.
4 arquivos de teste pra 36 mil linhas
Nada cobria autenticação, permissões, billing ou o motor de automação. Refatorar sem rede de segurança era aposta.
Sem CI, Dockerfile ou hook
Lint, build e teste dependiam de disciplina manual, e o lint acusava 2.561 problemas.
App sem camada de design nem camada de serviço
Zero variáveis CSS, 951 cores em hex nos estilos, treze componentes chamando a API direto e dez cópias de formatCurrency que discordavam entre si.
16 pontos

O que fizemos

Senha-mestra removida
O login voltou a comparar só o hash, e o caminho ganhou teste: a senha antiga responde 401 em qualquer conta.
WebSockets autenticados no handshake
Um gateway base valida a sessão, resolve o workspace pelo token e ignora o que o cliente envia. Os cinco herdam dele.
Ambiente validado no boot
Um schema zod exige os segredos em produção, rejeita o valor de exemplo e derruba o boot listando tudo que falta, de uma vez.
CORS por allowlist e helmet
As origens vêm da configuração, e os cabeçalhos de segurança vão em toda resposta. O X-Powered-By saiu.
Rate limit por rota
Limite global e limites dedicados nas rotas de autenticação, no assistente, no envio de chat e nos webhooks, contando o IP real atrás do balanceador.
Access de 15 minutos e refresh rotativo
Refresh opaco guardado como hash, reuso derruba todas as sessões, logout e troca de senha revogam. Os dois tokens em cookie httpOnly.
Arquivos só com sessão e workspace válidos
As rotas exigem autenticação e checam o vínculo antes de servir. Identificador de outro workspace responde 404 ou 403.
Baseline e synchronize desligado em todo ambiente
Uma migration de baseline gerada das entidades cria o schema num banco vazio. As antigas foram arquivadas, e o CI sobe o banco do zero antes dos e2e.
Fluxos multi-escrita em transação
Criar e mover negócio gravam o histórico na mesma transação, e o webhook escreve sob trava de linha. De uma transação pra nove.
Envelope e filtro de exceção globais
Toda resposta tem success, message e data. Todo erro tem código estável e detalhes por campo, e nenhuma mensagem de driver chega ao cliente.
Serialização com exclusão garantida
Campos sensíveis marcados com @Exclude e um interceptor próprio que quebra ciclos sem perder a exclusão. Há teste travando o hash de senha fora da resposta.
Services fatiados por responsabilidade
Os seis maiores viraram 44 arquivos, nenhum acima de 400 linhas, sempre começando pelos testes. O teto de 500 linhas é uma catraca que só desce, e o mapa de exceções está vazio.
Escopo de tenant verificado por guarda
Toda consulta a repositório precisa carregar o workspace ou constar numa lista com motivo escrito. A guarda reprova o build e afirma quantos arquivos leu.
487 casos de teste, 121 deles e2e
Segurança, relatórios e ciclo de vida do negócio rodam contra Postgres real no CI, e cada asserção é conferida por mutação.
CI nos dois repositórios e hooks locais
Lint sem warning, tipos, testes, e2e, build, auditoria de dependências, Docker e varredura de segredos. Pré-commit e pré-push rodam o mesmo lint em container.
Design system com tokens e services com contrato
219 variáveis de token, zero cor crua, um formatador só. As 28 rotas do painel carregam sob demanda, e o bundle inicial caiu de 404 pra 243 kB.
Os números

O que dá pra medir da entrega

Números apurados no próprio repositório do projeto, comparando o que existia antes com o que está no ar hoje.

Entrega
487

Casos de teste na API: 366 unitários e 121 e2e contra Postgres real, de 11.

482

Casos de teste no app, de 3 num arquivo gerado pelo CLI.

258

Rotas que a API expõe hoje, sob um envelope único e contrato OpenAPI exportado.

53

Índices no banco, de 26. As 12 entidades de tenant sem índice ganharam o seu.

6

Guardas executáveis na API que reprovam o build, mais 5 verificações estáticas no app.

11

ADRs registrando as decisões de arquitetura, 5 na API e 6 no app.

Arquivos de teste na API

arquivos de teste

De 11 casos de teste pra 487, 121 deles e2e contra Postgres real.

Casos de teste no app

casos de teste

De um arquivo gerado pelo CLI pra 84 arquivos de spec.

Maior arquivo da API

linhas

Teto de 500 linhas imposto por guarda. Nenhum arquivo acima dele, e o mapa de exceções está vazio.

Cobertura de linhas da API

48,7%
Linhas cobertasMedição registrada em 05/10/2026 com os e2e contra Postgres real. Os unitários sozinhos cobrem 13,8%.

Achados da auditoria, por severidade

  • Críticos7
  • Altos9
  • Médios12
  • Baixos5
Frente por frente

Segurança e acesso

Da senha-mestra à sessão em cookie, o que protegia menos do que devia e como ficou.

Senha-mestra removidaO login compara só o hash. A senha antiga responde 401 em qualquer conta, e há teste travando isso.
WebSockets autenticadosSessão validada no handshake e workspace resolvido pelo token, nos cinco gateways.
Sessão inteira em cookie httpOnlyPrefixos __Host- e __Secure-, Secure, SameSite=Strict. O JavaScript da página não enxerga token nenhum.
Refresh rotativo com detecção de reusoGuardado como hash. Reutilizar um refresh antigo derruba todas as sessões e fica na trilha de auditoria.
Ambiente validado no bootSegredo faltando ou valor de exemplo derruba o boot com a lista completa do que falta.
Superfície pública inventariadaToda rota @Public() está numa lista com motivo escrito e mecanismo próprio de proteção. A guarda reprova a que não estiver.
Permissões sem atalhoCargo não concede nada e mapa vazio significa exatamente o papel. Antes, mapa vazio concedia tudo.
Trilha de auditoriaLogin falho, reuso de refresh e redefinição de senha gravados em audit_logs.
Frente por frente

Confiabilidade em várias instâncias

O que se perdia a cada deploy numa VM, e como passou a sobreviver a reinício e a escala a zero.

Outbox transacionalWebhook, cópia de mídia e automação gravam o trabalho antes de responder. A execução sai por fora, com varredor de 30 s como rede de segurança.
Passo de automação não se perdeReivindica, executa e só então apaga, com FOR UPDATE SKIP LOCKED. Antes era apaga e executa.
Tarefa de boot numa instância sóAdvisory lock do Postgres evita que cada cold start repita seeds e re-registro de webhooks.
Escrita concorrente sob trava de linhaDois eventos do mesmo workspace não sobrescrevem mais um ao outro: transação curta, pessimistic_write, nada de rede dentro.
Health checks separados/health/live e /health/ready, este conferindo o banco, com shutdown hooks ativos.
Logs estruturadosPino em JSON, com correlação de requisição e segredos mascarados no caminho da URL.
Frente por frente

Banco e arquitetura

Como o schema passou a ter baseline, as chaves viraram chaves de verdade e os arquivos gigantes foram fatiados.

Baseline de migrationsGerada das entidades, cria o schema num banco vazio. synchronize: false em todo ambiente.
Chaves estrangeiras de verdadeColunas que apontavam pra outra entidade eram varchar em 18 casos, e 18 tabelas não tinham FK. Agora uuid uniforme, com FK.
Seis services-deus fatiadosViraram 44 arquivos, nenhum acima de 400 linhas, sempre começando pelos testes.
Teto de 500 linhas como catracaA guarda só deixa o teto descer, e o mapa de exceções está vazio.
Escopo de tenant por guardaConsulta sem workspace só passa com motivo escrito. A guarda afirma quantos arquivos leu.
Contrato OpenAPI conferido no CI do appCada URL que o front monta precisa existir no snapshot exportado da API.
Frente por frente

Front-end e design system

Do Angular 18 ao 21, e da cor escrita à mão ao token: o que mudou pra quem usa o sistema todo dia.

Angular 18 → 21Um major por vez, com o portão verde entre eles. Cada versão nova achou um bug real.
Design system com tokens219 variáveis de token no lugar de 951 cores em hex nos estilos. Cor crua reprova o build.
Componente desenha, service buscaTreze componentes chamavam a API direto; agora um. Dez cópias de formatCurrency viraram um formatador só.
Zero componentes acima de 400 linhasEram 21, somando 14.851 linhas acima da régua.
CSP sem script inlinescript-src 'self', e uma guarda no build reprova handler inline e folha presa em media=print.
URLs em portuguêsCerca de 50 rotas em inglês viraram interface em português, com redirecionamentos.
Idioma imposto por guarda nos dois lados453 identificadores em português no app e 207 na API saíram. A regra reprova o build.
Bundle inicial menorDe 404 pra 243 kB, com as 28 rotas do painel sob demanda e ECharts fora do chunk inicial.
Frente por frente

Qualidade que se sustenta

Guardas, CI, hooks e documentação: o que impede a reincidência depois que a Bootcom sai de cena.

6 guardas executáveis na API
Idioma, tamanho de arquivo, superfície pública, escopo de tenant, injeção com forwardRef e decimal em texto.
5 verificações estáticas no app
Contrato da API, CSP do build, idioma, tokens de design e arquitetura, todas no CI.
CI nos dois repositórios
Lint sem warning, formatação, tipos, testes, e2e com Postgres, build, auditoria de dependências, Docker e varredura de segredos.
Hooks de pré-commit e pré-push
Rodam o mesmo lint do CI, em container, sobre o que está no index.
Testes conferidos por mutação
Quebrar o código, ver o teste falhar, restaurar. Um teste que passa de qualquer jeito não documenta nada.
11 ADRs e memória viva
Decisões registradas com o motivo, pegadinhas que já custaram tempo e um journal por sessão, em português e inglês.
Documentação de 18 pra 56 arquivos
36 na API e 20 no app, com README que descreve o que o código faz de verdade.
O resultado

Como a operação ficou

O que mudou no dia a dia de quem usa o sistema, depois da virada de arquitetura.

  • Token roubado
    Válido por 7 dias15 min, revogável
  • Trabalho em segundo plano no deploy
    Perdido ou duplicadoGravado antes da resposta
  • Ambiente novo a partir do repositório
    ImpossívelMigrations do zero
  • Deploy
    À mão, numa VMTerraform e Cloud Run
  • Regra do projeto quebrada
    Passava no buildReprova o build
  • Erro de banco pro cliente
    Mensagem do driverCódigo de erro estável
Resumo

Plannus hoje

Tudo que mudou, reunido num único lugar.

Sistema em produção no Cloud RunInfraestrutura inteira em Terraform, no lugar da VM com deploy manual.
33 achados da auditoria tratados por fases7 críticos, 9 altos, 12 médios e 5 baixos, cada um com evidência.
487 casos de teste na API366 unitários e 121 e2e contra Postgres real, de 11.
482 casos de teste no appDe 3 casos num arquivo só.
Nenhum arquivo acima do teto500 linhas na API, 400 por componente no app, impostos por guarda.
Sessão inteira em cookie httpOnlyAccess de 15 minutos e refresh rotativo, fora do alcance do JavaScript.
Banco sob baseline e migrationssynchronize desligado em todo ambiente.
Angular 21 e design system com tokensZero cor crua nos estilos. Antes, 951.
11 guardas e verificações que reprovam o build6 na API e 5 no app, cada uma nascida de um erro real.
56 documentos entre ADRs, memória e journalDe 18, nos dois repositórios.
Dúvidas

Perguntas frequentes

Duas coisas que andam juntas. Colocamos agentes de IA pra trabalhar na operação da sua empresa: escolhemos com você onde começar, construímos e acompanhamos o resultado na Reliever, a nossa plataforma. E cuidamos do software por trás: auditamos e corrigimos sistemas que já existem e construímos MVPs do zero. Nos dois casos, programadores de verdade fazem o trabalho, com double check sempre.

Os três, no mesmo contrato. A consultoria define onde a IA gera resultado, os engenheiros constroem e colocam no ar, e a Reliever é onde os agentes rodam e são medidos. Plataforma sozinha costuma virar licença parada, e time sozinho não escala.

É a plataforma da Bootcom onde os agentes de IA são criados, operam e são medidos. Cada execução fica registrada e, onde o risco exige, uma pessoa do seu time aprova antes de o agente agir.

Pelo diagnóstico gratuito: uma conversa de 1 hora a 1 hora e meia com quem vai construir. Você preenche o formulário, a gente responde em até 10 minutos pra marcar, e você sai da conversa com prioridades claras, mesmo que não feche com a gente.

Até 30 dias, dependendo do escopo definido na etapa de diagnóstico. A IA acelera o que é repetitivo, uma pessoa revisa cada entrega, e testes, documentação e deploy automatizado entram desde o primeiro dia, pra o MVP se sustentar depois da validação.

Sim, é o caso mais comum. Passamos a IA pelo código, pela arquitetura e pela infraestrutura do que já existe pra encontrar falhas, arquitetura ruim, código ruim e desperdício, e entregamos um plano de melhorias por risco e impacto. Se quiser, a gente aplica as melhorias, sem precisar reescrever tudo do zero.

Em duas frentes. Na consultoria, a IA varre código, arquitetura e infraestrutura atrás de falhas que levariam dias pra achar na mão, e depois ajuda a aplicar as melhorias. No desenvolvimento, ela acelera o código repetitivo e os testes. Nos dois casos, programadores de verdade auditam o que a IA fez, com double check sempre: quem decide e responde pelo resultado é sempre uma pessoa do time.

Sim, oferecemos acompanhamento pós-lançamento. O escopo de suporte é combinado junto com a proposta, de acordo com a necessidade do projeto.

A stack é escolhida de acordo com o projeto, priorizando tecnologias maduras, bem documentadas e fáceis de manter, não a tecnologia mais nova por modismo.

Não. Se você não tem tudo definido, começamos pela etapa de diagnóstico, que existe justamente pra transformar uma ideia solta em escopo, prioridades e prazo claros.

Prazo e forma de pagamento são definidos junto com a proposta, geralmente dividido em etapas ligadas a entregas, não tudo adiantado nem tudo só no final.

Trabalhamos 100% remoto, com toda a comunicação por chamada, WhatsApp e repositório compartilhado. E, em Natal (RN), nossa cidade, podemos encontrar você pessoalmente: reunião, apresentação ou acompanhamento do projeto.

Sim, sem problema. Se o projeto exigir, assinamos o NDA antes mesmo da primeira conversa técnica mais aprofundada.

Dá. Começamos com a auditoria com IA do que já existe, o mesmo processo da consultoria, pra entender o estado real do código antes de assumir a manutenção ou continuar o desenvolvimento.

Sim. Você pode contratar suporte contínuo ou chamar a gente pontualmente pra novas funcionalidades, sem contrato de fidelidade obrigando isso.

Quer ser o próximo case da Bootcom?

Conte se você tem um sistema pra auditar ou um MVP pra construir do zero, e vamos conversar.

Entre em contato