5 pontos por GN⁺ 2025-12-14 | 2 comentários | Compartilhar no WhatsApp
  • 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

 
yangeok 2025-12-16

Acho que dividir tudo e normalizar cada parte traz alguns riscos.

 
GN⁺ 2025-12-14
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

    • O que você disse faz sentido, mas na prática a situação é bem mais complexa
      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
    • Falar é fácil, mas na prática isso tende a causar bugs sutis e incompatibilidades entre serviços
      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
    • O fato de precisar atualizar uma biblioteca de forma geral não significa necessariamente acoplamento ruim
      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
    • O próprio conceito de biblioteca compartilhada faz com que todos os serviços fiquem fortemente acoplados
      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
    • Então isso quer dizer que seria preciso reescrever o código de logging em cada um dos 140 serviços?
      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

    • Minha experiência foi o oposto. Trabalhei 10 anos na SendGrid e vi a empresa crescer de 12 para 500 pessoas, e isso só foi possível graças à arquitetura de serviços
      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

    • Muitas equipes carecem de disciplina técnica
      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
    • Trabalhei no projeto da Twilio, e era realmente uma bagunça
      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
    • A Lei de Conway se prova mais uma vez
      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

    • Na empresa anterior eu implementei isso diretamente
      Usávamos Bazel para gerenciar a árvore de dependências e bazel query para encontrar os alvos afetados e executar os testes automaticamente
      Criamos um workflow integrado ao GitHub Actions para bloquear PRs
      Funcionava bem, mas levou meses para ficar pronto
    • Essa frase de que “ao mudar de microsserviços para monólito os problemas desapareceram” incomoda
      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

    • “Se é um software para resolver um único problema de negócio, então faz sentido agrupá-lo em um único microsserviço
      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
    • Esse tipo de racionalização no fim parece só um paliativo para encobrir limitações do runtime
      Em ambientes como Beam, JVM, Rust e Go isso já é um problema resolvido
    • Fico curioso sobre qual seria exatamente a unidade desse limite da quantidade de código que o event loop consegue processar
      Seria um problema de cache de CPU?
    • Tenho dúvidas se em ambientes grandes ainda se usa mesmo Node.js ou Python
      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

    • Nós também passamos por algo parecido, mas resolvemos dando a cada equipe um ambiente de desenvolvimento independente
      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

    • Fico curioso se agora eles voltaram de novo para microsserviços
    • Acho meio ruim ressuscitar um texto de 7 anos atrás. No mundo da tecnologia, isso já é praticamente história antiga
  • 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