In this article
AngularJS, the original 1.x framework, reached end of life at the end of 2021. Business-critical applications built on it are still running everywhere, and each month adds risk: no security patches, a shrinking pool of developers who know it, and browsers moving away from the assumptions it was built on. If one of those applications is yours, you have probably been given two options. Rewrite it from scratch, or leave it alone. Neither is the right answer.
We have written Angular since the AngularJS days, and most of our Angular work now is migration. This is the incremental plan we use. The application keeps serving users throughout, and the business never depends on a single cutover weekend.
Why a full rewrite is the riskiest option
A rewrite sounds clean: build the new application beside the old one, switch over when it is ready. "Ready" takes longer than anyone estimates, the old application keeps changing while the new one is built, and switch-over day arrives with a feature gap, a data migration and users who have to relearn everything at once. Many rewrites are abandoned halfway, leaving two half-maintained codebases instead of one old one.
Leaving the application alone is also a decision, and its cost grows. Every month on an unsupported framework is a month of unpatched vulnerabilities, and the engineers who remember AngularJS get harder to hire.
The incremental route
Modern Angular ships an upgrade bridge that lets AngularJS and Angular run inside the same application at the same time. Components and services from either side can be used by the other. That capability turns the migration from a cliff into a staircase. Modules move across one at a time, the application works at every step, and the business approves the work in stages rather than signing off one large number.
Step 1: audit before estimating
We start every migration with an audit of the legacy codebase, and we do not estimate before it. The audit answers these questions.
- How many modules, components, directives and services exist, and how tightly are they coupled?
- Which third-party AngularJS libraries are in use, and does each have a modern equivalent?
- How much of the code is tested? Untested code migrates slowly because every move needs manual verification.
- Where does the business logic live? Logic in controllers and templates is harder to move than logic in services.
- What does the build look like? Many AngularJS applications predate module bundlers, and the build is the first thing to modernise.
The output is a module-by-module map with a difficulty rating for each, and from that a staged estimate. You see the whole journey before committing to the first step.
Step 2: prepare the legacy code
Before any modern Angular is introduced, the AngularJS code is moved toward the shape Angular expects. This is low-risk refactoring inside the existing framework.
- Convert controllers and templates to component directives, which map almost one-to-one onto Angular components.
- Move business logic out of controllers into services.
- Introduce TypeScript to the legacy code, loosely at first, so types accumulate before the move.
- Replace the build with a modern bundler that can serve both frameworks.
- Add tests around the modules that will move first.
Each of these changes ships to production on its own. Users notice nothing, and the application is already in better shape.
Step 3: start the hybrid
The Angular upgrade module starts a modern Angular application that hosts the AngularJS one inside it. From this point both frameworks are live. The application looks the same to users, and the team can write new features in modern Angular immediately, before any old module has moved. That early win is important. The migration stops being pure cost and starts delivering modern code from the first sprint.
Step 4: move modules, leaves first
Modules move in dependency order, starting with the ones nothing else depends on. Each is rewritten in modern Angular, exposed to the legacy side through the bridge where needed, and verified against its tests. Then the next one. Shared services and the router usually move last, because everything depends on them.
The rhythm is two-week sprints, each ending with a deployment you can click through. A typical sprint moves a handful of modules. The application is in production throughout, running a mix of old and new code that users never see.
Step 5: remove the bridge
When the last AngularJS module is gone, the upgrade bridge and the legacy framework are removed and the build simplifies. What remains is a modern Angular application with standalone components, signals, strict TypeScript and a twice-yearly release cadence that keeps it current. The team that migrated it knows every corner of it, which is the best possible handover.
Time and budget
Migrations are scoped from the audit rather than from a formula, but two patterns hold. The preparation step is a larger share of the work than clients expect and the module moves are smaller, because well-prepared modules move fast. And the total is usually comparable to a rewrite while the risk is far lower, because at no point is there a day on which everything must work for the first time.
A few things slow migrations down: untested code, business logic in templates, abandoned third-party libraries with no modern equivalent, and a team that keeps adding features to the legacy side during the migration. The last one is a management decision. Once the hybrid is live, new features go in modern Angular.
Is this the right moment?
If your AngularJS application still earns its keep and will for years, yes, and it was also yes in 2022. If it is due for replacement anyway, a migration may not be worth it, and we will say so after the audit. Either way, the audit is where to start. Our Angular development page covers both new builds and migrations, and a codebase audit is free to scope.