Um monorepo, muitas áreas de produto, sem virar caos
O ClubPetro não é um produto. São uns dezesseis. Customer Success, Finance, Support, Marketing, Sales, Admin, Platform, Tech, e por aí vai — cada área com suas telas, suas regras, seu público interno. E tudo isso vive num monorepo só. Quando a gente conta isso, a reação comum é: "não vira um caos?" A resposta honesta é que poderia virar, e a diferença entre caos e alavanca está em algumas decisões de fronteira que a gente leva a sério.
Por que um monorepo, e não dezesseis repositórios
A tentação de separar é real. Cada área parece um produto independente; por que não dar a cada uma seu repositório, seu deploy, sua vida?
Porque o custo de separar, para um time pequeno, é maior do que o custo de conviver. Num monorepo:
- Uma mudança atravessa camadas num commit só. Precisou de um campo novo no banco, uma regra nova no domínio, uma procedure e uma tela? É um PR, não seis, sincronizados entre seis repositórios com versões que nunca batem.
- O compartilhamento é de graça. Um componente de tabela feito para o Finance está imediatamente disponível para o Sales. Sem publicar pacote, sem bump de versão, sem esperar o outro time atualizar a dependência.
- A refatoração é possível. Renomear um conceito que aparece em dez áreas é uma operação atômica. Em repositórios separados, seria uma campanha.
Para um time grande com dezenas de squads independentes, a matemática muda. Para o nosso, o monorepo é o que deixa poucas pessoas moverem muita superfície.
As fronteiras que impedem o caos
Monorepo sem disciplina de fronteira vira uma bola de lama grande. O que segura a nossa é a separação clara entre pacotes compartilhados e áreas de produto.
Os pacotes compartilhados são a fundação:
packages/ui— o design system, com dezenas de componentes sobre Radix. Toda área consome daqui. É o que faz o Finance e o Support parecerem o mesmo produto, mesmo construídos em semanas diferentes por pessoas diferentes.packages/db— o acesso a dados via Drizzle, um lugar só que fala com o Postgres. Ninguém instancia cliente de banco solto; todo mundo passa pelos singletons.packages/domain— a lógica de negócio pura, sem I/O. As regras que valem para toda a empresa moram aqui, não espalhadas pelas telas.packages/shared,packages/ai,packages/messaging— tipos comuns, os agentes Claude, e o envio de e-mail e WhatsApp.
A regra que mantém isso são: as áreas de produto dependem dos pacotes compartilhados, nunca o contrário, e uma área não fura a fronteira da outra. O Sales não importa direto o código interno do Finance. Se os dois precisam da mesma coisa, essa coisa sobe para um pacote compartilhado. É a diferença entre reúso saudável e acoplamento acidental.
O que trava e como a gente lida
Não é tudo idílico. Monorepo grande tem seus atritos, e vale ser honesto sobre eles:
- O typecheck de uma área grande estoura memória. É uma realidade da nossa base; a gente convive dando mais memória ao processo em vez de fingir que não existe.
- Existe um baseline de erros de tipo pré-existentes que o build tolera de propósito. A disciplina aqui é não confundir "erro que já existia" com "erro que eu acabei de introduzir". O que importa é o delta, não o zero absoluto.
- CI só roda o que foi afetado. Um PR que mexe só no Marketing não paga o custo de buildar o Finance inteiro. O
turbocom filtro por pacotes afetados é o que mantém o feedback rápido mesmo com a base crescendo.
Nenhuma dessas fricções justifica quebrar o monorepo. Cada uma tem uma mitigação que custa menos do que a alternativa de sincronizar dezesseis repositórios.
Por que isso importa para quem constrói aqui
Um monorepo bem cuidado é um multiplicador para time pequeno. Você toca uma área de manhã e outra à tarde sem trocar de contexto de ferramenta, sem clonar outro repositório, sem descobrir que a versão do design system que aquele time usa é três atrás. A superfície é grande, mas a distância entre qualquer dois pontos dela é curta.
O builder que prospera nesse ambiente é o que respeita fronteira sem se esconder atrás dela. Que sabe quando uma coisa pertence à sua área e quando ela precisa subir para um pacote compartilhado porque outra área também vai querer. Que enxerga o sistema inteiro, não só o próprio canto. Num monorepo, o código de todo mundo é, em algum grau, o código de todo mundo — e é justamente isso que deixa a gente andar rápido sem se atropelar.