One of South Africa's largest banks · programme review

From microservices to a leaner monolith

A high-profile banking programme was paying a distributed-systems tax it had no operational need for. Consolidating the architecture took more than 40% out of delivery cost.

40%+
Delivery cost removed on the banking programme
90%
Peak saving on comparable initiatives since
1
Deployable, one on-call surface, same domain boundaries

Problem

The programme had been specified as a set of microservices — the default assumption for a project of its profile. In practice the traffic profile did not require independent scaling, no two components needed different release cadences, and the team was small enough that every service was owned by the same people.

What the shape did produce was cost. Each service carried its own pipeline, environments, secrets, observability and integration tests. A change touching two services cost several times a change touching one. Estimates were high because the work genuinely was high, and the client was being asked to fund complexity that bought them nothing.

Making the case

Arguing this on engineering aesthetics would have failed. I built the case in the client's terms: cost per change, environments to maintain, time to onboard an engineer, and the number of independent failure points between a customer action and a completed transaction.

I was explicit about what consolidation gives up — independent scaling, blast-radius isolation, per-service technology choice — and showed that none of those were requirements this programme actually had, with the criteria under which we would reverse the decision. That is what made it a decision the client's own architects could sign off on rather than a contractor's preference.

Before / after

Before
Service per domain — own pipeline, environments, secrets, dashboards
Cross-service contracts and versioning for every shared change
Distributed tracing needed to answer basic questions
Eventual consistency handled with compensating logic
After
One deployable, modules per domain — the same boundaries, in code
Contract changes are refactors the compiler and tests catch
One pipeline, one runtime, one place to look when something breaks
Transactional consistency where money is involved

Execution

Consolidation was incremental, not a rewrite. Domain boundaries were preserved as modules with explicit interfaces, so the seams survive if a component ever genuinely needs to be extracted. Services moved in behind their existing contracts one at a time, each with its tests carried across, so the programme kept delivering client-visible work throughout.

The discipline that keeps this from decaying is boundary enforcement in review: modules talk through their interfaces, not each other's internals. A monolith with no internal structure is worse than the microservices it replaced — that is the failure mode this approach has to earn its way out of.

Outcome