28 pontos por GN⁺ 2025-05-10 | 10 comentários | Compartilhar no WhatsApp
  • Separar o código cedo demais em microsserviços em startups iniciais causa uma queda séria na produtividade da equipe e aumento da complexidade
  • A arquitetura monolítica (única) oferece otimização para sobrevivência com deploy simples, lançamento rápido de novas funcionalidades e colaboração eficiente
  • Microsserviços só trazem vantagem real da separação quando há necessidade de grande escalabilidade, cargas de trabalho diferentes ou requisitos de runtime separados
  • Separação excessiva de serviços, proliferação de repositórios, ambiente local de desenvolvimento instável e desalinhamento da stack tecnológica levam a redução de velocidade e queda do moral da equipe
  • Startups devem começar com um monólito e adotar uma abordagem cuidadosa, separando apenas quando surgirem gargalos claros

Introdução e contexto

  • A sobrevivência de uma startup é determinada por iteração rápida, entrega de novas funcionalidades e criação de valor para o usuário
  • A arquitetura base do projeto, a stack tecnológica e a escolha da linguagem de programação afetam a velocidade da equipe
  • A adoção precoce de microsserviços parece sofisticada no papel, mas na prática causa queda de produtividade, serviços inacabados e excesso de complexidade
  • Dados: surgem vários custos de desenvolvimento, como orquestração de serviços, problemas com Docker/scripts, CI/CD duplicado, acoplamento entre serviços, custo de observabilidade e testes dispersos
  • Em vez de avançar sem critério para uma arquitetura complexa, o texto enfatiza a importância de uma arquitetura pragmática

Pontos fortes do monólito

  • Seja um SaaS ou um simples wrapper de banco de dados, o app fica mais complexo com o tempo, mas a arquitetura monolítica tende a permanecer simples e flexível com mais facilidade
  • O deploy é fácil, há suporte de frameworks populares (Django, ASP.Net, Nest.js etc.) e o benefício das comunidades open source é grande
  • Caso real: em uma startup do setor imobiliário, um monólito em Laravel permitiu alcançar com facilidade inúmeras integrações com terceiros e expansão de funcionalidades
  • Foi possível focar em atender as demandas e expectativas do negócio sem introduzir infraestrutura complexa nem separar tudo em microsserviços
  • Lição: a simplicidade da arquitetura ajuda a equipe a se concentrar em entregar; desde que se evite falhar na modularização interna, a escala continua viável

Microsserviços são sempre a melhor opção?

  • Muitos engenheiros acham que microsserviços são a resposta padrão, mas na prática eles só mostram seu valor quando existe um motivo especial, como escalabilidade
  • Em equipes pequenas, com pouco volume e em fase de mudanças rápidas, o efeito pode ser o oposto: infra duplicada, desenvolvimento local lento e ciclos de iteração mais demorados
  • Empresas como a Segment também passaram por mudanças motivadas por estruturas ineficientes
  • Lição: microsserviços são uma ferramenta para resolver gargalos, não um template para o começo

Por que microsserviços falham especialmente no estágio inicial

1. Fronteiras de serviço arbitrárias

  • Ao tentar dividir serviços por lógica de negócio com base em design orientado a domínio e teoria de clean architecture, a lógica real e as fronteiras dos serviços muitas vezes não se alinham direito
  • Exemplo: separar usuário, autenticação e autorização aumenta a complexidade de deploy e a dificuldade de desenvolver APIs
  • Separar nessa fase, antes de qualquer gargalo real aparecer, torna o sistema instável e lento
  • Simular futuras separações com flags ou toggles internos e explorar fronteiras de forma orgânica pode ser mais eficaz do que correr para trabalhos urgentes de infraestrutura
  • Lição: decisões de separação devem se basear em gargalos reais, não em teoria

2. Excesso de repositórios/infraestrutura

  • Estilo de código, testes, configuração, documentação e CI/CD aumentam na mesma proporção do número de serviços
  • Com uma estrutura de monorepo, é possível administrar tudo em um só lugar e melhorar a consistência do código e a eficiência da colaboração
  • No caso de Node.js, ferramentas como nx ou turborepo facilitam o gerenciamento de dependências e builds entre serviços internos
  • Como desvantagens, há dependências complexas, necessidade de ajuste de performance no CI e demanda por ferramentas de build mais rápidas
  • No ecossistema Go, também faz sentido começar com um workspace único e só depois, com o crescimento, considerar a separação em módulos
  • Lição: equipes pequenas podem ganhar tempo com monorepo e infraestrutura compartilhada

3. Ambiente local de desenvolvimento instável

  • Tempo excessivo para rodar localmente, scripts complexos e dependências específicas do sistema causam atraso no onboarding e perda de produtividade
  • Falta de documentação, problemas de compatibilidade e hacks específicos de OS (ex.: scripts apenas para macOS) viram obstáculos
  • Em um projeto, um proxy em Node.js reduziu a complexidade do Docker e encurtou o tempo de onboarding dos desenvolvedores
  • Lição: se o app só roda em um único sistema operacional, a produtividade do time acaba dependendo da confiabilidade de um único laptop

4. Desalinhamento da stack tecnológica

  • Node.js e Python são bons para iteração rápida, mas em ambientes de microsserviços surgem com frequência problemas de desalinhamento entre build e runtime
  • Go tem vantagens em binários estáticos, builds rápidos e simplicidade operacional
  • A escolha da stack no início deve ser cuidadosa e, se necessário, é possível misturar linguagens com protocolos como gRPC
  • Sem exigências especiais como ML ou ETL, misturar stacks tende apenas a aumentar a complexidade
  • Lição: escolha uma stack adequada à realidade da equipe, não ao cenário idealizado

5. Complexidade oculta: comunicação e monitoramento

  • Em microsserviços, service discovery, versionamento de API, tracing distribuído e gestão centralizada de logs são itens obrigatórios
  • Rastrear bugs e incidentes em um monólito pode exigir apenas um stack trace, mas em um ambiente distribuído isso fica muito mais complexo
  • Para fazer direito, é preciso adotar ferramentas especializadas como OpenTelemetry e montar uma stack de observabilidade
  • É preciso reconhecer que sistemas distribuídos exigem investimento obrigatório em desafios adicionais de engenharia

Quando microsserviços fazem sentido

  • Isolamento de workload: separar tarefas assíncronas específicas, como processamento de imagem ou OCR, pode ser eficiente
  • Desequilíbrio na necessidade de escala: quando Web API e workloads de ML têm exigências diferentes de hardware e operação, faz sentido separá-los
  • Necessidade de outro runtime: componentes incompatíveis com o runtime do app principal, como código legado em C++, devem ser mantidos como serviços separados
  • Como mostram casos de grandes organizações de engenharia (ex.: Uber), isso só é adequado quando há necessidade organizacional clara e capacidade operacional madura
  • Mesmo em equipes pequenas, a separação pode ser prática em casos raros, como um serviço externo de analytics fácil de gerenciar
  • Lição: adote apenas em workloads em que os benefícios práticos da separação sejam claros

Guia prático para startups

  • Comece com um monólito e foque no trabalho usando frameworks já comprovados
  • Um repositório único traz mais benefícios para equipes iniciais em eficiência operacional, gestão e segurança
  • Simplificar o ambiente local de desenvolvimento é importante e, quando isso for difícil, é essencial fornecer documentação e vídeos detalhados
  • Invista cedo em CI/CD para automatizar tarefas repetitivas e reduzir a carga mental da equipe
  • Separe seletivamente apenas quando surgirem gargalos claros; fora isso, concentre-se em modularização e testes dentro do monólito
  • O objetivo principal é preservar a velocidade de desenvolvimento
  • Lição: comece pela simplicidade e escale de acordo com a necessidade real de separação

Se você realmente precisar usar microsserviços

  • Avalie a stack tecnológica e invista em ferramentas de experiência do desenvolvedor: é necessário preparar automação por serviço, scripts claros e ferramentas integradas de gestão de deploy
  • Padronize protocolos confiáveis de comunicação entre serviços: é preciso entender itens extras de implementação, como consistência de schemas de mensagens, documentação e tratamento de erros
  • Estabilize a infraestrutura de testes: testes unitários, de integração e E2E precisam escalar de acordo com a separação dos serviços
  • Considere bibliotecas compartilhadas: mantenha o código comum de observabilidade e comunicação no menor escopo possível para evitar rebuilds frequentes de todos os serviços
  • Observabilidade deve entrar cedo: comece pelo básico, como logs JSON estruturados e correlation IDs
  • Em resumo, se você vai aceitar a complexidade, é importante projetar um sistema gerenciável e tratá-lo com disciplina total

Conclusão

  • Adotar microsserviços com pressa só deixa peso extra, então a simplicidade deve vir em primeiro lugar
  • Não separe sem um ponto claro de dor; a perspectiva importante é adicionar apenas a complexidade mínima necessária para sobreviver e crescer
  • Sobreviver vem primeiro; escalar fica para depois

10 comentários

 
kuil09 2025-05-12

Concordo com a essência do texto original de modo geral.
Acho que é uma questão de experiência da organização.
Pode ser útil imaginar alguém vendendo comida em um food truck e depois se transformando em um restaurante.
Desde o começo, falta absolutamente experiência aos envolvidos para considerar divisão de trabalho e especialização.

 
dhlee0305 2025-05-12

Acho que startups precisam escolher caminhos de menor custo para prolongar seu tempo de sobrevivência. Microsserviços definitivamente não são baratos. Quando aplicados de fato no ambiente real, acabam gerando custos consideráveis. Sempre que possível, acredito que projetar uma arquitetura adequada ao próprio serviço da empresa é a forma de obter efeitos semelhantes com menos custo.
Não estou dizendo que microsserviços são ruins. É um modelo que exige muitos recursos.

 
mhj5730 2025-05-12

Acho que só dois já bastam: um monólito exclusivamente síncrono e um monólito exclusivamente assíncrono... Vejo a adoção de microsserviços, no fim das contas, até onde vai a escala das tabelas que precisam ser gerenciadas pelo banco de dados. Se o número de tabelas for absurdamente grande e complexo, aí vale pensar em MSA; se for simples, monólito é a escolha ideal.

 
roxie 2025-05-12

Quando todas essas ondas tiverem passado, como as gerações futuras se lembrarão desta era?

 
n1ghtc4t 2025-05-12

Naquela época, também havia a onda daquela época...

 
bungker 2025-05-11

Também acho que microsserviços têm muitas vantagens em startups. Antes de tudo, recomendo muito usar um monorepo.

  • Quando a direção do produto muda, nos microsserviços fica mais claro e são menos pontos para modificar do que em um monólito. Acho que isso é realmente muito importante.
  • Na era do desenvolvimento com IA, unidades menores de microsserviços são mais fáceis de desenvolver com ajuda de IA. (Não estou dizendo que em um monólito não dá para fazer.)
  • Reconheço o peso do CI/CD, mas há serviços que acabam sendo descartados ainda na fase de definir a direção. Mesmo que você construa isso só quando a direção final estiver definida, no fim quase vira um simples copiar e colar, então dá para montar em menos de uma semana.
  • Existem projetos open source com pontos fortes bem claros dependendo da linguagem. Segurança e lógica de negócio em Java, IA em Python, por exemplo — numa arquitetura de microsserviços dá para usar o máximo possível de open source.
 
andone 2025-05-11

Concordo que, na era do desenvolvimento voltado para IA, é essencial implementar em unidades pequenas, com responsabilidade única.

 
bus710 2025-05-10

Isso apareceu de leve nos comentários também, mas a linha beam/otp parece bem flexível e boa. No caso do Gleam, a boa sintaxe tanto de Go quanto de Rust, somada à estabilidade do beam, fez dele uma linguagem bastante impressionante. Estou começando a querer usá-la em projetos pequenos.

 
ndrgrd 2025-05-10

Se você divide o time demais, até se reunir para trocar ideias vira um trabalho enorme.

 
GN⁺ 2025-05-10
Opinião no Hacker News
  • Perceberam que, sem problemas reais de escala, equipes grandes ou domínios que precisem evoluir de forma independente, microsserviços só geram custo e não trazem benefício; na prática, a própria Segment reverteu essa separação por esse motivo. A ideia é que isso é mais um padrão organizacional do que uma tecnologia. Em geral, vale operar um monólito único e só separar frontend, backend e tarefas demoradas (por exemplo, geração de PDF) em serviços distintos. Depois, quando o número de serviços cresce, aí começam as preocupações com padronização e arquitetura. Com menos de 200 engenheiros, a perda de produtividade costuma ser maior. Não é que as empresas sobrevivam por causa dos microsserviços, mas apesar deles.
    • Alguns desenvolvedores tendem a adotar microsserviços pensando no currículo para uma futura big tech, mas isso quase não ajuda uma startup de verdade. Para impedir isso, é preciso uma liderança técnica realmente sábia.
    • Relato de uma startup com cerca de 50 engenheiros que dividiu tudo em dezenas de serviços: um novo integrante levava no mínimo 6 meses para entender o sistema; havia apenas algumas centenas de usuários, mas a complexidade era enorme. Queimaram US$ 50 milhões em capital de risco e no fim faliram. O produto era inovador e bem projetado, mas isso não adiantou.
    • Ao separar serviços, também existe a opção de não dividir o código, e sim implantar um monólito comum separado apenas por flags de função. Até workers em background podem rodar junto com o servidor web, usando health checks e métricas, enquanto o load balancer distribui o tráfego por função.
    • Como no caso vivido pela Khan Academy, depois de escalar bem um monólito, fica possível julgar os limites corretos entre serviços com base na experiência. Nesse momento, adotar microsserviços pode fazer sentido.
    • Há motivos técnicos para separar, sim, mas serviços são difíceis por natureza. Ainda assim, se for necessário, o importante é ter disposição para ganhar maturidade. Não se deve rejeitar algo só porque virou moda; é preciso avaliar os trade-offs.
    • Só porque existem "várias equipes" não significa que seja preciso fragmentar a organização, mas há diretores que veem dois squads pequenos nascerem e já querem dividir o monólito. Como equipes costumam ser reorganizadas com frequência (a cada 9 meses), definir cedo demais os limites da arquitetura pode virar um desastre. Só vale tratar algo como fronteira arquitetural se houver indício de que aquela equipe conseguirá operar de forma estável por pelo menos 18 meses.
    • Como a definição original de microsserviços era "domínios que podem evoluir de forma independente", isso nem parece algo tão novo assim.
    • Houve um caso em que apenas uma URL muito específica tinha exigências muito diferentes de tráfego e memória, e o servidor PHP não servia bem; então adicionaram só um servidor em outra linguagem. O resultado foi uma melhora de 1000x em desempenho/custo. Também vale repensar, no estilo Java/Spring/Guava, se o conceito de serviço precisa ser implementado "fora" do processo ou "dentro" dele. Se houver boa padronização de stack, versões e serialização, a produtividade pode até aumentar; sem isso, surgem falhas de comunicação, problemas de compatibilidade e muita confusão.
    • Conway's law (a estrutura do código reflete a estrutura da organização) realmente se confirma na prática.
    • Serviço é algo prestado por pessoas; microsserviços seriam serviços dentro da economia interna de uma empresa.
    • Relato de 15 anos atrás: era fácil operar uma solução única com equipe pequena, mas depois da adoção de microsserviços surgiu o sofrimento de ter de iniciar e coordenar cada serviço. Isso ensinou na prática a importância do YAGNI (não construir antes de ser necessário).
    • Há muitos motivos técnicos para separar, como caminhos de tráfego alto ou críticos. Em organizações pequenas isso normalmente não é desejável, mas recortar apenas os caminhos de alta carga pode gerar grande efeito até em startups.
    • Na prática, o principal benefício dos microsserviços é permitir que pequenos grupos trabalhem de forma independente sem interferir entre si. Mas, como várias equipes passam a ser donas de seus próprios serviços, reuniões de padronização se tornam intermináveis e, em vez de colaboração, a diplomacia do "cuide do seu próprio quintal" acaba ficando mais importante.
  • Em uma equipe de 2 a 3 desenvolvedores, adotaram microsserviços porque parecia algo elegante, apesar dos alertas em contrário. Só depois de 2 anos começaram a reconstruir o código existente e, mesmo após 3 anos, ainda não resolveram problemas de deploy e outros. Se tivessem escolhido um monólito modular no começo, nada disso teria acontecido. Concordam com o texto.
    • Se a pessoa deixa um histórico cheio de tecnologias/projetos chamativos e logo depois troca de emprego, o impacto fica para os outros.
    • Microsserviços só são realmente úteis quando cada serviço tem uma equipe própria responsável por ele. Nunca viram bom resultado quando uma mesma equipe cuida de vários serviços.
    • Grande parte dos problemas vem do fato de que as pessoas continuam pensando nos microsserviços como se fossem um único sistema (um monólito). Isso gera atrito do design ao deploy. Só funciona quando a organização inteira compra a ideia; ao transformar um sistema existente em microsserviços, costuma haver muita resistência interna.
    • No fim, a sensação é que toda startup passa por um sofrimento parecido.
    • Até em um projeto interno de UI composto por 8 a 12 microsserviços, com apenas 5 a 10 usuários reais, a única consequência foi complexidade desnecessária. Ninguém entende por quê.
    • Houve quem adotasse microsserviços + Java por influência da consultoria da Thoughtworks. A consultoria recebeu seu dinheiro e foi embora, enquanto a organização ficou perdida tentando reimplementar um monólito cheio de funcionalidades como microsserviços distribuídos. O projeto acabou fracassando e a empresa desapareceu. Desde então, passaram a desconfiar de grandes consultorias.
  • No estilo mental do grug, surge a dúvida: por que escolher deliberadamente uma forma mais difícil de construir sistemas, ainda por cima acrescentando chamadas de rede?
    • É como adicionar a uma linguagem, tipo Python, uma funcionalidade de "monkey patch" que ela nem tinha.
    • Houve a observação de que chamadas de rede transformam uma regra (rule) em uma lei (law).
    • Na piada grug, quando há muitos grugs com cérebro de grug, a superorganização passa a achar que 9 grugs conseguem fazer um bebê grug em 1 mês.
  • Microsserviços são eficazes em organizações grandes para resolver problemas humanos, remover barreiras burocráticas e dar autonomia aos desenvolvedores; em startups pequenas, o ganho real costuma ser pequeno. Ainda assim, quando um domínio específico exige mistura de stacks (por exemplo, Elixir com Python/Go), esse tipo de separação pode ser necessário.
    • Se partes do app têm cargas ou necessidades de recursos muito diferentes, até uma startup pequena pode se beneficiar de serviços separados. A maior parte da lógica de negócio pode continuar em um monólito, e apenas tarefas especiais, como GPU, ficam isoladas. Mas esse padrão não deve se transformar em uma manutenção de 100 serviços; a preferência básica continua sendo monólito, separando apenas quando necessário.
    • Microsserviços acabam criando novas dependências organizacionais e exigências de coordenação. Por exemplo, uma política de "qualquer um pode mexer em qualquer serviço" aumenta muito a manutenção, porque todo mundo passa a ter de aprender tudo para o sistema continuar funcionando. No fim, gasta-se tempo demais procurando quem consegue tocar cada parte.
    • Se houver equipes em conflito ou sobrepostas, a ineficiência cresce ainda mais.
  • Microsserviços, na arquitetura de software, lembram Conway's Law (a estrutura organizacional influencia a estrutura do código): cada fronteira de equipe vira uma fronteira que precisa ser atravessada, e criar artificialmente grandes fronteiras arquiteturais dentro da organização quase sempre é improdutivo. Em vez disso, defendem usar injeção de dependência, desenhar bons limites modulares e até aplicar o modelo de atores para gerir um monólito modular. Depois, se houver necessidade real, separar serviços fica muito mais fácil. Pela relação custo-benefício, essa abordagem funcionou melhor, com base em experiência real de dividir monólitos 3 ou 4 vezes.
    • Conway's Law é uma lei sobre comunicação, isto é, sobre fronteiras comunicacionais. Não é obrigatório alinhar tudo às fronteiras das equipes; ao contrário, muitas vezes seria preciso reorganizar a empresa em função do software, o que implicaria um reorg a cada mudança de arquitetura. Isso aprofunda o distanciamento entre equipes e objetivos de negócio. A opinião é para não idolatrar cegamente o jeito Amazon de fazer as coisas.
  • Do ponto de vista técnico, microsserviços podem ser apropriados em startups quando: (1) o app principal exige outra linguagem no núcleo (por exemplo, Rails + R), (2) apenas alguns serviços têm escala muito diferente, (3) certos dados exigem requisitos distintos de segurança ou retenção, como informações médicas. Fora isso, quase não há motivo para granular tanto; em organizações pequenas, normalmente sai pior.
    • Dividir alguns serviços novos não é a mesma coisa que microsserviços de verdade; microsserviços reais separam até coisas que, em um app comum, seriam apenas módulos.
    • Não é só escala: se confiabilidade e disponibilidade exigidas forem radicalmente diferentes, separar em serviços distintos também pode ser racional. Isso ajuda a reduzir risco e acelerar deploys por meio da separação de preocupações para cumprir SLA.
    • Mesmo nesses casos, alguns acham que isso não difere tanto assim de simplesmente operar o servidor de banco de dados separadamente.
  • O verdadeiro ganho dos microsserviços é organizacional: quebrar o problema em partes para que cada equipe seja dona completa de uma fatia e responda pelo delivery de ponta a ponta. Isso permite especialização, mudanças naturalmente incrementais e interação apenas por API. Não há compartilhamento de um único banco de dados, sistema de arquivos ou API. Em compensação, padronização e tooling de gestão (incluindo monitoramento, testes e CI/CD) tornam-se essenciais, algo que de qualquer forma todo sistema precisa em algum grau.
    • Infra, logging e autenticação podem ser empacotados ou geridos com gRPC, filas de mensagens etc. Assim, criar novos serviços fica rápido, e até os merge requests dentro das equipes ficam menores, reduzindo conflitos e aumentando a produtividade. Em equipes de 5 a 10 pessoas, havia muitos conflitos, problemas de deploy e de release usando monólito, mas após a mudança para microsserviços houve melhora em produtividade, colaboração e prontidão. Ainda assim, regras claras para testes, documentação e endpoints eram fundamentais.
    • Também existem casos em que a separação é tecnicamente necessária por desempenho, como para isolar bancos de dados, mas em startups comuns isso pode ser raro.
  • A força do ecossistema BEAM/OTP (Erlang, Elixir) é permitir manter um monólito enquanto se pratica um design "no estilo microsserviços", com transição fácil quando a separação real se torna necessária. Em pequena escala, preservam-se as vantagens do monólito; quando o sistema cresce, dá para obter escalabilidade e independência ao mesmo tempo. Em troca, é preciso aprender objetos imutáveis, comunicação concorrente e gerenciamento de falhas, além de enfrentar possível dificuldade de contratação.
  • A maioria concorda: em ambiente de startup, quase é impossível definir bons limites de serviço. Ainda assim, em equipes pequenas, microsserviços às vezes compensam por causa de APIs externas ou dependências complexas. Nesses casos, a fronteira de rede já é inevitável, então transformar isso em um serviço separado não aumenta tanto a complexidade. O mesmo pode valer quando build/deploy são complexos demais e a separação ajuda a simplificar o monólito.
  • Microsserviços só fazem sentido quando há necessidade de escalar muito ou de microgerenciamento entre equipes. Mas não se deve abrir mão de limites modulares claros. É possível obter dentro do monólito algumas vantagens dos microsserviços com orientação a objetos no estilo message passing. É necessário treinar a disciplina de não mexer diretamente no banco de dados. Em linguagens como Java, isso passa por modularização, namespaces no schema do banco, exposição mínima de interfaces e possibilidade de testes/monitoramento independentes. Também fica mais fácil implantar vários serviços ao mesmo tempo.
    • Se os limites lógicos forem bem compreendidos, dá até para reforçá-los com ferramentas como ArchUnit.