← voltar ao sobre
2025 · 7 min de leitura

Não é difícil, é amplo

Migrando uma plataforma de 300 marcas para fora do Material UI v4

Nosso build de produção levava 54,5 segundos. Nosso dev server levava cerca de um minuto pra subir. Com cinco desenvolvedores batendo nisso várias vezes por dia, era aquele tipo de custo que você para de notar porque sempre esteve ali.

Mas os tempos de build não eram o problema de verdade. Eram o sintoma que eu conseguia medir.

O problema de verdade

A plataforma — um CRM e produto de mensageria whitelabel espalhado por seis repositórios — estava presa ao Node 16. Não por escolha. O Material UI v4 era o que nos segurava ali, e toda tentativa de avançar o Node esbarrava nele.

O que tornava isso teimoso não era dificuldade técnica. Não havia solução esperta a achar, nem bug sutil a caçar. Sair do MUI v4 significa ir pro v5, e o v5 troca o JSS pelo Emotion. Isso não é atualizar uma dependência — é reescrever a camada de estilo de cada componente da aplicação.

Então o problema não era difícil. Era amplo.

Essa distinção importa, porque problemas amplos apodrecem diferente dos difíceis. Um problema difícil atrai atenção; alguém acaba achando ele interessante o bastante pra resolver. Um problema amplo só fica ali parado, porque nenhuma peça isolada é difícil e o todo é enorme. Ninguém quer ser a pessoa que mexe em cada componente do código.

Também era uma corrente

Depois que mapeei, não havia caminho incremental:

  • Node 16 estava preso pelo MUI v4
  • Sair do MUI v4 significa MUI v5
  • MUI v5 precisa de React 18
  • React 18 no nosso setup significava trocar o build

Quatro upgrades, nenhum deles podia ir ao ar de forma independente. Qualquer tentativa de fatiar teria deixado o app num estado que não buildava. Tinha que passar como uma peça só.

O que tornou viável

Aqui está a parte sobre a qual eu quero mesmo fazer um ponto.

Um mês antes de tudo isso, eu estava trabalhando em algo sem relação — ou eu achava. A plataforma é whitelabel, com mais de 300 marcas parceiras, cada uma precisando do seu visual, e cada tela resolvia isso de forma independente. O mesmo componente reimplementado em vários lugares com estilos ligeiramente diferentes. Corrija um bug em um, e ele seguia quebrado nos outros.

Então construí um design system: design tokens pra camada de tema, uma biblioteca de componentes compartilhada por cima, e uma reestruturação do código por feature pra ficar óbvio onde cada coisa morava.

Construí pensando em consistência. O que ele de fato me deu foi uma migração barata.

Quando comecei, a maior parte da plataforma compunha um pequeno conjunto de componentes compartilhados em vez de estilizar tela por tela. A superfície que podia quebrar havia encolhido. Em vez de cada tela ser um risco, só os componentes-raiz eram.

Eu não tornei a migração segura testando mais. Tornei segura encolhendo o que podia quebrar.

Execução

O trabalho em si era mecânico, e foi isso que tornou duas semanas realista. O @mui/codemod cuidou da maior parte da transformação v4 → v5. Busca e substituição em larga escala cuidaram de quase todo o resto. Problemas amplos são exatamente o tipo em que ferramentas são boas — o motivo de não serem resolvidos não é capacidade, é apetite.

Validando

O risco aqui não é funcional. Quando você troca o motor de estilo, seus testes continuam passando e suas telas saem sutilmente erradas. Testes unitários não pegam um botão com o padding errado.

Então validei em camadas, cada uma pegando um modo de falha diferente:

  • Codemods — consistência mecânica em milhares de arquivos, sem erros de digitação humanos
  • QA manual com o time de dev — quebras técnicas e comportamento
  • Uma semana em staging com nosso time de vendas — a camada visual

A terceira fez o trabalho de verdade. Nosso time de vendas vive naquele CRM todo dia. Eles sabem como cada tela deveria parecer de um jeito que nenhuma suíte de testes codifica, e notam o errado na hora. Dar a eles uma semana de uso normal valeu mais que qualquer snapshot test que eu escrevesse no mesmo tempo.

O que acharam foi estreito: paddings em botões, selects e campos de texto. E como eram componentes compartilhados, corrigir na raiz e nos estilos globais corrigia em todo lugar de uma vez — o design system se pagando pela segunda vez.

Resultados

AntesDepois
Build de produção (frio)54,5s2,3s
Build de produção (Webpack, cache quente)22,4s
Dev server (início frio)~60s~80ms
Versão do NodePresa no 16Livre

Cerca de 24× mais rápido nos builds de produção. Mesmo medindo contra o melhor caso do Webpack — cache quente do babel-loader — ainda é uns 10×, e o Vite entrega 2,3s de forma consistente em vez de depender do estado do cache. Previsibilidade acabou importando tanto quanto o número bruto.

A mudança no dev server é diferente em tipo, não só em grau. O Webpack empacota a aplicação inteira antes de servir qualquer coisa. O Vite serve ESM nativo e transforma só o módulo que o navegador pede, então o start não escala com o tamanho do projeto. Oitenta milissegundos numa plataforma de seis repositórios são os mesmos oitenta milissegundos de um app de brinquedo.

O que eu faria diferente

Testes automatizados de regressão visual — Chromatic, Percy ou snapshots do Playwright — teriam dado uma rede de segurança repetível em vez de um esforço humano único. O QA manual funcionou, mas não escala e não vai estar lá na próxima migração.

Eu também gostaria de uma cobertura de temas mais ampla em staging. Com 300 marcas parceiras, um componente correto sob um tema não é necessariamente correto sob todos.

A lição

A lição aqui não é sobre o Vite, e nem é realmente sobre o Material UI.

É que o momento de investir em arquitetura compartilhada é antes de você precisar dela. O design system não se justificou pela consistência visual, embora tenha entregue isso. Ele se justificou tornando uma mudança cara em barata — e a essa altura já estava no lugar, que é a única forma de isso funcionar.