Architecture improvement is a change to the shape of the system — boundaries, data ownership, runtime topology — not a rename of folders. Do it when the current shape is the reason you cannot ship, scale, or sleep. Do not do it because a blog post used the word “modernize.”
Diagnose before moving boxes
- What force failed: independent deploys, a coupling that causes outages, a data model that cannot express a new product?
- Where is the seam you already have (module, DB schema, HTTP API)? Extract along that seam.
- Write the test or metric that would prove the new shape works (deploy independently, p95, “team B does not need team A’s freeze”).
Moves that usually pay
- Split a module with a different rate of change; give it its own database only when you must.
- Introduce an anti-corruption layer instead of rewriting the core. See migrating to DDD.
- Replace a synchronous chain of four HTTP calls with a consumer + outbox if the user does not need the whole chain in the request.
- Kill a shared “util” library that forces lockstep releases.
Moves that usually do not
- Microservices for a five-person team with one database they still share.
- Rewriting in a new language to “improve architecture.”
- Adding Kubernetes to a single VM app to look like a platform.
Example
Extract the module whose change rate is actually different. Give it a database when the data owner is different, not before.
What breaks it
A five-person team splits into services that still share one database. They paid for network failures and kept the coupling. The module split above is the smaller move.
Pitfalls
- Big-bang rewrite while still taking product work in the old system with no strangler path.
- Architecture review as theater: diagrams nobody can map to repos.
Patterns: Architectural Patterns.