Monorepo na prática com Turborepo
Entenda o que é um monorepo, por que times escolhem isso em vez de repositórios separados, e como o Turborepo organiza workspaces e cache de build com um exemplo prático de duas apps compartilhando um pacote de UI.
Já falei aqui sobre como estruturar deploy, CI/CD e até multi-tenancy, mas ficou faltando uma decisão que costuma aparecer assim que um projeto cresce pra mais de uma aplicação: manter tudo num repositório só, ou espalhar em vários. Monorepo é o nome pra primeira opção, e o Turborepo é a ferramenta mais usada hoje pra fazer isso funcionar bem no ecossistema JavaScript.
O problema de repositórios separados
Imagina um sistema com um site principal, um painel administrativo e uma API, cada um no seu próprio repositório. Parece organizado, mas na prática esconde um problema: se os três compartilham um componente de botão, uma função de formatação de data ou um tipo do TypeScript, essa parte comum precisa existir em algum lugar. O jeito comum de resolver é publicar isso como um pacote separado, num registro tipo npm, e cada repositório instala aquele pacote como dependência externa.
Isso funciona, mas cria fricção: qualquer mudança no pacote compartilhado exige publicar uma versão nova, atualizar a dependência em cada repositório separadamente, e torcer pra ninguém esquecer de subir um deles. Um ajuste pequeno vira uma sequência de passos manuais espalhados em três lugares diferentes.
Monorepo: tudo no mesmo lugar
Um monorepo junta várias aplicações e pacotes compartilhados dentro do mesmo repositório Git, organizados em pastas:
meu-projeto/
├── apps/
│ ├── site/
│ └── admin/
├── packages/
│ ├── ui/
│ └── utils/
└── package.json
Cada pasta dentro de apps/ e packages/ é o que se chama de workspace, praticamente um projeto próprio, com o próprio package.json, mas vivendo dentro do repositório maior. Quando site e admin dependem do pacote ui, essa dependência aponta pra pasta local, não pra um registro externo:
// apps/site/package.json
{
"dependencies": {
"ui": "workspace:*"
}
}Muda algo em packages/ui, e site e admin já enxergam essa mudança na hora, sem publicar nada, sem esperar propagação de versão. É a mesma vantagem de trabalhar num único projeto, mas mantendo cada aplicação isolada em sua própria pasta.
Onde o Turborepo entra
Gerenciar workspaces sozinho, sem ferramenta nenhuma, já é possível com os workspaces nativos do npm, pnpm ou yarn. O problema que sobra é rodar tarefas (build, teste, lint) de forma eficiente num repositório com várias aplicações. Buildar tudo do zero toda vez que qualquer coisa muda, mesmo o que não foi alterado, é desperdício de tempo.
O Turborepo resolve isso com cache inteligente por tarefa. Um turbo.json na raiz descreve como as tarefas se relacionam:
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"]
}
}
}O dependsOn: ["^build"] diz que, antes de buildar um workspace, é preciso buildar primeiro os workspaces dos quais ele depende (o ^ significa "as dependências desse pacote"). Rodando o comando na raiz:
turbo buildO Turborepo calcula a ordem certa sozinho: builda packages/ui antes de apps/site, porque site depende dele. E, mais importante, guarda o resultado de cada build em cache. Se você rodar turbo build de novo sem ter mudado nada em packages/utils, ele pula esse build inteiro e reaproveita o resultado anterior, mostrando algo como >>> FULL TURBO no terminal em vez de rodar tudo de novo.
Cache remoto: o mesmo cache pro time inteiro
Além do cache local na sua máquina, o Turborepo também suporta cache remoto, compartilhado entre todo mundo do time e o CI/CD. Se um colega já buildou aquele mesmo pacote, com aquele mesmo conteúdo, você não precisa buildar de novo na sua máquina, o Turborepo baixa o resultado já pronto de um servidor de cache.
Isso importa bastante em pipeline de CI/CD (que já mostrei aqui com GitHub Actions): sem cache remoto, todo PR builda o monorepo inteiro do zero, mesmo que só um arquivo de uma aplicação tenha mudado. Com cache remoto, só o que realmente mudou é rebuildado, e o resto vem do cache, deixando o pipeline bem mais rápido.
Vale a pena pro seu projeto?
Monorepo com Turborepo compensa quando existe de fato mais de uma aplicação compartilhando código, ou quando o time cresce o suficiente pra sentir a dor de manter pacotes espalhados em repositórios separados. Pra um projeto único, sem nada compartilhado com outra aplicação, isso é estrutura demais pra um problema que ainda não existe.
Fechando
O ganho real de um monorepo não é estar tudo no mesmo lugar por estar, é eliminar a fricção de manter código compartilhado sincronizado entre aplicações diferentes. O Turborepo entra justamente pra que essa organização não custe velocidade: com cache local e remoto, só o que de fato mudou é reconstruído, mesmo com várias aplicações crescendo dentro do mesmo repositório.
Leia também
- Agent loops: fazendo o Claude Code se corrigir sozinho8 de setembro de 2026 · 8 min
- Debugging avançado no Node com Chrome DevTools5 de setembro de 2026 · 5 min
- Feature flags: ative e desative funcionalidade sem novo deploy4 de setembro de 2026 · 4 min
- Gerenciamento de estado no React: Context API, Zustand ou Redux?2 de setembro de 2026 · 5 min