- A Twilio Segment operava uma estrutura com centenas de microsserviços, mas migrou para um serviço único (monolítico) devido à complexidade e ao custo de manutenção
- No início, separou cada API de destination para garantir isolamento de falhas e escalabilidade, mas, com o número de serviços ultrapassando 140, o overhead operacional disparou
- A gestão de múltiplos repositórios e bibliotecas compartilhadas ficou difícil, e problemas em testes e deploys passaram a afetar o conjunto dos serviços
- Para resolver isso, introduziu o sistema Centrifuge e uma estrutura de monorepo, além de criar o Traffic Recorder para automatizar testes
- Como resultado, a velocidade de desenvolvimento e a estabilidade melhoraram bastante, e a Twilio Segment mantém a estrutura monolítica por produtividade e eficiência operacional
A adoção de microsserviços e seus limites
- A Twilio Segment adotou uma arquitetura de microsserviços para sua infraestrutura de dados de clientes, projetando cada serviço para processar eventos de forma independente conforme seu objetivo
- Enviava dados para centenas de destinations server-side (ex.: Google Analytics, Optimizely etc.)
- No começo, usava uma fila única, mas isso gerava head-of-line blocking, em que a falha de um destination causava atraso geral
- Para resolver isso, passou a usar serviços e filas separados para cada destination, obtendo isolamento de falhas e escalabilidade independente
- Porém, à medida que o número de serviços cresceu, a complexidade operacional e o custo de manutenção aumentaram rapidamente, levando à queda na velocidade de desenvolvimento e ao aumento da taxa de falhas
Problemas de repositórios separados e bibliotecas compartilhadas
- Cada destination usava formatos de API diferentes, exigindo código de transformação customizado
- Inicialmente, tudo era gerenciado em um único repositório, mas falhas nos testes afetavam o conjunto inteiro, o que levou à separação dos repositórios
- Depois, com a adição de mais de 50 novos destinations, surgiram mais de 50 repositórios
- Bibliotecas compartilhadas foram introduzidas para funções comuns, mas cresceram os problemas de divergência de versão e o custo de deploy
- Como os padrões de carga variavam entre os serviços, era difícil definir configurações de autoescalonamento, e em alguns casos os operadores precisavam ajustar manualmente
Migração para o monólito e adoção do Centrifuge
- Foi tomada a decisão de consolidar mais de 140 serviços em um único serviço
- Para substituir as filas individuais, foi desenvolvido o sistema Centrifuge, que entrega todos os eventos a um único serviço
- Depois, o Centrifuge evoluiu para a infraestrutura de backend do Connections da Twilio Segment
- Com a transição para uma estrutura de serviço único, foi possível reduzir a carga operacional e simplificar a resposta a falhas
Monorepo e automação de testes
- Todo o código dos destinations foi unificado em um único repositório, padronizando em uma única versão mais de 120 dependências
- Isso simplificou o gerenciamento de versões e melhorou a eficiência de manutenção
- Para automatizar os testes, foi introduzido o Traffic Recorder
- Ele registra e reproduz requisições e respostas HTTP reais, eliminando dependências de rede externa
- O tempo de teste caiu de minutos para a faixa de milissegundos, com grande ganho de estabilidade
- A taxa de falhas nos testes caiu, e a produtividade dos desenvolvedores melhorou significativamente
Efeitos e trade-offs da estrutura monolítica
- Após a consolidação em um serviço único, a velocidade de deploy e a eficiência de desenvolvimento melhoraram bastante
- Em um ano, o número de melhorias em bibliotecas compartilhadas subiu de 32 para 46
- Um único engenheiro consegue fazer deploy em poucos minutos
- A eficiência operacional também melhorou, permitindo absorver picos de carga com um grande pool de workers
- Ainda assim, existem desvantagens, como maior dificuldade de isolamento de falhas, queda na eficiência de cache e risco em atualizações de dependências
- Parte dessas perdas foi compensada pela simplicidade operacional e pelo ganho de produtividade
Conclusão
- Os microsserviços resolveram os problemas iniciais de desempenho, mas se mostraram inadequados para expansão em larga escala e atualizações em lote
- A migração para o monólito melhorou tanto a estabilidade operacional quanto a velocidade de desenvolvimento
- Para que a transição seja bem-sucedida, são essenciais um sistema de testes robusto e a aceitação dos trade-offs
- A Twilio Segment ainda mantém microsserviços em parte da infraestrutura, mas avalia que, para destinations server-side, a arquitetura monolítica é mais adequada
2 comentários
Acho que dividir tudo e normalizar cada parte traz alguns riscos.
Comentários do Hacker News
Ao reunir o código de todos os destinos em um único repo, foi possível consolidar tudo em um único serviço
Como resultado, a produtividade de desenvolvimento melhorou muito. Agora não é mais necessário implantar mais de 140 serviços sempre que uma biblioteca compartilhada é alterada
Um único engenheiro consegue fazer o deploy em poucos minutos
Se toda mudança em biblioteca exige reimplantar todos os serviços, então isso não é um serviço de verdade, mas sim um monólito distribuído
A própria ideia de forçar a sincronização de bibliotecas compartilhadas em todos os serviços vai contra a filosofia de uma arquitetura de serviços
Isso está mais próximo de um sistema compartilhado de build e deploy no estilo da Amazon do que de “reimplantar tudo a cada atualização de biblioteca”
As bibliotecas são obtidas de uma única fonte gerenciada centralmente, e quando as versões divergem é preciso migrar tudo por questões de compatibilidade
Quando uma vulnerabilidade de segurança exige remover uma versão específica, é necessário redeploy geral, mas os benefícios da gestão centralizada são muito maiores
Esse tipo de sistema ainda é classificado como microsserviços, mas em custo e eficiência operacional ele funciona como um ambiente compartilhado
Chamar isso de monólito distribuído é um exagero
Ao seguir o padrão de microsserviços, o risco de deploy aumenta, embora no começo isso não fique tão visível
Por exemplo, se você corrigiu um bug em uma biblioteca relacionada a dinheiro, na prática acaba se perguntando se não precisa reimplantar todos os serviços
Uma biblioteca com vulnerabilidade de segurança precisa ser substituída por completo independentemente do desenho do sistema
Nesses casos, uma arquitetura monolítica é até mais fácil de lidar
Em microsserviços de verdade, eles deveriam trocar mensagens e usar JSON
Bastaria conhecer a API, não o código. Só assim cada um poderia ser implantado e escalado de forma independente
Não faz mais sentido usar módulos compartilhados?
Na empresa anterior, tudo era operado como microsserviços, e antes disso a empresa era serverless na AWS
Em ambos os casos, o maior problema era a comunicação entre serviços. Era difícil sincronizar contratos, e o deploy também era complicado
No começo dava para avançar rápido, mas com o tempo a complexidade explodiu. Surgiu o desenvolvimento baseado em medo, e havia reuniões demais
Na empresa atual, a estrutura é monolítica e muito mais fácil de lidar. Os tipos são claros e o refactoring é simples
É interessante ver agentes de IA construídos sobre a nossa própria plataforma se autoaperfeiçoando dentro da codebase
A única desvantagem é o tempo de build mais longo, mas com a evolução da cadeia de ferramentas espero deploys 10x mais rápidos em 2026
Minha conclusão é que, graças à estrutura monolítica, conseguimos crescer e escalar muito mais rápido
Em uma estrutura monolítica, a separação de responsabilidades sempre acabava se rompendo, e o acoplamento entre equipes era forte
Velocidade real e escala só eram possíveis quando as equipes estavam separadas
Foram necessários 2 anos, 50 equipes e mais de 150 pessoas para migrar de ORM para DTO
Um trabalho complexo assim não teria sido possível sem microsserviços
Olhando para esse texto, o cerne do problema não parece ser a escolha técnica entre microsserviços vs. monólito, mas sim
a qualidade e a estrutura da organização de engenharia
O repositório de código e a estrutura de testes revelam exatamente o nível da organização
Se não houver alguém capaz de dizer “isso não vamos fazer”, a complexidade explode
É preciso haver uma liderança com autoridade para a equipe parar e pensar
Quando surgiam problemas de API, em vez de analisar a causa, corrigiam os dados e fechavam o ticket
Mesmo quando o mesmo problema se repetia, a causa raiz não era resolvida
Dá até para prever em certa medida a estrutura da codebase de uma empresa só pelas entrevistas
Isso não parece exatamente uma migração para monólito, e sim ainda uma estrutura SOA
Só que com um escopo de serviço maior
Se uma única equipe gerencia 140 serviços, então SOA é uma estrutura para escalar a equipe, não os serviços
Se uma única equipe gerencia todas as bibliotecas compartilhadas, surgem inconsistências de versão e confusão de API
No fim, a estrutura organizacional define a arquitetura. Uma equipe consolidou tudo para reduzir a complexidade
Isso não é “monólito”, mas sim um nível de serviço com escopo ajustado adequadamente para a equipe
Acho que essa estrutura é a mais ideal. Se a equipe crescer, será preciso separar de novo
Não sou um defensor de microsserviços, mas chama atenção a falsa dicotomia entre “monorepo vs. microsserviços”
Ferramentas demais assumem uma relação 1:1 entre serviço e repo
Mas é perfeitamente possível manter tudo em um único repo e ainda assim fazer deploy independente
Seria ótimo se lugares como o GitHub pudessem tratar pastas como serviços independentes
Usávamos Bazel para gerenciar a árvore de dependências e
bazel querypara encontrar os alvos afetados e executar os testes automaticamenteCriamos um workflow integrado ao GitHub Actions para bloquear PRs
Funcionava bem, mas levou meses para ficar pronto
O problema real era a falta de operação e ferramentas — CI, autoscaling e esquema de on-call estavam todos deficientes
As duas abordagens podem fracassar
Em ambientes como Node.js ou Python, há um limite para a quantidade de código que o event loop consegue processar
Já gerenciei 200 serviços com 6 a 8 pessoas, e também um único monólito com 80 pessoas
Microsserviços são melhores para mudanças pequenas, mas mudanças amplas são difíceis
Monólitos são o contrário
No fim, o que importa não é a arquitetura, mas sim a forma de abstração, testes e desacoplamento
O critério de “micro” não é a tecnologia, e sim a unidade de negócio
Se você dividir abaixo disso, vira um nanosserviço
Em ambientes como Beam, JVM, Rust e Go isso já é um problema resolvido
Seria um problema de cache de CPU?
Achei que normalmente se usasse Go, Java ou C#
Na maioria das empresas, microsserviços eram na verdade a causa de 90% dos problemas
Isso não faz sentido fora de organizações gigantes como AWS, Google ou Netflix
Já é difícil dividir um sistema em unidades componíveis; adicionar fronteiras de rede por cima disso é tolice
Acho que a próxima tendência será sair de React e SPA e voltar para uma abordagem centrada no servidor
Dizer que a mudança para microsserviços aconteceu porque “os testes quebravam com frequência” parece uma abordagem muito invertida
É estranho reestruturar totalmente a codebase por causa de testes quebrando
Ao separar VMs e configurações de CI/CD por equipe, os conflitos de teste desapareceram
A desvantagem é não detectar imediatamente conflitos entre funcionalidades, mas como a propriedade do código era clara, isso não era um grande problema
Houve um pedido para adicionar [2018] ao título
Dizem que separaram os repos porque “quando os testes quebravam, era preciso mexer até em código sem relação”, mas
parece que existiam outras soluções possíveis, como mudar a forma de executar os testes ou permitir deploy manual
Separar os repos não era a única resposta