The next change tests the architecture

  1. 01NeedWhat must change
  2. 02DependenciesWhich boundaries are affected
  3. 03DeliveryWho ships and operates it
Compare change cost with the team’s ability to operate the solution.

When I work with analysts and engineering teams, I keep returning to the same point: knowing the names of architecture patterns does not tell you which one to choose. The useful question is how much the next change will cost and who can carry it safely into production.

That gives me two decision variables:

  1. Cost of change: how much code, data, coordination, and validation a new requirement will touch.
  2. Operational maturity: whether the organisation can observe, release, and recover several independently running components.

What “cost of change” actually means

Lines of code are a poor proxy. A one-line schema change can trigger migrations across many consumers, while a large internal refactor may leave every external contract untouched.

I look at questions such as:

  • How many modules and repositories must change?
  • How many teams must coordinate the release?
  • How long do testing and deployment take?
  • Is a data migration required?
  • Can the change be rolled back independently?
  • What is the incident blast radius?
  • Who owns the result after release?

Architecture becomes expensive through coupling and ambiguous ownership, not size alone.

When a modular monolith is cheaper

A monolith often provides a short path from an idea to a working feature: one deployment unit, local calls and easier shared transactions within one database. It need not run as exactly one process: replicas and background workers are possible. Moving session state out of the process simplifies horizontal scaling, while shared databases and other dependencies still need capacity testing. “Monolith” does not mean “small” or “slow.”

Trouble begins when modules reach into one another’s internals, the shared database becomes an undocumented API, and every change requires a full regression cycle. The first response should often be better module boundaries, not an immediate network boundary.

I would usually keep a modular monolith when:

  • the domain model is still changing substantially;
  • one team, or a few tightly coupled teams, owns the product;
  • a shared release is not a persistent bottleneck;
  • most of the workload scales in the same way;
  • availability, security, and regulatory needs do not demand separate boundaries;
  • platform engineering and on-call capability are still limited.

A well-structured monolith is not an embarrassing intermediate state. It can be the right long-term architecture.

What microservices buy

The useful promise of microservices is not generic “scalability.” It is selective independence:

  • a team can release a component without coordinating a full-system deployment;
  • a hot workload can scale separately;
  • a component can have its own SLO and access policy;
  • a failure can be contained if dependencies are designed to degrade safely;
  • one owner controls the domain lifecycle and its data.

You pay for that independence with networks, timeouts, retries, contract evolution, distributed diagnostics, and harder data consistency. If the organisation cannot manage those costs, microservices increase the cost of change rather than reduce it.

Polyglot development is a cost as well as an option. Every additional language needs a build path, security updates, observability, operational support, and engineers who can maintain it years later.

Signals that a service boundary may pay off

I look for a current constraint rather than a forecast about future growth:

  1. The domain boundary is stable and has a clear data owner.
  2. A team is repeatedly blocked by the shared release cycle.
  3. The workload has a distinct scaling profile.
  4. It needs a separate availability, security, or compliance boundary.
  5. Isolation will reduce the blast radius instead of creating another cascading dependency.

One signal is rarely enough. If a module boundary, background queue, or database replica solves the problem, a new service may be unnecessary.

The operational readiness gate

Before extracting services, I want to see:

  • automated builds, tests, and deployments;
  • metrics, centralised logs, and distributed traces;
  • explicit SLOs, actionable alerts, and an on-call model;
  • contract tests and schema-compatibility rules;
  • secrets and access management;
  • a service owner, runbook, and rollback path;
  • a platform that prevents every team from rebuilding this foundation alone.

Kubernetes does not close this gate by itself. It schedules containers; it does not define domain boundaries or make remote calls reliable.

An incremental migration path

A safe migration is less dramatic than a “microservices transformation”:

  1. Measure where changes and releases are actually slowing down.
  2. Repair boundaries inside the monolith first.
  3. Select one component with a clear benefit from separation.
  4. Define its contract and data ownership.
  5. Build observability, compatibility, and rollback into the move.
  6. Check whether the real cost of change went down.

If it did not, further decomposition should not continue on autopilot.

Measures that matter

I care less about the number of services than about:

  • lead time from an accepted change to production;
  • deployment frequency and failure rate;
  • time to restore service;
  • test and release duration;
  • the share of changes requiring cross-team coordination;
  • incidents caused by integration boundaries.

Architecture is doing its job when business changes move faster without increasing operational risk. Everything else is an implementation choice.


Originally published on my Telegram channel @it_underside.

Yours, DPUPP

Related Articles