Decomposition proposal: twelve microservices, nine engineers, one ledger
A nine-person engineering team maintains a 380,000-line Django monolith serving 25,000 requests per minute at peak. The single PostgreSQL database is 1.2 TB, and its largest table holds 900 million rows. Deploys take 40 minutes, mostly a sequential migration step, and the team releases twice a week. p95 latency on the two slowest endpoints is 1.8 s against a 1 s target; both endpoints run a reporting query that scans the large table. The CTO proposes splitting the system into 12 microservices over the next six months, citing 'scalability and velocity', ahead of a funding round. A straw poll of commit history shows most changes touch three or four of the proposed services at once. There is no platform team, no service mesh, and no distributed tracing, and two intended product teams own the domain.
1. What evidence would justify decomposition, and what does the existing evidence actually support?
Read the worked answer
The stated driver is a financing event, which is not a technical requirement, so the first job is to test the proposal against measured pain rather than ambition.
Gather four kinds of evidence. (1) Change coupling: analyse twelve months of commits and incident links to find which modules genuinely change together. If most changes touch three or four of the twelve proposed services, the proposed boundaries cut across the real seams and every feature becomes a cross-service release. (2) Deploy time attribution: 40 minutes is dominated by a sequential migration step, not by the monolith per se. Splitting into twelve services multiplies migrations, so this gets worse, not better. (3) Latency attribution: both slow endpoints run a full-table reporting query. That is a query and indexing problem; the fix is a read model or a materialised aggregate, not a service boundary. (4) Operational capacity: nine engineers with no platform team, no tracing, and no mesh means each service adds CI, versioning, on-call, and observability surface. Twelve services is roughly 0.75 engineers per service.
What the evidence supports is modularisation inside the monolith plus extraction of the reporting path. Service count is not a scalability metric.
2. Propose a decomposition and consistency strategy that fits the team and the ledger requirements.
Read the worked answer
Work from the inside out. First enforce module boundaries within the monolith: explicit interfaces, no cross-module table access, and ownership aligned to the two product teams so each owns a set of modules. This is reversible and costs weeks, not quarters.
Then extract only what has an independent scaling or failure profile. The reporting queries are the obvious candidate: move them to a read replica or a derived read model updated asynchronously, which removes the 1.8 s scans from the transactional path without touching payment correctness.
For the ledger, keep strong consistency where the invariant lives. A single database transaction is the cheapest correct mechanism for money movement; a saga introduces compensating actions and observable intermediate states, which for a ledger means you now need reversal handling and reconciliation. If the ledger must eventually split, use the transactional outbox so the event publishes atomically with the commit, make every consumer idempotent on a dedupe key, and specify each compensation explicitly. Do not split the ledger first.
A 1.2 TB Postgres with 900 million rows is not a scaling problem until you have measured a limit being hit. Partition the large table before you distribute the system.
3. How would you define and measure success, and what migration costs must be budgeted?
Read the worked answer
Define the metrics before the work starts, so the programme cannot declare victory on service count. Use outcome measures: p95 and p99 latency on the critical endpoints, deploy lead time, change failure rate, mean time to restore, and the number of cross-team coordination events required per release. If the real goal is release independence, measure how often a team ships without coordinating with the other, and set a review at 90 days with a decision to continue, pause, or reverse.
Budget honestly for the parts that sink these programmes. Dual-running means two topologies, two sets of dashboards, and two on-call rotations during the transition. Data backfill needs a verified reconciliation, not a row count. Each extraction needs a rollback path to the monolith that stays viable until the new path has run a full business cycle, including month-end if there is one.
Finally, state the alternative cost. If modularising in place plus a read model delivers most of the latency and velocity benefit in one quarter with two engineers, the funding-round narrative should not be allowed to overwrite that comparison. Write the comparison down; it is the artefact you will want in six months.