The safest way to replace an internal system everyone relies on is to ship the replacement read-only first. A read-only app is useful on day one — it can be a faster, clearer view of the same data — and it is structurally incapable of corrupting the system it will eventually replace. Where it disagrees with the incumbent, the disagreement surfaces as a question to investigate, not as damage to undo.
The alternative has a name — the big-bang cutover — and a well-documented worst case. In April 2018, TSB Bank moved roughly five million customer accounts to a new platform in a single weekend. The independent investigation that followed found the platform was not ready, and the regulatory and compensation bills ran into the tens of millions of pounds. This post treats TSB as one dated, thoroughly investigated example of a failure mode — not as evidence of how often migrations fail. No source in our research supports an industry-wide failure rate, so we make no claim about one.
What follows is a rollout pattern: why the established migration literature already points this way, how to enforce read-only at the database rather than trusting the application, a graduation ladder each surface must climb before it earns write access, and the counter-intuitive case for surfaces that should stay read-only forever.
- 01Ship the replacement read-only first.A read-only app is useful on day one and cannot corrupt the incumbent. Disagreements between the two systems surface as questions to investigate rather than as damage to repair.
- 02Big-bang cutover is the documented failure mode.TSB's April 2018 single-weekend migration of roughly five million accounts led to locked-out customers, a reported 70x fraud spike, and a press-reported £48.65M combined regulatory fine. One example, not a base rate — but a dated, independently investigated one.
- 03Enforce read-only at the database, not in the app.An application that chooses not to write is one bug away from writing. A database role granted only SELECT cannot write. Postgres 14's pg_read_all_data closes the new-table gap the naive recipe leaves open.
- 04Write access is earned per surface, one test at a time.Each surface graduates on its own evidence: a reconciliation test matched to the stakes of the data, a clean read-only period, and an explicit decision. Graduating everything in one event recreates the big-bang risk profile.
- 05Some surfaces should never graduate.Where the incumbent — a spreadsheet, a CRM screen — is genuinely the better place for a human to edit, the new app should stay a better view forever. Full replacement is an option, not the goal.
01 — The Anti-PatternWhat a big-bang cutover costs when it goes wrong.
On the evening of Sunday 22 April 2018, TSB migrated roughly five million customer accounts in one weekend from Lloyds Banking Group's platform to Proteo4UK, a new platform built by Sabis, the IT arm of TSB's parent Sabadell. In the days that followed, Computer Weekly's reporting on the independent investigation records that customers were locked out of accounts, some saw money missing, and some could see other customers' accounts and transactions. Internet and mobile banking were, per the report, “unstable and almost unusable.”
The investigation — commissioned from law firm Slaughter and May, a 262-page report that Computer Weekly says is reported to have cost around £25 million — concluded: “The platform was not ready to support TSB's full customer base and Sabis was not ready to operate the platform.” Its finding on the method is the part every internal tools team should read twice: “TSB did not give sufficient consideration to whether a largely single-event migration was the right choice, what the risks would be, or how those risks would be mitigated.” The report acknowledges the appeal honestly — single-event migration is the fastest, cheapest and least complex way to proceed — and then insists that a bank choosing it must understand the risks and robustly test the platform before going live.
Combined FCA + PRA penalty
Levied in December 2022 for operational risk management and governance failures related to the 2018 migration. Press-reported figure; TSB's CEO resigned and the CIO was personally fined under the UK's senior-managers regime.
Compensation paid to customers
Separate from the regulatory fine, per press coverage of the regulatory action. The direct bill sat on top of the reputational cost of weeks of degraded service.
Peak fraud attacks vs normal
Fraud attacks against TSB customers peaked at seventy times normal levels in the weeks after the migration, per the Slaughter and May report as cited by Computer Weekly. Attackers move fastest when a system is visibly wounded.
Two details matter for anyone tempted to file this under “banks are different.” First, TSB did test. Panorama Consulting's post-mortem notes TSB admitted that while it did perform pilots, they weren't carried out at a sufficient scale — the testing existed, but its scale did not match the scale of the live event. Second, the single-event choice was reportedly made without seriously weighing alternatives, and the board did not adequately probe why the platform remained unstable despite roughly eighteen months of delay and workstreams reported as running around seven months behind schedule. The failure was not a lack of effort. It was concentrating all of the risk into one irreversible moment — a moment regulators later priced at £48.65 million.
To be precise about what this example does and does not prove: it proves the failure mode exists, is expensive, and has been independently dissected. It does not tell you what share of cutovers fail — no dated, published source we reviewed measures that, and this post will not pretend to.
02 — The PrecedentThe migration literature already agrees: replace incrementally.
The read-only-first rollout is a specialization of a pattern the industry has endorsed for years. Martin Fowler named it the strangler fig application: grow the new system around the old one, moving behavior across gradually rather than in one event. Fowler is also clear that the pattern is not just a technical trick — it needs four things to work: understand the outcomes you want, decide how to break the problem into smaller parts, deliver those parts successfully, and change the organization's culture so the incremental approach sticks.
“it begins with small additions, often new features, that are built on top of, yet separate to the legacy code base. As we do this we move bits of behavior from the legacy system into the new code base.”— Martin Fowler, martinfowler.com, on the strangler fig pattern
Both major cloud vendors document the same pattern independently. Microsoft's Azure Architecture Center defines it as incrementally migrating a legacy system by replacing specific pieces of functionality until the new system comprises all of the old one's features — and, tellingly, its worked example is a database, not a UI: extract domain tables into an isolated domain database, load them via ETL, keep them synchronized with change-data-capture, and only after validation does the new database become the system of record. AWS's Prescriptive Guidance — continuously maintained documentation rather than a dated page — frames the risk driver identically: a big-bang migration approach is risky because of the size and complexity of the monolith, and the pattern exists so users are minimally impacted while the business keeps getting new features.
Transform
Build the modernized component as a separate system next to the incumbent. Nothing about the old system changes yet — which is exactly why the new one can ship early.
Coexist
Intercept calls at the monolith's perimeter — typically an HTTP proxy — so old and new serve traffic together. This is the phase a read-only-first rollout deliberately extends: coexistence is where trust is built.
Eliminate
Retire old functionality once traffic is fully redirected. Microsoft adds the crucial cost note: rollback is cheap while legacy objects and sync still exist, and removing them should be a deliberate final step for each domain — because after that, rolling back gets significantly harder and riskier.
Microsoft is also candid about when the pattern is wrong: when requests to the back-end can't be intercepted, when you can't modify the legacy system to redirect calls, when the system is small enough that wholesale replacement is simple, or when you must fully decommission the old solution quickly. Those caveats are worth taking seriously — an internal ops app that can't read the incumbent's data at all has no read-only rollout to run. But for the common case — a new operations app growing up next to a CRM, an ERP, or a long-lived spreadsheet that people depend on every day — the read-only-first rollout simply takes that pattern one step further than the migration guides do.
03 — The PatternRead-only first: shipping the view before the pen.
Here is the pattern in full. Build the replacement app against a synchronized copy or a direct read path into the incumbent's data. Ship it to real users on day one — as a read-only surface. Let people live in it: dashboards, search, record views, reports. Meanwhile, log every place where what the new app computes or displays disagrees with what the incumbent shows. Each disagreement is a free correctness test that cost nobody any damage. Only when a specific surface has run clean does it become a candidate to accept writes — and then only that surface.
Which copy of the data the app should read, and how stale a read can safely be, is a real question — it is just a different question, covered in our guide to read paths and stale data; this post assumes reads are solved and asks when the application earns the right to write.
It helps to see what the pattern is not. It is not shadow deployment. Portainer's definition of shadow deployment is a new version running in parallel that receives a copy of live traffic and processes it without ever returning a response to the user — the comparison happens off the request path, in a metrics pipeline or reconstructed from logs. A read-only ops app is close to the inverse: it does answer real users, it just answers only with reads. That inversion quietly avoids most of the failure modes Portainer catalogs for shadow mode: no doubled infrastructure to mirror traffic into, no “side effect leakage” where a shadow call accidentally hits a real payment or notification system, no timestamp and non-determinism noise producing false diffs, and no separate shadow workload inheriting production's regulatory requirements. A read-only app has no side effects to leak by construction.
Big-bang cutover
Fastest and cheapest when it works — the TSB report itself concedes this. But it concentrates every risk into one irreversible weekend, and testing at less than live scale, as TSB's pilots were, leaves the biggest risks unexercised until go-live.
Parallel run + reconciliation
Both systems process real work; outputs are reconciled on a cadence. Strong evidence, but double data entry or complex sync while both systems accept writes. Standard practice in payroll-class migrations.
Shadow deployment
New version silently processes copied traffic. Powerful for validating a like-for-like service swap, but costly to run and irrelevant to users — nobody benefits until cutover, and side-effect leakage is a named hazard.
Read-only first
The new app serves real users immediately, with zero write paths. Disagreements surface as questions. Each surface earns writes separately on its own evidence — and some never do. Slowest to full replacement; safest per step; useful from day one.
The deeper property is organizational, and it is the part the migration literature mostly skips: a read-only launch converts the scariest question — “do we trust the new system?” — from an argument into a measurement. Nobody has to assert trust in a meeting. The disagreement log either shrinks to zero on a surface or it doesn't, and everyone can see which.
04 — EnforcementRead-only must be a property of the credential, not the codebase.
“Our app doesn't write” is a policy. A database role that cannot write is a guarantee. An application that merely chooses not to issue writes is one ORM default, one migration script, one copy-pasted admin endpoint away from writing anyway — so the read-only phase should be enforced where the application cannot override it: in the database's permission system.
In Postgres, the good news is the starting posture. Crunchy Data's walkthrough puts it plainly: “Postgres is not permissive by default (whew!) so we need to explicitly GRANT some of the privileges.” A genuinely read-only role is built from explicit GRANT SELECT ... TO app_readonly statements against PostgreSQL's GRANT privilege vocabulary — SELECT and nothing else from the write-capable set (INSERT, UPDATE, DELETE, TRUNCATE, and the rest).
The same walkthrough documents the trap in the naive recipe: default privileges only apply to objects created by the role that set them, so if a different user creates a new table, a read-only role granted access at setup time won't be able to read it — and, more dangerously in the other direction, an incompletely-scoped role can quietly drift from what you believed you granted. Postgres 14 added the built-in pg_read_all_data role precisely to close that gap, granting read access to objects created by any user rather than requiring per-creator ALTER DEFAULT PRIVILEGES statements. If you are on 14 or later, use it for the rollout role; if not, re-apply default privileges per creating role and audit.
05 — Earning WritesThe graduation ladder: one test per surface, one surface at a time.
How clean does a surface's read-only period have to be before it earns a write path? The honest answer is that the tolerance depends on the stakes of the data, and the industry's practitioner guidance reflects that. Data-migration reconciliation guidance commonly sets tolerance at zero for financial and transactional records — reconciled field by field — while lower-priority data domains are checked with aggregate and count-based methods, with non-zero tolerances commonly cited around 0.01%. Treat those figures as practitioner convention, not a regulatory standard; no named standards body publishes a universal number.
Duration guidance from the same practitioner literature is equally unstandardized but directionally consistent: payroll-class systems are commonly run in parallel for two to three full processing cycles before cutover, and financial-servicing implementations are described as using daily transaction matching over roughly fifteen days to three months to build confidence — with the recurring advice that it is better to extend a parallel run than to discover major issues after full cutover. The read-only-first pattern turns that advice from a company-sized decision into a surface-sized one.
The ladder below is an illustrative scenario for a hypothetical operations app — the surface names and numbers are invented for this post, not drawn from TSB or any real client. The shape, not the specific cells, is the point: three write-eligible surfaces entered the read-only period, and exactly one earned a write path in the first month.
| Surface | Day-one access | Test to earn write | Read-only period found | Decision |
|---|---|---|---|---|
| Illustrative scenario — invented for this post, not client data | ||||
| Dashboard summary view | Read-only | None planned — display-only by design | Two aggregation mismatches, both traced to timezone handling and fixed in the view | Stays read-only indefinitely |
| Customer contact detail | Read-only | Field-level match with the incumbent across one full review cycle, zero unexplained diffs | Clean from week two onward after one mapping fix | Graduated to write in month one — the only surface that did |
| Status-change action | Read-only (shows current status) | Proposed changes queued for human approval until a full cycle passes with no rejected proposals | Three proposals rejected in week one — rules encoded from a stale process doc | Not graduated — still proposing, humans still approving |
| Financial figure override | Read-only | Zero-tolerance field-level reconciliation plus sign-off from the figure's owner | One rounding disagreement, still under review | Not graduated — zero tolerance means zero |
| Bulk record delete | Not built | Deliberately none — deletion stays in the incumbent, behind its existing controls | n/a | Never ships as a write surface |
Three design choices in the ladder do the real work. First, the test is matched to the stakes: the financial surface gets the zero-tolerance, field-level treatment the reconciliation literature reserves for transactional records; the contact-detail surface needs a clean cycle, not perfection from day zero. Second, there is an intermediate rung between reading and writing — proposing. A surface can draft the write and queue it for a human to approve, which produces exactly the evidence a graduation decision needs. That rung is also where this pattern touches agent governance: approval gates govern what an agent is allowed to do; the ladder governs when the application itself is allowed to write. Third, every rung is per-surface. The failure mode the TSB report describes — one decision, one event, all accounts — cannot be reproduced by a process that is structurally incapable of graduating more than one surface per decision.
06 — The Counter-Intuitive CaseSome surfaces should stay read-only forever.
The strangler-fig and shadow-mode literature share an unexamined assumption: that the end state is full replacement. Microsoft's pattern ends with the legacy tables removed; AWS's third step is named “eliminate.” For an internal operations app, we think that assumption deserves to be rejected explicitly. Some surfaces should never graduate to write — not because they failed a test, but because the incumbent is genuinely the better place for a human to make that edit.
A finance controller's spreadsheet with fifteen years of embedded institutional logic; a CRM screen an operations team has used daily for a decade, with its validation rules, its audit trail, and its muscle memory — these are often the right editing surface for the people accountable for the data. What they lack is a good view: fast cross-record search, a clean dashboard, a report a manager can open on a phone. A read-only app that provides the view while the incumbent keeps the pen is not an unfinished migration. It is a finished product with a deliberately drawn boundary — and it is a large share of the value of the CRM automation work we ship: the system of record stays authoritative, and the new surface makes it dramatically more usable.
The permanent-read-only decision also caps risk in a way no test can. A surface with no write path has no write bugs, no write permissions to audit, no rollback story to maintain. When the underlying data is high-stakes and the editing workflow in the incumbent works, “a better view, forever” is not a compromise — it is the highest-return, lowest-risk deliverable in the whole program.
07 — ImplicationsWhat this changes about building internal tools.
This pattern slots into decisions you are probably already making. It picks up after the build-vs-buy decision is made in favor of building: read-only-first is how the thing you chose to build gets adopted without betting the operation on it. It is also a different axis from deployment mechanics — a feature-flag rollout controls when code ships; this ladder controls when the application earns the authority to write. A candidate worked example from our own archive: an agent-audited costing engine is precisely the kind of surface that should compute and display for a long time before any figure it produces flows back into the system of record.
Looking forward, we expect the graduation ladder to become a governance artifact in its own right — a living document that answers “what can the new system touch, and why?” the way the database's grant list answers it technically. Teams that adopt it get a second benefit almost for free: the read-only period produces a disagreement log, and that log is the best requirements document the write phase will ever have. If you are sequencing a replacement like this around a CRM, an ERP, or a long-lived spreadsheet, this staged rollout is exactly the kind of program our AI transformation engagements are built around.
08 — ConclusionReplace the system without the cutover.
Useful on day one. Trusted one surface at a time.
The TSB migration is remembered because everything graduated at once: five million accounts, one weekend, one decision. The independent report's conclusion — that the platform was not ready and that the single-event choice was never sufficiently questioned — is a description of concentrated, untested trust. The read-only-first rollout is the opposite shape: distributed, tested trust, built surface by surface, with the incumbent intact underneath the whole time.
The mechanics are unglamorous and that is the point. Enforce read-only in the database role, not the application's good intentions. Let real users live in the read-only app while the disagreement log does the arguing. Graduate one surface per decision, against a reconciliation test matched to the stakes of its data — zero tolerance where money moves. And hold the counter-intuitive line: a surface that stays read-only forever, with the old spreadsheet or CRM screen keeping the pen, is frequently the pattern's best outcome, not its failure.
One example does not make a base rate, and this post has deliberately measured nothing. What the pattern offers is not a statistic but a property: at every step of the rollout, the worst thing the new system can do to you is be wrong on a screen — and a question on a screen is the cheapest form a migration error will ever take.