Context
I led the frontend platform for the Norwegian National Archive (Asta) at Cefalo as Senior Staff Software Engineer, with migration-lead responsibility across five production frontend repositories: the archive management system, the admin app, the archive storage system, the public search app, and the requisition app. Four of these were Angular applications; the requisition app was built in React and was brought up to the latest React release as part of the same modernization effort.
The platform serves a production system used by more than 10,000 archivists, plus members of the public, so the migration had to happen without disrupting a system that was actively in use.
Problem & constraints
Two pressures made the migration necessary. Staying on an unsupported Angular version meant losing security patches, and the older version was increasingly blocking library upgrades and slowing down feature delivery.
The complicating factor was timing: the team had already been rebalanced from 10 engineers to 5 before the migration started. I had to plan and execute the migration with the leaner team from the outset, not adjust for it partway through. Feature demand from the business didn't pause for the migration either — the five repositories kept shipping new work while the upgrade was underway.
Options considered
One repo at a time, strictly sequential
Rejected
Fully migrate and regression-test one repository before starting the next. This was the safest option in isolation, but with five repositories, a five-person team, and feature demand that didn't stop, doing them one after another end-to-end would have made the total timeline too slow.
All five repositories fully in parallel
Rejected
Migrating all five at once would have minimized calendar time, but it concentrated too much simultaneous risk on a five-person team that also had to keep delivering features. There was no precedent yet for how the upgrade path would behave across the codebases, so running all five blind at the same time was too risky.
Pilot on the smallest repo, then overlap the rest
Chosen
Migrate the admin app — the smallest of the five — first, sequentially version by version with full regression, to prove the migration pattern. Once proven, apply that pattern to the remaining four repositories with overlapping timelines. This resolved the riskiest unknowns on the smallest, lowest-stakes codebase before applying them everywhere else.
Decision & architecture
The admin app went first as the pilot. I upgraded it version by version — v14 through v21, not a single jump — with full regression testing at the end. Doing this on the smallest repo first meant I could work through every breaking change on a codebase small enough to fully regress before touching anything else.
The single biggest technical call was Angular Material: the breaking changes introduced after v14, from the shift to MDC-based components, would have cost more engineering time than the rest of the framework upgrade combined — so we deliberately kept Material on v14 and deferred the MDC migration rather than let it swallow the timeline. With that decided on the pilot, the remaining four repositories were migrated with overlapping timelines rather than sequentially, since the pattern was already de-risked.
Alongside the version upgrade, I standardized the engineering setup across all five repositories: the same lint and test configuration, per-repo CI pipelines, Playwright end-to-end suites, PR checks, SonarQube checks, and SBOM checks in CI.
Outcome
All four Angular repositories were migrated from v14 to v21, and the requisition app was brought up to the latest React release as part of the same effort. The whole migration — pilot plus the four overlapping repos — took about two months in total, delivered by half the original team while feature work on the platform continued.
The standardization work outlasted the migration itself: consistent lint, test, Playwright, SonarQube, and SBOM checks across all five repositories are the lasting artifact of this project, not just the version bump. The trickiest call of the whole effort wasn't the framework upgrade — it was Angular Material, where the post-v14 MDC breaking changes were expensive enough that pinning Material to v14 and deferring that migration was the right trade.