Jun 16, 2026 4 min read

AngularJS to Angular Migration: A Field Guide from a 100k-Line Production Rescue

AngularJS reached end-of-life in January 2022, yet large AngularJS codebases are still quietly running payroll, logistics, and – in the case of the platform I migrated at Sharebite – corporate meal benefits for some of the biggest teams in the US. This is the guide I'd hand to a team about to start the same journey: what actually worked on a production system past 100k lines, with feature delivery running the whole time.

First decision: rewrite, hybrid, or strangler?

There are three honest options, and the right one depends on variables that have nothing to do with code:

  • Big-bang rewrite. Only sane when the app is small (weeks, not months, of rebuild) or the business can tolerate a feature freeze. For a large production app, a multi-quarter freeze is usually a non-starter – and the rewrite always takes longer than the estimate.
  • Hybrid with ngUpgrade. Run AngularJS and Angular in the same page, migrate component by component. Powerful, but you pay a permanent tax: two frameworks in the bundle, two change-detection systems to reason about, and a class of bugs that only exists at the boundary.
  • Strangler by route. Keep the old app intact; build new screens in a new Angular app; move routes over one at a time behind the same navigation shell. Coarser-grained than ngUpgrade, but each migrated screen is fully modern – no boundary layer to debug.

We used the strangler approach for the main platform and eventually rebuilt two Angular apps from scratch with modern architecture and lazy loading. My rule of thumb: ngUpgrade when your screens are deeply interwoven; strangler when your app decomposes into routeable areas – and most business apps do.

Before you migrate a single component

The migration itself is the second half of the project. The first half is making the old code migratable:

  1. Kill $scope first. Convert AngularJS controllers to controllerAs syntax and components (.component()) inside AngularJS. Every component you modernize this way becomes a near-mechanical port later.
  2. Move logic out of the framework. Business logic that lives in plain TypeScript services doesn't care which framework calls it. Every line you move out of directives and controllers is a line you migrate for free.
  3. Introduce TypeScript before Angular. AngularJS runs fine under TypeScript. Getting types in early surfaces the implicit contracts you'll otherwise discover as production bugs mid-migration.
  4. Write characterization tests on the flows that pay the bills. Auth, checkout, data integrity. You don't need coverage everywhere – you need it where a regression costs money. We leaned on E2E (Cypress) precisely because E2E tests survive a framework swap; unit tests written against AngularJS internals don't.

Sequencing that keeps the business calm

The pattern that made this survivable: migrate in vertical slices, ship every slice.

  • Pick the least-coupled routeable area first – not the most important one. Your team's first slice is a learning exercise; don't learn on checkout.
  • Establish the "golden path" on slice one: folder structure, state management, lazy-loading boundaries, testing conventions. Every following slice copies it. This is where having someone who's done it before compresses months into weeks – the golden path is 80% of your migration velocity.
  • Keep a shared design system early. If old and new screens look different, users file bugs about the migration instead of not noticing it – the best migration is one users never see.
  • Report progress in business terms: "screens migrated / screens total," not story points. Stakeholder patience is a finite resource; spend it deliberately.

The traps, so you can step around them

  • The long tail of weird. The first 80% of screens migrate on rails. The last 20% hide the decade of accumulated hacks – the jQuery plugin wrapped in a directive, the $rootScope event bus, the directive that patches another directive. Budget the last 20% of screens as 50% of the timeline.
  • Two routers fighting. In hybrid setups, AngularJS's router and Angular's router both want to own the URL. Decide the owner explicitly and make the other one a guest.
  • Bundle bloat during transition. You're shipping two frameworks. Set a performance budget at the start, lazy-load aggressively, and treat the day AngularJS leaves the bundle as a milestone with a measurable payoff – ours came with a visible drop in load time.
  • Team split. If one squad does "the migration" while others do "features," the feature squads keep adding AngularJS code behind you. Everyone migrates; new code in the old framework is a lint error, not a code-review debate.

When it's done

The end state is more than "no AngularJS in package.json." Done means: one framework in the bundle, lazy-loaded route boundaries, a test suite the team trusts, and – the part I care most about – an architecture the next engineer can read without an archaeology degree.

I've done this rescue more than once, and the pattern transfers: the same playbook works for monolith-to-modular and for "the agency left and nobody understands the frontend." If you're staring at a legacy codebase and a roadmap that won't wait, that's what my web application development and architecture audit engagements are for – or email sinclar96@gmail.com and describe what you're up against.

Building something in this stack?

Get in touch