Case study · Product Design end-to-end

Construí um ERP inteiro com IA. A decisão mais importante foi não colocar IA dentro dele.

Como eu levei um distribuidor de hortifrúti do papel e da calculadora até um sistema operacional próprio, da pesquisa de campo à segurança em produção.

Quadro de avisos do galpão com os pedidos em papel colados Antes · o quadro de avisos
Fila de Pedidos do sistema, com as colunas Aguardando, Separando, Separado, Conferido, Entregando e Finalizado Depois · a Fila de Pedidos
O estado inicial do projeto, e o que o substituiu. À esquerda, onde os pedidos moravam: papel colado num quadro. À direita, a mesma informação com estado, urgência e tempo decorrido — e sem depender de ninguém lembrar de tirar o papel.

Todo mundo em 2026 tem um projeto feito com IA. Quase todos são genéricos, e quase nenhum tem um usuário.

Este é específico. Foi desenhado para uma operação real, com nome, endereço e funcionários cadastrados. E o gancho que me interessa não é que eu usei IA para construí-lo. É que, tendo IA disponível de graça no meu editor, eu decidi não colocar nenhuma IA dentro do produto. É a decisão que eu mais gosto de defender.

Antes dela, o caminho inteiro: como eu descobri o problema, o que a pesquisa revelou que ninguém tinha pedido para resolver, como as decisões foram tomadas e registradas, e o que existe hoje de código, de banco e de segurança.

Assista ao vídeo em que eu falo sobre este projeto. “Eu fiz um ERP sozinho com Claude Code” · 21 min

01

O negócio, e o que ele exige de um sistema

O NR compra hortifrúti no CEASA de Curitiba e revende em Guarapuava para supermercados, restaurantes, hotéis e pessoa física. O diferencial competitivo dele é logística própria até o CEASA, o que garante frescor e variedade que o concorrente local não alcança.

A operação inteira, da compra à entrega, rodava em papel, calculadora, WhatsApp e planilha. O estoque era “pelo olho”.

Vista de cima do galpão do NR Hortifruti, com caixas empilhadas em paletes, caminhão na doca e funcionários trabalhando
O galpão em Guarapuava num dia comum. Caixa empilhada em palete, caminhão encostado na doca, mercadoria mudando de lugar o tempo todo. É esta operação que o sistema precisava descrever sem atrapalhar.

E ela tem duas características que definem tudo o que veio depois.

Variabilidade extrema. O preço de um produto muda em quatro horas. A safra muda a qualidade. Um fornecedor some. O peso de uma caixa varia. O mesmo produto é comprado em caixa e vendido em quilo, unidade ou caixa fechada, dependendo do cliente.

Conhecimento na cabeça das pessoas. O que comprar, quanto comprar, qual cliente aceita qual produto, qual horário entregar, o que “abacaxi 2” significa para aquele cliente específico. Nada disso estava escrito em lugar nenhum.

Uma operação assim não cabe num molde pronto. Foi daí que saiu a régua do projeto inteiro.

A operação da empresa não deve mudar. O sistema deve mudar para a operação da empresa.

02

A descoberta: campo, não briefing

Não houve briefing. Houve galpão.

Pedido manuscrito numa folha de agenda presa a uma prancheta, sobre a mesa ao lado de um teclado
Um dos artefatos que a pesquisa analisou. Repare no que nenhum formulário captura: “vagem se tiver (bem fresquinha)” e “quiabo (se tiver fresquinho)”. Condicional e critério de qualidade, na mesma linha da quantidade.
MétodoO que rendeu
Observação presencial recorrente Artefatos que ninguém menciona numa entrevista: a folha A4 do comprador, o quadro de avisos, a prancheta de assinatura, as 4 balanças. E os padrões que só aparecem vendo o trabalho acontecer:
  • as diferentes formas de um cliente fazer o pedido, e as diferentes formas de um pedido chegar ao galpão: impresso ou escrito à mão
  • frutas muito parecidas, com variedades diferentes, que se confundem entre si na hora de separar
  • métodos incorretos de conferência
  • métodos de compra e venda muito variáveis para o mesmo produto
Entrevista por função
comprador, atendente, separador, conferente, precificação
Cada persona do sistema saiu de uma pessoa real com um problema real
Transcrição de áudioA lógica de precificação foi capturada assim: 4 regras de cálculo que só existiam na cabeça de uma pessoa
Análise de artefato físicoA planilha, o CSV, a folha do comprador
Análise do dado legado72.992 linhas de histórico de vendas do sistema antigo, importadas e analisadas
Documentação AS-IS / TO-BEDois documentos longos escritos antes de qualquer linha de código

A pergunta que abriu o projeto

Na primeira conversa com o dono, eu não perguntei quais funcionalidades ele queria. Perguntei:

“O que acontece com a operação se você faltar amanhã?”

A resposta era: ela para. O comprador é o único que negocia, e negocia por WhatsApp com uma rede de fornecedores que só ele conhece. A atendente é a única que sabe traduzir o pedido informal de cada cliente. A conferente é a única que sabe qual cliente aceita entrega a que horas.

Isso reposicionou o projeto. O produto não é “um sistema de pedidos”. É a externalização do conhecimento tácito de uma operação inteira, sem tirar dela a flexibilidade que a mantém competitiva.

O que a pesquisa revelou, e que ninguém pediu para resolver

Estes são achados, não requisitos.

O pedido só nascia no sistema na hora da pesagem

A atendente recebia por WhatsApp, escrevia à mão num papel e colava num quadro de avisos. O separador pegava o papel, coletava os produtos, ia até a balança e só ali criava o pedido no sistema, digitando o que ele mesmo tinha pesado. Entre o cliente pedir e o sistema saber existia um buraco de horas cuja única fonte de verdade era uma folha de papel.

A conferência conferia a coisa errada

A conferente pegava a folha impressa pelo separador e conferia os produtos contra o que o separador tinha pesado. Nunca contra o que o cliente tinha pedido.

Pare um segundo nisso. Se o separador esquecesse um item, a conferência batia perfeitamente, porque o item esquecido não estava na folha. Era uma etapa de controle de qualidade matematicamente incapaz de detectar a falha mais comum do processo. Ninguém no NR tinha percebido, porque de dentro do processo ele parece correto.

Esse é o achado mais importante do projeto, e ele não veio de nenhuma pergunta que me fizeram.

A prioridade era física, e por isso era burlável

Não havia campo de prioridade. A atendente colava o papel mais urgente mais em cima no quadro, e os separadores deviam pegar de cima para baixo. A regra de negócio existia, implementada em fita adesiva.

Só que ninguém estava olhando. Como o quadro é físico e a escolha é livre, o separador podia pegar outro pedido: um mais fácil, ou um bem longo que não era prioritário. Um pedido longo é uma forma de ficar ocupado por muito tempo sem entregar o que era urgente.

Esse é o padrão que todo processo puramente físico sofre: onde existe brecha, alguém encontra o caminho de trabalhar menos. A prioridade no quadro não era ignorada por indisciplina, era ignorada porque nada no processo tornava a escolha visível.

O sistema não sabia quem tinha feito o quê

As 4 balanças tinham um login compartilhado. Para saber quem separou um pedido, era preciso ir olhar a prancheta onde o separador escrevia o nome dele à mão.

A matemática era feita na calculadora

Para pesar um pallet com 20 caixas, o separador contava as caixas, somava as taras na calculadora, somava a tara do pallet, subtraía do peso bruto e digitava o resultado. A cada produto. A cada pedido.

O comprador não tinha estoque para consultar, e não queria ter

Este achado é contraintuitivo, e é o meu favorito depois da conferência cega. Eu entrei na conversa achando que o problema era “ele não sabe o estoque”. A resposta foi:

“Não importa. Se eu tiver 100 kg de batata e a batata estiver ruim, ele vai comprar do mesmo jeito.”

O comprador sempre olha o produto fisicamente antes de comprar, porque a decisão dele é sobre qualidade, não sobre saldo. Isso matou um requisito que parecia óbvio, o dashboard de estoque como gatilho de compra, e redefiniu o módulo inteiro: a requisição começa com ele olhando o galpão, e o sistema é o bloco de anotação, não o oráculo.

Tela de recebimento de compra com aviso de conclusão com divergência, comparando quantidade pedida e recebida por fornecedor
O outro lado da mesma operação, e o motivo do princípio que vem a seguir. 55 itens recebidos, 30 com divergência: pediu 30 caixas de batata e chegaram 25; pediu 20 de couve-flor e chegaram 39. A diferença entre o combinado e o que desce do caminhão não é exceção — é a terça-feira.
03

O princípio que definiu o produto

Toda a descoberta convergiu para uma frase, escrita assim no documento inicial:

“Nós temos regras e temos também costumes. Existem critérios que geralmente são construídos em ERPs e são boas práticas. Só que tem coisas que não dá para colocar no sistema. Não dá para ser tão perfeito quanto dizem os manuais. Precisamos fazer um sistema que abrace o fluxo deles e faça com que continuem trabalhando praticamente da mesma forma, só que muito mais fácil. Que não trave, que não faça o usuário parar e perder minutos lendo, aprendendo, tentando. Tem que ser quase instantâneo.”

O sistema abraça o fluxo de trabalho existente. Ele não força a operação a se moldar a ele.

Isso tem três consequências práticas.

(a) Digitalizar o artefato, não substituí-lo por um conceito novo. O quadro de avisos virou a Fila de Pedidos. O papel mais em cima virou o badge “Urgente”. A prancheta virou o registro de quem assumiu o pedido. O usuário reconhece o próprio processo na tela, e não precisa aprender um modelo mental novo.

(b) Onde o processo humano é o diferencial competitivo, o sistema fica de fora. A negociação do comprador com cerca de 50 fornecedores acontece por WhatsApp e vai continuar acontecendo por WhatsApp. Trazer isso para dentro, “agora ele entra e faz uma cotação”, quebraria a coisa que faz o negócio funcionar. O sistema começa depois da negociação, no instante em que ela vira uma instrução de compra.

Essa é a decisão mais madura do projeto: reconhecer onde o produto não deve entrar.

(c) O sistema corrige o que estava errado, mas só o que estava errado. A conferência cega foi mudada, porque estava estruturalmente quebrada. A negociação não foi tocada, porque estava certa.

04

Definição: três esferas e personas com restrição física

A arquitetura de informação saiu da operação, não de um template de ERP.

EsferaQuemO que acontece
Compras e RecebimentoGerente, Comprador, Conferente de RecebimentoDefine o que comprar, negocia fora do sistema, registra a compra e conta o caminhão. É aqui que o estoque nasce
Pedidos e AtendimentoAtendenteRecebe o pedido do cliente, cria no sistema, precifica
GalpãoSeparador, Balança, Conferente de Entrega, MotoristaSepara, pesa, confere e entrega

Cada persona foi definida por função + dispositivo + restrição física, nunca por cargo.

PersonaDispositivoA restrição que moldou a interface
AtendenteDesktop, split-viewTrabalha com o WhatsApp aberto ao lado; precisa colar texto e transformar em pedido
SeparadorCelular dedicado, travado no appUma mão ocupada, ambiente físico, aparelho travado no app
Balança4 terminais compartilhadosNão é uma pessoa, é o login da estação. Pessoas se identificam nela
Conferente de EntregaMobile/tablet na docaConfere em pé, item a item, contra o pedido do cliente
Conferente de RecebimentoMobile/tablet na docaConta caixas que chegam aos poucos, de um caminhão sendo descarregado
MotoristaCelular, na ruaUma mão, tela pequena, sinal ruim, não pode ver preço nenhum
Gerente / CompradorDesktop + mobileDecide, não executa. Precisa de visão ampla
AdministradorDesktop + mobileAcesso total

Duas “personas” do sistema não são pessoas, e isso é resultado de design e não acidente: Balança é um perfil de estação física, e Desenvolvedor era um acesso somente-leitura para um integrador externo. Hoje são 10 perfis no total, com permissão por seção.

Tela de produtos no desktop, usada pela atendente Atendente · desktop
Terminal da balança mostrando peso bruto e peso líquido em números grandes Balança · terminal fixo
Tela do separador no celular Separador · celular
Tela do motorista no celular Motorista · celular
Quatro faces do mesmo sistema, uma por função. Nenhuma é a interface do desktop reduzida: o terminal da balança tem números grandes porque é lido de longe e operado com a mão ocupada, e as telas de celular existem porque quem as usa trabalha em pé.

A linguagem como artefato de design

Este é o item que eu mais destacaria, porque a maioria dos designers nunca faz.

Existe um documento de 563 linhas que é a fonte de verdade da linguagem do sistema. Ele define o nome exato de cada persona, com o que ela faz e o que ela explicitamente não faz; cada label de contexto; e as distinções que parecem sinônimos e não são.

Um exemplo real do que ele força:

“Retorno da conferência”: voltou da conferência por item reprovado.

“Retorno de entrega”: o pedido saiu e voltou inteiro, porque o cliente sumiu. A diferença que falta é perda.

“Entrega com divergência”: o cliente ficou com parte. A diferença é venda.

“Recoleta”: voltou porque faltou coletar, não porque algo foi reprovado.

Quatro situações que um sistema mal desenhado chamaria de “devolução”, e que significam coisas financeiramente opostas. A frase de abertura do documento resume a tese:

“Ambiguidade aqui causa bugs de entendimento antes de virar bug de código.”

Ele também lista termos proibidos. Não dizer “importar do WhatsApp”, porque não existe integração com WhatsApp: a atendente copia e cola, e o nome correto é Campo de Texto Livre. Não dizer “número diário”, porque para a operação existe um número de pedido. Não dizer “data da ocorrência” numa perda, porque esse campo existiu, criava duas verdades sobre o mesmo evento, e foi removido.

A taxonomia saiu do dado real

O catálogo não foi inventado. Peguei os 341 nomes de produto que a empresa efetivamente vendeu nos 4 meses anteriores e classifiquei um a um, gerando as regras de classificação:

  • BDJ/bandeja vira modo unidade: mesma base, variante de venda diferente
  • Calibre numerado (Abacaxi TP10/12/15/18) vira base separada, porque o calibre muda o preço de compra
  • Tamanho que afeta preço (Tomate Graúdo/Médio/Miúdo) vira base separada
  • Origem ou produtor vira base separada, porque pode afetar preço
  • HB 623 - CAIXA PLÁSTICA não é produto, é embalagem

E gerou uma lista de ambiguidades para validar com o cliente: “ALFACE CRESPA e ALFACE CRESPA UN parecem duplicatas”, “MURGOTE: identificar produto”. Levantar a dúvida em vez de adivinhar é parte do método, e é o que separa taxonomia de chute.

Hoje são 382 produtos cadastrados.

05

Por que existem duas tabelas de produto

Esta é a decisão de modelagem que mais paga juros, e ela é de produto antes de ser de banco.

O mesmo tomate pode ser comprado em caixa de 20 kg e vendido por quilo para o restaurante, por bandeja para o mercado e por caixa fechada para o atacadista. Se “produto” for uma coisa só, você é obrigado a escolher: ou duplica o item e perde o estoque unificado, ou unifica e perde o preço por modo de venda.

O modelo separa as duas perguntas:

  • produto_base, o produto físico. O que entra e sai do estoque. Carrega estoque, custo, peso médio e os campos de compra. Um registro por produto real.
  • produtos, o produto de venda. O que aparece no pedido do cliente. Carrega preço, margem, unidade de venda e o peso da bandeja. Pode haver vários por base.
Ficha de edição de um Produto Base, com controle de estoque, peso médio, quantidade por embalagem e custo por quilo
A ficha do Produto Base: o que entra no estoque. Peso médio, quantidade por embalagem, custo por quilo — e o estoque contado nas três unidades ao mesmo tempo, 25 kg · 50 cx · 250 un, porque a mesma mercadoria é comprada, guardada e vendida em grandezas diferentes.

Custo, estoque e peso vivem na base. Preço e margem vivem no produto de venda.

Isso não é teoria: hoje, 73 produtos-base já têm mais de um produto de venda no cadastro, e os três modos de venda estão em uso: quilo, unidade e caixa.

O modo caixa fechou a matriz comprar × vender: comprar em kg, manter o estoque em kg e vender a caixa fechada. E ele obrigou uma segunda decisão, porque um produto vendido em caixa conta, nunca pesa. A tela de separação ramifica pelo modo de venda, não pela unidade, e grava contagem em vez de peso.

Há ainda um segundo regime, que resolve um bug silencioso: produtos que nunca se vendem em quilo, como ovos e bandejas. Antes, cadastrar um produto sem peso médio e comprar 20 caixas a R$ 50 gerava estoque 0 e custo nulo em silêncio, porque o recebimento calculava contagem × peso_medio, e peso_medio era zero. A correção foi tornar o regime explícito, nunca inferido da ausência de peso: ausência de peso é o bug, não um sinal de regime. Hoje 13 produtos-base rodam em regime contado, com estoque em unidades e custo em reais por unidade.

Metade inferior da mesma ficha, mostrando histórico de estoque e a seção Destino com três produtos de venda derivados
A metade de baixo da mesma ficha, e é onde as duas tabelas ficam visíveis. Um Produto Base gera três produtos de venda — por caixa, por kg e por unidade —, cada um com o próprio preço. O histórico logo acima é o outro lado da decisão 7.10: “inativo some das telas, mas preserva o histórico”.
06

Receber sem pesar: a faixa plausível

Um exemplo de decisão que só aparece quando você olha o dado real, e um dos meus favoritos, porque a solução “óbvia” estava ativamente errada.

Compra em kg. O caminhão chega. O conferente conta os volumes. A regra original creditava contagem × peso_medio.

Por que isso está errado: contar caixas não mede peso. 100 kg em caixas de 22 kg ocupam 5 volumes, sendo 4 cheios e 1 pela metade. O conferente conta 5, corretamente, e o sistema credita 5 × 22 = 110 kg. Dez por cento de estoque fantasma, e valor de estoque acima do que foi pago, em toda compra em kg. O dinheiro que saiu foi quantidade_solicitada × valor_unitário, e o estoque que entrou era outro número.

A decisão: dentro de uma faixa plausível, a verdade é o peso pedido. Fora dela, a verdade é a contagem. Nada disso exige que alguém pese o caminhão.

Plausível exige duas condições, e a segunda entrou pelo code review, o que é exatamente o ponto:

  1. a contagem cai dentro do intervalo [floor(q), ceil(q)];
  2. e o peso nominal fica a menos de 20% do peso pedido.

Sem a segunda condição, um pedido de 39 kg em caixas de 20 kg (faixa [1,2]) aceitaria uma caixa de 20 kg e creditaria 39 kg, o que é pior que o bug original. Somado a isso, um teto absoluto: recebimento em kg nunca credita acima do pedido.

E aqui a decisão se conecta com a filosofia geral do sistema: aqui o sistema trava. A validação recusa um item de regime peso que não tenha peso médio em fonte nenhuma. Foi uma decisão tomada contra a recomendação do code review, a pedido explícito do dono, com o risco medido e mitigado: a validação testa o lote inteiro antes de mutar qualquer coisa, então nunca fecha um recebimento pela metade.

Recebimento é dinheiro entrando no estoque. Ali travar é barato, e deixar passar é caro e silencioso.

07

As decisões que valem uma conversa

Cada uma segue a mesma estrutura: problema, alternativa óbvia, decisão, razão.

7.1  O status é reflexo da operação, não trabalho do usuário

ProblemaTodo ERP pede que alguém mude o status manualmente. É trabalho extra que não produz nada, e é a primeira coisa que o usuário abandona quando está com pressa. Aí o dado apodrece.

Alternativa óbviaBotões de “marcar como separando”, “marcar como separado”.

DecisãoNenhum separador sabe que existe status. Ele toca em “iniciar separação”, o equivalente digital de tirar o papel do quadro e botar no bolso, e o resto é derivado dos atos reais: pesou, terminou, imprimiu.

“Sabe aquele negócio chato, que o usuário precisa mudar status só para dizer para o sistema que alguma coisa aconteceu? Não. Esses status vão ser reflexo da operação em tempo real, onde as outras pessoas nem sabem que existem esses status.”

O que mostraDado só é confiável quando registrá-lo é subproduto do trabalho, não trabalho adicional.

Fila de pedidos com a aba Conferido selecionada, mostrando dez pedidos nesse estado
Dez pedidos na coluna “Conferido”, e ninguém marcou nada. O separador tocou em “iniciar separação”, o conferente terminou de conferir, e o status foi consequência disso. É o que o texto acima quer dizer com registro sendo subproduto do trabalho.

7.2  Dois caminhos de identificação, e o PIN identifica sem autorizar

ProblemaQuatro balanças compartilhadas, cinco ou seis separadores. Sem identificação não há rastreabilidade. Com login por sessão, o separador digitaria usuário e senha toda vez que chegasse na balança, o que é inviável e ele abandonaria.

DecisãoNão é um caminho só. São dois, porque existem duas situações diferentes, e tratá-las igual foi o erro que o desenho evitou.

Caminho 1  ·  Pesar um pedido que já existeO pedido veio da atendente e o separador já iniciou a separação dele. O terminal fica sempre numa tela de espera que mostra os nomes de quem está separando naquele momento. O separador chega, toca no próprio nome, e a balança carrega os produtos do pedido dele. Sem PIN, sem senha, sem digitação.

Tela de espera da balança listando os separadores ativos no momento Caminho 1 · toca no próprio nome
Tela de identificação por PIN no terminal da balança Caminho 2 · PIN
Os dois caminhos, no mesmo terminal. O da esquerda não pede nada porque o sistema já sabe quem está separando. O da direita identifica quem pesou — e só isso: o PIN não abre permissão nenhuma.

Isso funciona porque a tela não está autenticando ninguém, está dando continuidade a um processo que já tem dono. Tocar no nome de outra pessoa não abre nada de útil: apareceriam os produtos do pedido dela, não os do pedido que ele está carregando nas mãos. O caminho errado não leva a lugar nenhum, então não precisa ser trancado. O que ele garante é rastreabilidade: fica gravado quem pesou o quê.

Caminho 2  ·  Criar um pedido com o cliente na frenteAqui é venda presencial. O cliente está ali, e quem atende precisa criar o pedido, não continuar um. Isso é entrar no sistema de verdade, e é onde entra o PIN de 4 dígitos.

Pense nos dois lados do mesmo computador. De um lado, o uso tradicional: a pessoa digita o PIN e ganha acesso para criar o pedido e, se quiser, pesar por ali mesmo. Do outro lado, uma tela touch voltada para o galpão, onde basta tocar no nome para cair direto na pesagem. Mesma máquina, dois modos, escolhidos pelo que a pessoa precisa fazer.

E vale marcar a diferença: criar um pedido presencial não é a mesma coisa que a atendente criar um pedido. No presencial o cliente está na frente, o produto está ali, e o fluxo é mais curto porque a venda está acontecendo naquele instante.

A regra que torna isso seguroO PIN identifica, não autoriza. Quem se identifica opera com as permissões da estação, não com as do próprio perfil. Um conferente que se identifica numa balança não ganha acesso a preços por causa disso.

RazãoA fricção precisa cair para perto de zero numa ação que se repete dezenas de vezes por dia. A rastreabilidade não pode custar autenticação completa. E identidade nunca pode virar privilégio por acidente.

7.3  Conferência cega: conferir contra o cliente, não contra o separador

ProblemaO achado nº 2 da descoberta. Item esquecido é item invisível.

DecisãoA conferência passou a mostrar, na mesma linha, o que o cliente pediu e o que foi pesado. E a validação é granular: cada item recebe checkpoints de Produto, Variedade, Qualidade, Quantidade e Observação.

Tela de conferência no celular, com o que o cliente pediu e o que foi pesado na mesma linha
O achado central do artigo, numa tela. Antes, a conferência checava o pedido contra o que o separador tinha montado — e por isso não podia achar o erro dele. Aqui as duas colunas ficam na mesma linha, e cada item carrega os próprios checkpoints.

O refinamentoQuando o cliente estava fisicamente presente durante a separação e a pesagem, ele já viu a qualidade e as quantidades com os próprios olhos. Nesse caso a conferência granular pode ser pulada. A regra não é “sempre conferir tudo”, é “conferir o que ninguém ainda validou”. É o que separa design bom de design rígido.

7.4  A recoleta: quando a UI errada fabrica dado falso

Achado pós-lançamentoEsta é a melhor história do projeto, porque veio de usuários reais.

A conferente às vezes precisava devolver um pedido para a separação porque faltou pegar um item, não porque algo estava com defeito. Mas o único caminho de volta era “reportar um item”, que exige informar um motivo de defeito.

Então as conferentes começaram a inventar um defeito para conseguir devolver o pedido.

O estragoO sistema passou a acumular relatórios de qualidade falsos. O dado de “produto com problema” ficou contaminado por uma limitação de interface. Ninguém agia de má-fé: a interface simplesmente não oferecia o caminho honesto.

DecisãoCriar a Recoleta, um caminho de volta explícito que diz “faltou pegar, mas o produto existe”. Ela não reprova nada, preserva a conferência já feita e não conta como retorno de conferência.

“Inventar um defeito para conseguir devolver era exatamente o abuso que a recoleta veio eliminar.”

O que mostraUsuário burlando o sistema é diagnóstico de design, não indisciplina do usuário. E que eu continuo olhando depois do lançamento.

7.5  Não usar IA onde o teclado do celular já resolve

ProblemaO comprador quer ditar a lista de compras em vez de digitar. E os pedidos chegam como texto informal de WhatsApp: banana 5, abacaxi 2, tomate saladete 3cx.

Alternativa óbvia, e caraTranscrição de áudio por IA, interpretação por LLM. Em 2026, é o que praticamente todo mundo faria, e o que soa melhor numa demo.

Decisão, em duas partes

  1. A transcrição de voz é feita pelo teclado do próprio celular. O Android já tem o botão de microfone. O usuário fala, o teclado transcreve, e o sistema recebe texto. Custo zero, latência zero, sem depender de fornecedor nenhum.
  2. A interpretação é feita por código determinístico. São 996 linhas de parser que identificam produto, quantidade e unidade usando regras e busca por similaridade contra o cadastro real.
Modal Campo de texto livre com a lista de WhatsApp colada e o botão Processar Entra · o texto como chegou no WhatsApp
Pedido montado, cada item com o trecho de origem indicado por Extraído de Sai · cada item com a origem preservada
Nenhuma chamada de LLM entre as duas imagens. Repare no Extraído de: “couve 5 mç” em cada item: o parser guarda o trecho que originou a linha, então a atendente confere a interpretação sem precisar confiar nela.

A razão, escrita na descoberta antes de eu saber se ia funcionar

“Se estiver bem escrito, apenas um código bem feito já consegue fazer esse trabalho e talvez mais assertivo do que uma inteligência artificial, porque a IA vai pegar muito contexto e vai até demorar mais.”

O detalhe que torna o parser confiávelEle sabe se abster. Tem níveis de confiança e correções aprendidas por cliente. O que ele não reconhece não vira palpite auto-preenchido, vira “não identificado” com uma sugestão de um clique. Um parser que erra em silêncio é pior que um parser que não tenta, porque a atendente para de conferir.

Por que é a decisão certa, e não economia de preguiçosoDeterminístico significa reproduzível, testável, corrigível, auditável e de custo marginal zero. Quatro propriedades que importam mais que “inteligente” quando o usuário está com pressa e precisa confiar no resultado. Uma atendente que descobre que o sistema “às vezes inventa um item” para de usar o campo, e aí você perde a feature inteira, não só a precisão dela.

7.6  Na entrega, capacidade é aviso, nunca trava

ProblemaO sistema sabe o peso da carga e a capacidade do veículo. Quando um pedido não cabe, ele deve impedir?

DecisãoDepende de onde você está no fluxo, e a fronteira é explícita:

  • Antes da entrega, em cadastro, preço, estoque e recebimento, o sistema trava. Ali é dinheiro, e o erro vira prejuízo silencioso.
  • Na entrega, o sistema sugere e avisa, mas nunca desabilita o botão. Quem está com o caminhão na frente sabe se cabe mais uma caixa. O software não sabe.
Board de entregas com aviso de capacidade excedida e o botão de confirmar ainda habilitado
O argumento inteiro cabe numa tela: o aviso de capacidade aparece, e o botão continua clicável. Um disabled aqui não evitaria um erro — pararia um caminhão.

RazãoUm disabled na hora errada não previne um erro, ele para um caminhão. E quando o software impede algo que a pessoa sabe ser possível, ela perde a confiança no software inteiro, não só naquela tela.

7.7  Requisição de compra: um fluxo só, não duas telas

ProblemaO desenho clássico separa “criar requisição” de “confirmar recebimento”. Mas no NR a mesma pessoa pode fazer as duas coisas, e a compra real quase nunca obedece à requisição: o comprador pede 20 caixas de banana de um fornecedor, chega lá e só 10 estão boas, então pega 10 de um e 10 de outro, cada um com preço e peso médio diferentes.

DecisãoUma requisição só, editável, viva. Quem criou entra de novo e edita. Quem está no CEASA abre a mesma requisição e confirma, altera, adiciona fornecedor ou remove produto.

“Por que eu vou fazer ele criar uma requisição e depois ter que acessar outro lugar só para confirmar aquela requisição?”

Folha A4 manuscrita que o comprador levava para o CEASA A folha que ia para o CEASA
Requisição de compra no aplicativo A requisição, editável no celular
O mesmo par papel → tela da abertura, agora na compra. A folha era preenchida antes de sair e corrigida à caneta no CEASA; a requisição é a mesma, viva, e a divergência entre o pedido e o que chegou fica registrada em vez de sumir na margem do papel.

O desdobramentoA divergência vira informação. Confirmou 10 caixas e chegaram 8, a diferença fica registrada e rastreável. O sistema não esconde a diferença entre o planejado e o real, ele a torna visível.

7.8  Cliente não identificado: a conta de trânsito

ProblemaUm cliente chega no galpão, pega mercadoria, e o separador precisa registrar. Nem sempre dá para identificar na hora: pode ser alguém novo, pode estar corrido, pode não haver cadastro.

Alternativa óbviaObrigar a identificação, ou deixar o campo nulo.

DecisãoUma conta de trânsito, um destino explícito para a venda não identificada, que permite identificar depois. O caixa nunca trava por falta de cadastro, e o dado não vira um buraco.

RazãoObrigatoriedade em ponto de alta pressão gera dado lixo, porque o usuário escolhe qualquer cliente da lista para passar da tela. Campo nulo gera venda órfã. A terceira opção preserva as duas coisas.

7.9  Um número só para o humano

ProblemaO sistema tinha dois números de pedido: um sequencial global e um “número diário” curto que reiniciava todo dia. Duas verdades sobre a mesma coisa.

DecisãoToda superfície voltada ao humano, do card da fila à busca, ao toast, à notificação, ao ticket impresso e ao board de entregas, passou a falar só o número global, que é o mesmo da nota fiscal. O número diário sumiu da interface inteira.

E um detalhe que só aparece em campo: o número impresso no ticket sai com zero à esquerda (042). Quem digitava 042 não achava o pedido 42. A busca passou a remover o zero à esquerda antes de casar.

RazãoDuas numerações significam que, em algum momento, duas pessoas ao telefone vão estar falando de pedidos diferentes achando que falam do mesmo.

7.10  Nunca deletar, sempre inativar. Nunca apagar, sempre estornar.

Dois princípios irmãos:

  • Cadastros de produto, cliente, fornecedor e funcionário nunca são deletados, são inativados. O histórico de pedidos e os relatórios antigos permanecem íntegros.
  • Movimentações de estoque vivem num ledger append-only. Desfazer uma perda não apaga a linha: grava o evento inverso.

Razão, com o caso concretoSe o estorno fosse um DELETE, uma perda registrada em 31/07 e desfeita em 03/08 sumiria do relatório de julho retroativamente, e o número que o gerente viu na semana passada nunca mais bateria. Com o ledger, julho continua contando +10 kg e agosto conta −10 kg. Os dois eventos são verdadeiros, em meses diferentes.

7.11  O sistema elimina a calculadora

DecisãoO separador informa quantas caixas e qual modelo de caixa. Se usar pallet, escolhe qual. O sistema desconta tudo. A balança está conectada ao terminal, então o peso aparece no campo já com a tara descontada, e ele só confirma.

A decisão de hardware por trásOs pallets são de madeira e mudariam de peso conforme a umidade. Em vez de aceitar a imprecisão ou obrigar a pesar o pallet toda vez, a solução foi operacional: pesar cada pallet uma vez, etiquetar com número e peso, cadastrar no sistema. Validada com o dono, que confirmou que os pallets não ficam expostos.

O que mostraA solução de um problema de produto às vezes está fora da tela.

7.12  Atalhos de teclado: a persona desktop trabalha com as duas mãos

Fácil de esquecer num projeto cujas personas mais visíveis são mobile. Mas a atendente passa o dia inteiro em listas, com o WhatsApp aberto ao lado, e para ela o mouse é o gargalo. O sistema inteiro é operável sem tirar as mãos do teclado.

Navegação e ambiente

YAbre a busca global. Digita duas letras do nome da página e navega direto, sem abrir o menu lateral. E ela só lista o que aquele perfil pode ver: a busca respeita as permissões, exatamente como a sidebar.
TAlterna tema claro e escuro na hora.
BFoca e já seleciona o campo de busca da tela. Selecionar junto importa: quem aperta B quase sempre quer buscar outra coisa, não continuar a busca anterior.
NCria um registro novo na tela em que você está.
Modal de busca global aberto sobre a lista de compras, listando as páginas do sistema
A busca global, aberta com uma tecla. Ela lista as páginas e leva direto ao destino — é o que torna atalho de letra única viável, porque ninguém precisa decorar caminho de menu.

Filtros: a tecla é a primeira letra do filtroEste é o princípio que faz o resto não precisar ser decorado.

DDesativados. Vale em seis listas diferentes, sempre com o mesmo significado.
CCEASA nos fornecedores, caixa nas embalagens.
WWhatsApp.
PPallet nas embalagens, pendente nos produtos.
K / UModo de venda: kilo e unidade.

Não é um mapa arbitrário que o usuário decora. É uma regra que ele deduz uma vez e aplica em qualquer tela.

Busca e acesso ao item

  • No campo de busca, ESC e Enter tiram o foco. Não limpam.
  • Fora do campo, com texto digitado, ESC limpa a busca e 1 a 9 abrem o N-ésimo resultado visível, a mesma ação de clicar na linha.

O detalhe que faz a diferençaA regra do ESC é sem estado. Não é “duplo ESC”. A função primária do ESC é tirar o foco; o ESC seguinte, já sem foco, cai naturalmente em “limpar”. O usuário aperta duas vezes e as duas coisas acontecem na ordem que ele espera, sem que ninguém tenha programado um contador.

E as guardas, que são metade do trabalhoNenhum atalho dispara quando o cursor está num campo de texto, numa área editável ou num select. E nenhum dispara com drawer ou modal aberto. Sem isso, um atalho de letra única transforma qualquer digitação em comando, que é a razão pela qual a maioria dos sistemas desiste de atalhos de letra única.

A decisão de não mostrar nadaOs atalhos não têm etiqueta visual na tela. Foi decisão deliberada do dono. Quem usa todo dia descobre e ganha velocidade; quem não usa não perde nada e não vê ruído numa interface que já é densa. Zero mudança de render.

7.13  Tema escuro e claro, e por que o escuro é o padrão

O design system tem dois temas completos, com o mesmo conjunto de tokens. O que não é óbvio é qual veio primeiro.

O tema base é o escuro, “OLED Black × Electric Lime”, literalmente o cabeçalho do arquivo-fonte. O tema claro é um override. Isso inverte o default da indústria, e por um motivo operacional: as telas que mais rodam são as do galpão, em celular, muitas horas por dia, com brilho alto. Preto puro em tela OLED gasta menos bateria e cansa menos vista num ambiente com iluminação irregular.

O que isso custa, e onde o rigor apareceCor de status que fica vibrante e legível no preto frequentemente cai abaixo de 4.5:1 de contraste no branco. O arquivo de tokens trata isso explicitamente: há um conjunto de tokens de status só para texto sobre fundo claro, escurecidos o bastante para passar em AA, porque o valor vivo do tema escuro não passaria. Não é o mesmo token com opacidade diferente, são valores calculados para cada tema.

E por isso a regra de review visual do projeto tem quatro eixos, não dois: desktop e mobile, escuro e claro, mais navegação por teclado. Um bug de contraste só existe em uma das quatro combinações.

Tela de Novo Pedido no tema claro Tema claro · override
A mesma tela de Novo Pedido no tema escuro Tema escuro · base
A mesma tela do 7.5, agora nos dois temas. Não é filtro nem opacidade: os tokens de status são valores calculados para cada tema, porque a cor que passa em AA sobre o preto não passa sobre o branco.

7.14  De site no navegador a celular que só serve para trabalhar

O arcoO projeto começou como aplicação web, aberta no navegador. Terminou como um aparelho Android dedicado, que liga já travado no app e não faz mais nada além disso. Cada etapa desse caminho foi uma decisão, não uma consequência.

ProblemaUm aparelho compartilhado que também é um celular comum vira tudo menos ferramenta de trabalho: notificação, distração, aplicativo instalado por quem pegou antes, conta de outra pessoa ainda logada. E competir por atenção dentro da interface é uma briga que o software sempre perde.

1. O app carrega a web ao vivo, e isso muda tudo

O APK não embarca uma cópia do site. Ele carrega a interface ao vivo do servidor. A consequência prática é grande: toda melhoria de interface chega nos aparelhos no próximo deploy, sem gerar APK, sem pedir para ninguém atualizar nada, sem loja de aplicativo no meio.

O APK só precisa mudar quando muda a camada nativa: GPS em segundo plano, notificação, impressão, o kiosk. Ou seja, raramente. O que muda toda semana viaja pela web; o que muda uma vez por mês viaja pelo APK.

2. O APK se atualiza sozinho, e nunca em silêncio

O app compara a versão instalada com a publicada pelo servidor e, se houver uma nova, mostra o aviso de atualização. Hoje está na décima versão.

E aqui vai uma limitação que eu prefiro contar do que esconder: fora da loja oficial, é impossível instalar automaticamente. O sistema operacional exige que a pessoa toque em “Atualizar”, autorize e confirme. Então o desenho assume isso em vez de brigar: o app não promete atualização invisível, ele avisa bem. Prometer o que a plataforma não permite é a forma mais rápida de construir uma feature que falha em silêncio.

3. O mesmo APK para todo mundo, e ele decide sozinho o que é

Esta é a decisão da qual eu mais gosto nessa parte. Não existem duas versões do aplicativo, uma “normal” e uma “travada”. Existe um APK só, e ele detecta em tempo de execução se aquele aparelho foi provisionado como dispositivo dedicado da empresa.

  • Aparelho provisionado: sobe travado, vira a tela inicial do celular e permanece travado inclusive depois de reiniciar.
  • Aparelho comum, como o do motorista: o mesmo app roda normalmente, sem travar nada.

Uma distribuição só, um build só, sem risco de instalar a versão errada no aparelho errado.

4. O destravamento é nativo e funciona offline, e isso não é detalhe

Aqui está o ponto em que a arquitetura obrigou a decisão. Como a interface vem da web ao vivo, um aparelho travado e sem internet não teria tela nenhuma para se destravar. Se o destravamento fosse uma tela do sistema, um celular sem sinal no fundo do galpão viraria um tijolo.

Por isso o caminho de saída é 100% nativo e offline: um gesto discreto num canto da tela abre a tela de liberação, e a senha mestre é verificada no próprio aparelho, sem rede. A trava também abre exceção para o serviço de impressão, porque o separador precisa imprimir o ticket mesmo com o celular travado.

EstadoImplementado, testado em aparelho real e em produção desde 11/07/2026.

Por que isso é diferencial de verdadeMuita empresa precisa que o celular seja uma ferramenta de trabalho e não um dispositivo pessoal, e quase sempre resolve isso com regra interna, aviso e confiança. Aqui a restrição é técnica: o aparelho literalmente não faz outra coisa. É design de produto operando na camada de hardware e configuração, não só de interface, e é o tipo de coisa que não aparece num protótipo.

08

Design system: um registro de cicatrizes

189declarações de token, em arquivo-fonte único
31componentes primitivos
206componentes React no total
2temas completos sobre o mesmo conjunto de tokens

O sistema tem design system próprio, gerado, e não um tema improvisado.

A regra de governança: os tokens vivem em um arquivo-fonte e são gerados para dentro da aplicação por script, que roda automaticamente antes de todo dev e de todo build. Ninguém edita o CSS da aplicação à mão. Ninguém escreve um hex dentro de um componente.

Os primitivos incluem Field (campo de formulário com label sempre associada), ClickableRow (linha de lista clicável, com semântica diferente para navegar e para abrir), Drawer, Modal, ConfirmDialog, EmptyState, SearchField, SortDropdown, SegmentedTabs, Toast, PageSkeleton, KpiCard e OfflineBanner.

A parte que eu mais defendo: as regras têm proveniência

O documento de padrões de UI abre com a tese dele:

Cada regra previne um bug que já aconteceu.

Não é um style guide aspiracional. É um registro de cicatrizes. Exemplos:

  • Campo de formulário sempre pelo primitivo Field, porque htmlFor só funciona em elemento labelável, e um dropdown customizado precisa de aria-labelledby. Acessibilidade codificada no primitivo, não confiada à disciplina de quem escreve a tela.
  • Toda rota nova precisa de um estado de carregamento com o skeleton padrão.
  • Atualização em tempo real por broadcast explícito, nunca por polling, com uma regra específica de canal isolado, porque misturar dois mecanismos no mesmo canal derruba o outro em silêncio.

Por que isso importa: um design system que só padroniza aparência não paga o custo de existir. Este é um mecanismo de prevenção de erro. A regra existe porque o bug existiu, e está escrita no lugar onde a próxima pessoa vai tropeçar nela.

09

A arquitetura e o código

1.718commits, de 22/04 a 14/08/2026
72.626linhas de código em 492 arquivos TS/TSX
1.563testes em 82 arquivos, rodando em 2,2 s
313migrações de banco
44telas em 13 módulos
10perfis de acesso
37arquivos de Server Actions
50releases em produção em 44 dias

Stack: Next.js 16 com App Router e Server Actions, TypeScript, Supabase (Postgres, Auth e Realtime) e Vercel.

Banco em produção: 34 tabelas, 401 colunas, 58 funções Postgres, 20 triggers e 139 índices.

Cadastro real da empresa: 1.504 clientes, 1.548 endereços, 1.196 fornecedores, 382 produtos, e 72.992 linhas de histórico de vendas importadas e normalizadas do sistema legado.

Alcance da entrega, além do web app: autenticação Google OAuth com RBAC de 10 perfis; atualização em tempo real por broadcast; app Android próprio com modo dedicado (o aparelho liga travado no app), rastreamento de GPS em segundo plano e auto-atualização; geocodificação de endereços com mapa; notificações push; importação do sistema legado; e um diagnóstico de conformidade com a LGPD.

Sobre os 1.563 testes, e o que eles deliberadamente não cobrem

A suíte roda em 2,2 segundos, e isso é design, não sorte: ela cobre função pura. Toda regra de negócio que importa, do motor de conversão ao cálculo de baixa de estoque, da faixa plausível do recebimento à álgebra de custo dos dois regimes, do parser ao resolvedor de atalho de teclado, foi extraída para função pura e testada isoladamente.

Uma suíte que leva 2 segundos é uma suíte que você roda. Uma que leva 4 minutos é uma que você começa a pular.

Saída do terminal com a suíte de testes concluída: 82 arquivos e 1563 testes passando em 2,16 segundos
82 arquivos, 1.563 testes, 2,16 s. Os nomes dizem o que foi extraído para função pura: regime-estoque, balanca-toledo, parse-preco, capacidade-veiculo, item-nao-separado. Cada um corresponde a uma decisão contada aqui em cima.
O buraco na rede, dito na cara

A suíte não exercita Server Action, não sobe navegador e não testa sequência de interface. Toda a classe de defeito que é sequência, como estado que aparece na ordem errada, foco que vai para o lugar errado ou ramo que não renderiza, passa por ela intacta. É por isso que o review visual em quatro eixos existe como etapa formal. Saber onde a rede tem buraco vale mais que fingir que não tem.

10

Segurança

Esta seção normalmente não existe em portfólio de design. Ela existe aqui porque um sistema que carrega documento de cliente, localização de entrega e o custo de compra de todo o catálogo tem um modelo de ameaça real.

Autenticação sem senha

O login é Google OAuth, e o caminho de senha foi desligado no servidor. Isso elimina de uma vez uma classe inteira de ataque: não há hash para vazar, não há reset para sequestrar, não há força bruta para limitar.

Autorização por seção, e “ativo” como condição de entrada

Toda rota passa por um middleware que resolve o usuário, encontra o funcionário e exige que ele esteja ativo antes de checar o nível de permissão da seção. Ativo é condição de entrada, não detalhe de cadastro: desativar um funcionário corta o acesso na hora, sem depender de expirar sessão.

O perfil Balança tem um caminho próprio, porque é uma estação e não uma pessoa: ele resolve quem se identificou e opera dentro das permissões da estação. É a regra da seção 7.2, implementada onde ela precisa estar.

A regra que veio de um bug real

Uma revisão de segurança encontrou uma Server Action que recebia a identidade de quem estava agindo como um parâmetro vindo do navegador. Havia uma verificação no banco que parecia proteger a posse do registro, mas o valor conferido era justamente o que o cliente podia alterar. Não autenticava nada.

A regra que ficou: Server Action que representa “quem fez a ação” deriva essa identidade da sessão no servidor, nunca de parâmetro do cliente. A pergunta a fazer em toda action nova é literal: “o cliente pode mentir esse valor?”

É o tipo de defeito que nenhum teste de tipo pega, porque o código está sintaticamente perfeito. Só uma leitura adversarial encontra.

Negar por padrão, que é a decisão contraintuitiva

As 34 tabelas têm segurança em nível de linha habilitada. E existem exatamente 5 políticas no banco inteiro.

Parece descuido. É o desenho. Uma tabela com a proteção ligada e nenhuma política nega tudo por padrão. Não existe “esqueci de proteger essa tabela”, porque o estado inicial de qualquer tabela nova já é fechado, e cada exceção precisou ser escrita à mão, uma por uma, com justificativa.

O acesso legítimo não passa por ali: ele passa pelo servidor, depois da checagem de perfil. As pouquíssimas políticas que existem cobrem os casos em que alguém de fora precisa ler exatamente a própria linha, e nada além dela.

A inversão vale a pena declarar: a maioria dos sistemas começa aberto e vai fechando conforme lembra. Este começa fechado e só abre onde alguém provou que precisa.

LGPD

Existe um diagnóstico formal de conformidade, feito contra o schema real verificado, e não contra os arquivos locais, que são intenção e podem divergir. Ele inventaria o que é dado pessoal, identifica os operadores envolvidos e propõe a base legal de cada operação.

Do lado técnico: os dados ficam no Brasil, em São Paulo, o tráfego é HTTPS, o PIN é armazenado com hash, não há dado sensível clássico nem dado de cartão, e o acesso externo à base está fechado pelo modelo da seção anterior.

11

Como eu trabalhei com IA

Construí o sistema sozinho, com IA como par de programação, em cerca de quatro meses. Vale ser específico sobre o que isso significa, porque “fiz com IA” hoje descreve coisas muito diferentes.

O que a IA não fez: ela não foi a campo. Não fez as entrevistas, não achou a conferência cega, não decidiu que o comprador não precisa de dashboard de estoque, não escolheu deixar a negociação fora do sistema, não descobriu que as conferentes estavam inventando defeitos. Essas são as decisões que fazem o produto ser esse produto, e todas vieram de observação e conversa.

O que a IA fez: implementação, refatoração e, o que eu acho mais valioso, revisão adversarial. Foi uma revisão automática de segurança que encontrou o bug de identidade da seção anterior.

A disciplina que eu tive que construir por cima. LLM produz texto plausível com altíssima fluência, e isso é perigoso de um jeito específico: crítica plausível e defeito real são indistinguíveis na leitura. Rodar review duas vezes no mesmo código devolve duas listas diferentes, e as duas parecem boas. A conclusão que virou regra no projeto: review por LLM é amostragem, não decisão.

Então achado não é resultado, achado é candidato. Ele só vira correção depois de passar por um portão:

  1. Sem comando executado, é hipótese. Vai numa seção separada, com o comando que a resolveria ao lado.
  2. Tentar matar o achado antes de corrigi-lo. A pergunta não é “isso procede?”, é “qual o caminho concreto em que este código faz a coisa errada?”. Sem caminho alcançável, o achado morre, por mais elegante que seja a crítica.
  3. Correção ganha teste que já falhou. Escreve o teste, desfaz o conserto, vê vermelho pelo motivo certo, restaura. Sem isso, não se sabe se o teste cobre o bug ou só passeia perto dele.

A mesma disciplina vale para documentação, e é aí que ela é mais necessária, porque typecheck, lint e testes passam todos verdes com a documentação mentindo. Nada compila uma frase.

Um exemplo do processo funcionando contra si mesmo

Ao montar o material deste projeto, uma auditoria da documentação contra o código achou um erro ainda aberto: a página de visão geral listava, como princípio de design, que o parser usava IA, e chegava a nomear o modelo. Não era verdade havia mais de um mês. Esse código existiu, nunca foi consumido por componente nenhum, e foi removido em 09/07/2026. Mais irônico ainda: a própria documentação já listava “a IA processa o pedido” como termo proibido em outra página.

O erro sobreviveu exatamente nas páginas de visão geral, que são as mais lidas e as menos auditadas, porque “não descrevem feature nenhuma”. Foram corrigidas no mesmo dia, com entrada no log de decisões.

É a melhor ilustração que eu tenho do ponto: a documentação apodrece em silêncio, e apodrece primeiro onde ninguém procura.

12

Entrega: como o produto chegou aos usuários

50 releases em produção entre 01/07 e 13/08/2026, ou seja 44 dias corridos, mais de um deploy por dia útil. Cada promoção recebe uma tag permanente com data e resumo, porque o histórico da branch de produção só mostra o que passou. A tag é o registro de o que foi ao ar e quando.

A estrutura: um tronco de testes onde o dono valida antes de qualquer coisa ir para produção; produção só avança para um commit já testado, nunca para código não verificado; e trabalho grande roda em ramo isolado, sem nunca tirar do ar o que o usuário está testando.

Documentação como parte da entrega, não como sobra

346 documentos markdown:

OndeO que é
Wiki de domínio
39 páginas
Entidades, fluxos, conceitos e personas. A fonte de verdade do negócio
Log de decisões
1.858 linhas, 73 entradas datadas
O que mudou, por quê, o que foi considerado e descartado, e o que ficou em aberto
Specs e planosEspecificação de design e plano de implementação, por feature
ReviewsRevisões, auditorias e follow-ups
PlanejamentoPlanos e specs de features maiores
Uma entrada datada do log de decisões, registrando que a integração com IA existiu mas nunca foi usada e foi removida
Uma entrada do log — e, por acaso, a que fecha o argumento do título. Ela registra que a Server Action do Gemini “existiu mas nunca foi consumida e foi removida em 2026-07-09”, e que as colunas ai_texto_original / ai_processado são “fósseis de nome, não indicam uso de IA”. O rastro serve exatamente para isto: provar o que o sistema não faz.

E há um processo formal de reconciliação da documentação contra o código: auditorias que verificam se a documentação ainda descreve o sistema real, com a regra explícita de que quando divergem, o código é a verdade técnica e a documentação é corrigida.

13

O que este projeto demonstra

O que provaA evidência
Pesquisa de campo de verdadeObservação presencial recorrente, entrevista por função, transcrição de áudio, análise de artefato físico e dos 341 produtos do dado real
Enquadramento de problema, não coleta de requisito“E se você faltar amanhã” mudou o escopo inteiro
Achar o problema que o cliente não sabia que tinhaA conferência cega
Saber onde o produto não deve entrarA negociação com fornecedores ficou deliberadamente fora
Design de sistema, não de tela10 perfis, 3 esferas, 8 entidades, 7 fluxos ponta a ponta
Linguagem como material de design563 linhas definindo cada termo, incluindo os proibidos
Design system com governançaToken gerado de fonte única, e cada regra com proveniência de um bug real
Interface operável sem mouseBusca global, filtros por primeira letra e acesso direto ao resultado, com as guardas que tornam atalho de letra única viável
Aprender com o comportamento pós-lançamentoA recoleta, com usuário burlando o sistema tratado como diagnóstico de design
Resistir à solução da modaO parser determinístico, num ano em que tudo vira LLM
Entrega até a camada do aparelhoAplicação web que virou celular dedicado, com um APK único que decide em tempo de execução se trava, e destravamento nativo que funciona sem rede
Segurança como decisão de produtoNegar por padrão no banco, e um PIN que identifica sem autorizar
Rastro de decisão1.858 linhas de log datado com o porquê de cada escolha

A maioria dos cases de portfólio termina no protótipo. Este termina em software rodando, e continua, porque a operação real produz achados que nenhuma pesquisa prévia produziria.

Voltar ao site