This is the second article in a series about migrating a production healthcare platform from AngularJS to Next.js. The first article covered how we got here — 10 years of technical debt, ecosystem decay, and a failed AI migration attempt. This one is about the approach that actually works.
Why Big-Bang Rewrites Were Never an Option
When you run a healthcare SaaS platform used by clinical staff daily, you can't just flip a switch and hope for the best. You can't take the system down for a migration weekend. You can't risk broken workflows for users who are tracking quality events in hospitals. And you definitely can't pause feature development for months while you rewrite everything.
We knew this from experience. Our designer attempted a Metronic UI framework upgrade twice over the years. Both times followed the same pattern: months of isolated work, then an attempt to merge against a codebase that had moved on without him. The first time, weeks of merge conflicts. The second time — years later — he couldn't even get the project to start. Both attempts were abandoned entirely.
These weren't failures of effort or skill. They were proof that any migration approach requiring a "stop the world" phase is fundamentally broken for a living product with active development.
The Inspiration
The idea came from an unexpected place. I remembered how PrivatBank — one of the largest Ukrainian banks — migrated their web interface sometime in the 2010s. When you logged in, some pages would open in the old design, others in the new. The most common user flows were on the new UI first. Over time, more pages switched over until the old interface simply disappeared.
It took me a couple of months to fully articulate this concept to management. The abstract idea — "we'll run both frontends in parallel and switch pages gradually" — made sense in theory, but it wasn't clicking. Would users see two different apps? How would authentication work? How would we know which pages are ready?
Once we actually built a working prototype and deployed it — management immediately saw how powerful it was. Migration progress became visible and tangible. Not some abstract "we're working on it" buried in a backlog, but real screens that real users could interact with, with instant rollback to the old UI if anything was wrong.
We called it simply "the switch."
Why Our Backend Made This Possible
Before diving into the switch implementation, it's worth explaining why swapping the frontend was feasible at all: our backend was effectively frontend-agnostic from day one.
Over the years, several clients asked for direct API access to build their own integrations. I provided them with API structure documentation and even sample JavaScript snippets with plain AJAX calls — how to authenticate, how to pull data, how to submit records. They built their own tools on top of our backend, completely independent of our UI.
This meant our backend wasn't coupled to AngularJS in any meaningful way. It was a standalone API that happened to have an AngularJS consumer. Adding a Next.js consumer was architecturally no different from adding another client's custom integration. The frontend is just one of potentially many consumers.
On top of that, we had enforced backend-first security from the very beginning — every API call validates access levels server-side, regardless of what the frontend claims. This decision, made in 2015, turned out to be one of the most consequential of the entire project. When we swapped the frontend, the security model didn't need to change at all.
The Architecture
Modular nginx Configuration
The foundation was already in place thanks to our sysadmin's approach to nginx configuration. Instead of monolithic config files, he had structured everything modularly: the main nginx.conf includes all files from conf.d/, where each domain gets its own .conf file. Over 10 years of domain changes, mirror setups, and SSL certificates, this modularity proved invaluable — a programmer's approach to server configuration.
For the switch, we needed two server blocks: one for the old frontend, one for the new. Both proxying /backend/ to the same application server. The critical difference: the old frontend serves static files from a directory (classic AngularJS), while the new one proxies to a Next.js process on port 3000.
Here's the simplified structure of the new frontend's server block:
server {
listen 443 ssl http2;
server_name new.app.example.com;
# The switch configuration — a single JSON file
# that both frontends consume
location /frontend-redirect-config.json
add_header Access-Control-Allow-Origin "*";
default_type application/json;
alias /srv/nextjs/config/;
# Backend API — same upstream as the old frontend
location /backend/ {
proxy_pass http://localhost:18640/;
# WebSocket support
proxy_http_ver
Tags:
#0
Want to run a more efficient business?
Mewayz gives you CRM, HR, Accounting, Projects & eCommerce — all in one workspace. 14-day free trial, no credit card needed.
Try Mewayz Free →