Results
| Metric | Value | Source |
|---|---|---|
| Elapsed in-house time before the pod engaged | 12 months, migration in flight, no cutover reached | Client-reported |
| Delivery window once the pod engaged | 90 days, pod start to production cutover | Qognition delivery record |
| Time saved per staff member | 10+ hours per week | Client-reported, post-cutover |
| Production downtime at cutover | Zero — cutover ran behind a dual-write and read switch | Qognition delivery record |
How we measured, and what these numbers are not. The twelve months and the ninety days are not comparable units and we will not present them as a like-for-like comparison. Twelve months is *elapsed calendar time* during which the migration was one of several priorities for a team whose primary job was serving clients. Ninety days is a *dedicated delivery window* for a pod with no other assignment, starting from a codebase, a data model and a domain understanding the in-house team had already built and documented. The pod inherited real work. A meaningful share of the discovery, the schema decisions and the business-rule archaeology was done before we arrived, and the 90-day window was possible partly because of it.
The 10+ hours per week is client-reported, gathered by the firm from its own staff after cutover. It is a self-reported operational figure, not a time-and-motion study.
What we are not claiming. No revenue, margin or billable-hour recovery. Hours returned to staff are not automatically hours billed, and we will not do that arithmetic on a client's behalf.
The question this engagement had to answer
*How does a professional services firm finish a migration that has to happen without stopping the work that pays for it?*
This has nothing to do with capability. The in-house team knew the domain better than any outside firm would — they built the business rules, understood the edge cases, and produced the documentation that made a 90-day window credible. What they did not have was a contiguous block of engineering time. Migration work is the first thing interrupted when a client deadline lands, and interrupted migration work does not accumulate. It decays.
The firm did not need to be told how to do it. It needed a team whose only job was doing it.
What an embedded pod actually is
"Embedded pod" is the thing being sold here, so it should be defined rather than left as a word.
A dedicated team, not staff augmentation. Not contractors filling seats on the client's board. The pod has its own delivery lead and its own standards, and is accountable for an outcome rather than for attendance.
It owns the roadmap for its scope, contractually. Within agreed scope, the pod sets sequencing, makes the technical calls and answers for the ship date. The client owns priorities and the definition of done. This is written into the SOW, not assumed.
Fixed price, locked before code. Not a rate card, not a monthly burn against a hopeful estimate. Scope, price and window are agreed at Blueprint. We do not bill hours — the reasoning is in the Forge Method.
It works alongside the client's engineers, not instead of them. Here the in-house team held domain authority, reviewed the migration mappings and owned the systems the pod integrated with. The pod held the migration.
It hands over. The exit condition is the client's team operating what the pod built, with runbooks, tests and a documented rollback. A pod that becomes permanent has failed at its job.
Why this was hard
The legacy system was live and revenue-critical. Client work ran on it every day of the migration. Nothing could pause.
The data had history. Years of records, accumulated conventions, and fields whose meaning had drifted from their names. Migration is rarely a schema problem. It is a semantics problem.
Cutover risk sits on the firm, not on us. If a CPA network loses access to client records during filing season, the cost is not engineering time. It is client trust and regulatory obligation.
Twelve months of prior work had to be respected, not restarted. Discarding it would have been faster to plan and much slower to deliver.
Architecture: the migration and cutover path
The migration ran as a strangler with dual-write, a read switch and a live rollback path — not a big-bang weekend cutover.
flowchart TB
L1["Legacy platform<br/>live, serving all traffic"] --> EX["Extract + transform<br/>field mapping, semantics resolved<br/>with in-house domain owners"]
EX --> VAL["Validation harness<br/>row counts, checksums,<br/>accounting business-rule assertions"]
VAL --> N1["New platform<br/>backfilled, serving nothing"]
N1 --> DW["Phase 2 — dual write<br/>legacy remains system of record,<br/>new platform shadow-writes"]
DW --> REC["Reconciliation job<br/>diffs legacy vs new daily,<br/>reports drift by module"]
REC --> GATE{"Drift at zero,<br/>held for the agreed period?"}
GATE -- no --> DW
GATE -- yes --> RS["Phase 3 — read switch<br/>per module, per office,<br/>hold period between switches"]
RS --> SOR["Phase 4 — promotion<br/>new platform becomes<br/>system of record"]
SOR --> RSY["Reverse-sync window<br/>legacy kept current"]
RSY --> RB["Rollback: flip reads back to legacy<br/>rehearsed, named decision owner,<br/>stated trigger condition"]Why this shape. Every phase is reversible on its own. Backfill is reversible because the new platform serves nothing. Dual-write is reversible because legacy remains the system of record. The read switch is reversible per module and per office, so a bad switch has a blast radius of one module in one office, not the firm. Only the final promotion is a real commitment, and by then the same data has been served and reconciled under real load.
The rollback plan. For a defined window after promotion, writes synced back to legacy, keeping it current enough to take traffic again. Rollback was a switch flip with a documented decision owner and a stated trigger, rehearsed before promotion. A rollback plan that has never been executed is a document, not a plan.
What we did
1. Blueprint: read what existed before proposing anything. Week one went to the in-house team's work — their schema decisions, their mappings, their notes on the awkward data. That work set the direction. The Blueprint's job was to turn it into a sequenced plan with a committed date, not to have new opinions.
2. Built the validation harness before the migration. Row counts, checksums, and assertions written against the *accounting* meaning of the data, not just its shape: balances that must reconcile, relationships that must hold, records that must not orphan. It ran on every backfill iteration, and it mattered more than anything else built in the engagement, because it turned "the migration looks fine" into a pass or a fail.
3. Resolved semantics with the people who knew. Ambiguous fields went to the in-house domain owners, never to a guess. The pod's job was to be fast; the client's team's job was to be right about the domain. That split is why it worked.
4. Ran dual-write and reconciled daily. Once shadow writes were live, a job diffed both platforms every day and reported drift by module. Drift is the honest readiness signal. Cutover was not scheduled by the calendar. It was scheduled by drift reaching and holding zero.
5. Switched reads incrementally, by module and by office. Smallest surface first, in the office best placed to report problems fast, with a hold period before the next switch. Nothing was switched during a filing crunch.
How we evaluated it
Test coverage approach. Coverage percentage was not the target — migrations fail on data semantics, not branch coverage. Three layers carried the weight. Migration assertions: business-rule checks over migrated data — reconciliation, referential integrity, no orphans, no silently dropped records — run on every backfill. Characterisation tests on legacy behaviour: before changing anything, we pinned existing behaviour, including the parts nobody liked, so any difference on the new platform was a decision rather than a surprise. End-to-end tests on the workflows staff run daily, executed continuously against the new platform throughout dual-write.
The metric that mattered: reconciliation drift. A daily count of records differing between legacy and new, by module. The cutover gate was drift at zero, held for a defined period, with every historical non-zero explained. Not "mostly matching". Explained.
What happens when it is wrong.
| Failure | What happens |
|---|---|
| Backfill mismatch | Validation harness fails the run. Nothing is promoted. |
| Drift appears during dual-write | Legacy remains system of record. Drift is diagnosed before any read switch moves. |
| A module misbehaves after read switch | That module's switch flips back. One module, one office. Others unaffected. |
| Problem found after promotion | Rollback to legacy via the reverse-sync window, on a rehearsed procedure with a named decision owner. |
| Filing-season crunch | No switches scheduled. The domain calendar is a hard constraint in this industry. |
What was hard
The tradeoff. Dual-write plus reconciliation is materially more engineering than a weekend big-bang cutover, and it lengthened the build. We took it because the client's downside is client-record availability during filing periods, and no amount of saved engineering time is worth that risk. Reversibility was worth more than speed, even inside a committed window.
The alternative we rejected. Fixing the parts of the legacy data model that were plainly wrong while migrating. Tempting, and several fields deserved it. We refused, because a migration that also changes semantics has no reliable oracle — when the two platforms disagree you cannot tell whether the migration broke or the redesign worked. Those changes went onto the post-cutover roadmap and were made afterwards against a stable baseline.
What broke. Historical records predating a convention change in the legacy system. They passed structural validation and failed the accounting assertions, which is the right order to find out — but it surfaced late, because our first validation passes ran against recent data. We rebuilt the harness to sample across the full date range rather than the recent tail, and the fix cost real days in the window.
What we would do differently
Sample the oldest data first. Recent data is clean, familiar and reassuring. The oldest records are where conventions changed and where migrations break. We now validate the date extremes before the middle.
Rehearse rollback earlier. We rehearsed before promotion, which was in time. It should have happened at the end of dual-write, when there was slack to improve the procedure rather than only to verify it.
Write handover documentation during the build. It was written near the end and it was good. Writing it during Forge would have exposed gaps in the runbooks while they were still cheap to close.
Set the switch calendar against the firm's filing calendar in week one. We did it correctly, in week five. In accounting, the domain calendar is a delivery constraint and belongs in the Blueprint.
What is still running
The client's own engineers operate the platform. The data-model corrections deliberately deferred during migration are proceeding against a stable baseline, with the pod in a supporting role.