Executando verificação de segurança...
2

Do Caos à Modernidade: O que aprendi migrando um YMS do Angular 16 ao 21 sozinho (e o mito do prazo de 2 semanas)

No mundo corporativo, é muito comum ouvirmos frases como: "É só rodar um comando de update" ou "Dá pra fazer isso rapidinho". Recentemente, recebi o desafio de migrar o frontend de um sistema de gestão de pátio logístico (YMS - Yard Management System) do Angular 16 para a versão 21.

O prazo inicial estimado pela gestão? 2 semanas.

Hoje, dois meses depois, o projeto está rodando liso na versão nova, mas ainda estamos fazendo ajustes e "pentes finos". Quero compartilhar o que aprendi nessa jornada atuando como "exército de um homem só", os ganhos técnicos absolutos e a realidade de conciliar inovação com a manutenção de sistemas legados.

O Cenário: A Complexidade de um YMS e o Desafio do Legado

Para dar contexto, um YMS não é um CRUD simples. Estamos falando de um sistema "vivo" que gerencia entrada e saída de caminhões, pesagem em balanças, fluxos logísticos complexos e monitoramento em tempo real. Qualquer erro no frontend pode literalmente parar uma frota de caminhões.

Fazer essa migração sozinho já seria um desafio monumental, mas a realidade bateu à porta: nossos clientes on-premise.

Como temos clientes que utilizam o sistema instalado localmente (e que não atualizam a infraestrutura com frequência), eles precisavam continuar na versão 16. Conciliar o suporte contínuo para essa base legada enquanto eu reescrevia e modernizava a arquitetura para o Angular 21 exigiu um controle de versão impecável e muita sanidade mental.

Faxina Arquitetural: O Fim dos NgModules

O ponto de virada dessa migração não foi apenas a troca de versão, mas a decisão de abandonar completamente os NgModules. Ao migrar para Standalone Components, o projeto passou por uma auditoria forçada.

Sem a "caixa preta" dos módulos, que muitas vezes escondiam dependências e componentes que ninguém mais usava, ficou evidente o quanto de código morto o sistema carregava. Aproveitei esse movimento para remover bibliotecas órfãs e telas legadas. O resultado foi uma redução da pasta src de 40MB para 20MB. Essa limpeza reduziu drasticamente a carga cognitiva: builds locais ficaram mais velozes e a indexação da IDE tornou-se instantânea.

Para sustentar o crescimento do sistema sem transformar o código em um emaranhado de dependências, decidi abandonar a clássica organização por "tipo de arquivo" (onde todos os serviços e componentes ficam amontoados em pastas globais). Em um domínio denso como um YMS, a arquitetura Feature-based é o que garante a sanidade: cada funcionalidade passa a ser um ecossistema independente e encapsulado.

Isso nos trouxe alta coesão funcional: se preciso dar manutenção na lógica de pesagem, tudo o que preciso, do componente à regra de negócio, está dentro da mesma pasta. Isso elimina aquele vaivém cansativo entre diretórios espalhados por todo o projeto e torna o isolamento de contexto muito mais claro:

src/
└── app/
    ├── core/               # Singleton, Auth, Guards, Interceptors
    ├── features/           # O coração do negócio isolado
    │   ├── autenticacao/
    │   ├── frota/          # Tudo sobre pesagem fica APENAS aqui
    │   ├── logistica/
    │   └── relatorios/
    └── shared/             # UI Components, Pipes, Diretivas globais

Refatoração: Menos Boilerplate, Mais Performance

Aproveitei o motor do Angular moderno para refatorar visualmente as telas, deixando a UI mais fluida, mas foi no TypeScript que a mágica aconteceu. Separei exemplos que mudaram meu dia a dia:

1. O Fim do "Inferno do super()" com inject()

Um dos maiores ganhos de produtividade foi em nossos componentes e serviços genéricos. No padrão antigo, qualquer componente filho que herdasse de uma classe pai precisava passar todas as dependências via super() no construtor. Se o pai tivesse 5 dependências, o filho era obrigado a declará-las apenas para repassá-las.

Com o inject(), o componente filho "automaticamente" herda as dependências do pai sem nenhum boilerplate extra no construtor. O código ficou absurdamente mais limpo.

Antes (Angular 16 - Verboso):

@Component({ ... })
export class FilhoComponent extends PaiComponent {
  constructor(
    private service: LogisticaService, 
    private router: Router
  ) {
    super(service, router); // Repetição desnecessária
  }
}

Depois (Angular 21 - Moderno):

@Component({ standalone: true, ... })
export class FilhoComponent extends PaiComponent {
  // O filho já possui acesso às dependências injetadas no Pai via inject()
  // sem precisar de construtor ou super()
}

2. Resolvendo Memory Leaks com takeUntilDestroyed()

No monitoramento do pátio, lidamos com muitos fluxos reativos. Antes, eu precisava criar um Subject de destroy, implementar o OnDestroy e disparar o evento em toda tela para a memória do navegador não estourar. Agora, o framework cuida disso de forma nativa e segura.

Antes (Gerenciamento Manual):

destroy$ = new Subject<void>();

ngOnInit() {
  this.service.getDadosCaminhao()
    .pipe(takeUntil(this.destroy$))
    .subscribe();
}

ngOnDestroy() {
  this.destroy$.next();
  this.destroy$.complete();
}

Depois (Automático):

ngOnInit() {
  this.service.getDadosCaminhao()
    .pipe(takeUntilDestroyed())
    .subscribe();
}

O "Pente Fino" e a Esteira de Qualidade

Migrar de versão não adianta nada se a cultura de desenvolvimento não mudar. Mergulhei no código para fazer uma faxina implacável: removi centenas de comentários inúteis gerados por IAs que só atrapalhavam a leitura e deletei os famosos console.log de debug esquecidos.

Para blindar o projeto daqui para frente, modernizei todo o ferramental:

  • Testes na velocidade da luz: Substituí o antigo Karma/Jasmine pelo Vitest, acelerando absurdamente o feedback loop, e adicionei Cypress para garantir os fluxos críticos de entrada/saída de veículos.
  • Padronização forçada (no bom sentido): Configurei o ESLint e o Husky com regras rígidas. O código só passa se estiver no padrão.
  • Commits Semânticos: Adicionei o .commitlintrc. Agora seguimos um fluxo de Conventional Commits e code review obrigatório antes de qualquer integração.

A Realidade sobre a IA e o Prazo

Todo esse esforço gerou um impacto colossal na ponta: o tamanho do pacote final enviado ao cliente (bundle) caiu de 40MB para apenas 20MB. O sistema está absurdamente mais rápido através de Tree-Shaking eficiente.

Mas voltemos ao prazo de 2 semanas. Ele era inviável. A Inteligência Artificial ajuda muito? Demais. Ela acelerou a refatoração sintática repetitiva e me salvou de horas de digitação. Porém, a IA não tem o contexto do negócio. Ela não sabe que se o módulo da balança falhar, a empresa toma multa. As decisões arquiteturais, o isolamento das features e o "pente fino" de qualidade são um trabalho puramente humano.

Conclusão

Essa jornada de 2 meses (que ainda continua com as melhorias contínuas) me transformou como profissional. Saí do papel de "alguém que escreve código Angular" para assumir uma visão real de Engenharia de Software. O projeto hoje é outro: robusto, testável, escalável e pronto para o futuro.

Atualizar versão não é luxo, é pagar dívida técnica.

E vocês da comunidade, já tiveram que encarar uma migração crítica e complexa sozinhos com prazos irreais? Como lidaram com o embate entre a expectativa da gestão e a necessidade técnica de fazer a coisa certa?

Carregando publicação patrocinada...
1

Incrível! Pra mim uma história real bem contada vele mais do que um tutorial.

Parabéns por descascar esse abacaxi, cortar em rodelas, fazer um suco e ainda botar um guarda-chuvinha.

1

Historia bacana.

No futuro considere introdudir teste de compotamento fom cucumber, eles ajudam bastante nesse tipo de migracao..

Algumas coisas com o fim do ngmodules parece ter sido bem legal, mas sinceramente a remocao do super nao parece ser alguma coisa eu vai mudar o dia de ninguem nao.

No mais parabens

1

Obrigado pelo comentário!

A questão do super é mais para limpar o código e deixá-lo mais enxuto. De fato, não teria muito impacto no dia a dia; é mais para remover código em excesso.

Eu particularmente gostei do formato e da simplicidade com que os códigos ficaram.

É gosto mesmo .