Skip to content
//Accounting & CPA · Software Development

Embedded pod rescues a stalled migration for a Canadian CPA network

An embedded engineering pod took over a stalled internal platform migration, delivered it inside a committed 90-day window with a rehearsed rollback, and now owns the roadmap as an extension of the in-house team.

01The challenge

An internal migration had stalled for over a year. Knowledge was scattered and the in-house team lacked the senior capacity to finish it.

02What we did
  • 01Read what the in-house team had already built before proposing anything.
  • 02Built a validation harness against the accounting meaning of the data before migrating anything.
  • 03Ran dual-write with daily reconciliation, then switched reads incrementally by module and office.
  • 04Rehearsed rollback before promotion and handed over with runbooks and tests.

Results

MetricValueSource
Elapsed in-house time before the pod engaged12 months, migration in flight, no cutover reachedClient-reported
Delivery window once the pod engaged90 days, pod start to production cutoverQognition delivery record
Time saved per staff member10+ hours per weekClient-reported, post-cutover
Production downtime at cutoverZero — cutover ran behind a dual-write and read switchQognition 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"]
Architecture diagram — Mermaid source (not rendered)

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.

FailureWhat happens
Backfill mismatchValidation harness fails the run. Nothing is promoted.
Drift appears during dual-writeLegacy remains system of record. Drift is diagnosed before any read switch moves.
A module misbehaves after read switchThat module's switch flips back. One module, one office. Others unaffected.
Problem found after promotionRollback to legacy via the reverse-sync window, on a rehearsed procedure with a named decision owner.
Filing-season crunchNo 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.

//Results

The numbers

90 days
pod start to production cutover, Qognition delivery record
10+ hrs
saved per staffer each week, client-reported post-cutover
Zero
production downtime at cutover — dual-write and read switch, not big-bang

Verification —The 90-day delivery window and the cutover behaviour come from our delivery record. The twelve-month elapsed figure and the 10+ hours per week are client-reported and stated as such. A reference call with the client can be arranged under NDA on request.

//Tech stack
  • Next.js
  • Postgres
  • Prisma
  • Azure
  • GitHub Actions

Engagement led by

Prabal Bhandari

Founder & Chief Executive Officer · MS Computer Science, Georgia Tech

· Updated

//FAQ

Questions, answered straight

No, and the page should not be read that way. They built the domain model, resolved the hard business rules and documented the data. The pod inherited that, and it is why a 90-day window was credible. What they lacked was uninterrupted engineering time, because migration work is always the first thing displaced by client deadlines. Different constraint, not different competence.

//Next step

Start with a Blueprint.
Lock the scope. Ship the system.

Contact the studio

Not sure you're ready? Take the Build Readiness Scorecard