03 · Performance · 2026 · Work

Sync to Async Migration

Migrating a live production backend from synchronous to asynchronous, one controller at a time, without a rewrite and without taking the product down.

BEFORE · SYNCHRONOUSCONTROLLERSERVICEDATABASETHREAD BLOCKED FOR THE WHOLE REQUESTAFTER · AWAIT END TO ENDCONTROLLERSERVICEAWAITING I/O · THREAD FREEANOTHER REQUEST USES THE THREADMIGRATED CONTROLLER BY CONTROLLER · NO REWRITE

The problem

All of the backend was written as a synchronous flow. That was fine until the product scaled - as user count and data grew, threads sat blocked waiting on work that could have been awaited, request throughput suffered, and scaling out cost more than it should have.

What was built

A staged migration across controllers, services, and the files underneath them rather than a rewrite. Many individual changes were low risk, since we had already implemented locks on secure data. Converting the whole call chain end to end was the real work.

◆The API layer and its services are fully migrated to async.
◆Migrated incrementally: controllers prioritized as they came up in other work, with their services converted alongside them.
◆A handful of backend services are still on the synchronous path and are being converted as they are touched.
◆Result: higher throughput and less blocking under load across every migrated part of the backend.

What I'd do differently

I would design the system to be async from the ground up rather than retrofit it. Failing that, I would migrate all of the services first instead of converting them piecemeal alongside their controllers.