How I mapped 1,200 objects, established governance, and built the wave migration strategy — before a single line of conversion code got written.
Setting up the wave plan & critical artifacts
Before I let anyone start building, I needed the mandate, the scope, and the decision rights nailed down. This is the unglamorous phase where a migration is either set up to succeed or quietly set up to fail six months out.
I wrote the charter myself and took it to Steering rather than waiting for someone to hand me one. It named the outcome in numbers I'd be held to: ~1,200 objects across 8 functions, zero-defect reconciliation on every financial metric, adoption by ~4,500 users before I'd agree to decommission anything.
I put three options on the table myself, because I wanted the trade-offs on record before anyone could relitigate them mid-program.
| Approach | What it is | Why I rejected or kept it | Verdict |
|---|---|---|---|
| Lift & shift | Replicate the SQL Server schema as-is on Snowflake. | Fastest, but I'd be carrying every legacy flaw and un-tuned model into the new platform. I killed this one. | Rejected |
| Re-architect | Redesign the full data model for Snowflake from scratch. | Best end state on paper, but the timeline and risk ballooned past what the business would tolerate. I killed this one too. | Rejected |
| Hybrid | Migrate structure faithfully; selectively re-model high-value domains and retire dead objects. | Balanced speed and quality — and the rationalization I did up front paid for itself before we built anything. | What I recommended |
I built the RACI myself and forced every workstream lead to sign off on it in the same room. Ambiguous decisions resolve once, in the forum I designed for it — not repeatedly, at the keyboard, three weeks apart.
Without a written RACI, "who decides" defaults to whoever's loudest or most senior in the room — and that person changes by meeting. This is what let me point to a document instead of re-arguing authority every time.
| Key decision / deliverable | Me (TPM) | Exec Sponsor | Data Eng Lead | Function SME | BI Lead | Change Lead |
|---|---|---|---|---|---|---|
| Field-mapping sign-off | A | I | C | R | C | I |
| Report disposition (migrate/retire) | R | I | I | A | C | I |
| UAT sign-off | A | I | C | R | C | I |
| Go / no-go decision | R | A | C | C | C | C |
| Legacy decommission | R | A | C | C | I | I |
I inventoried every report and who actually used it before I let anyone touch a migration plan. Nobody thanks the TPM who faithfully migrates 400 reports when 150 of them haven't been opened in a year — so I didn't let that happen here.
Golden thread
The Sales/Revenue close-amount report is one I confirmed as high-use and kept in scope. I named its owner — a Sales Finance SME — right here, and held them accountable for the field's meaning all the way through mapping, UAT, and cutover.
I sequenced functions in dependency order — shared dimensions before the facts that depend on them — so no wave sat blocked waiting on another. I also protected the financial close calendar myself: no cutover ever got scheduled during month-end, full stop.
A wave plan isn't a Gantt chart for show — it's the sequencing logic made checkable. Anyone could see why their function landed where it did, which is what let me defend "you're Wave 3" without it sounding like an excuse.
The most important document
This is the one document I refused to let go stale. Every object's wave assignment, and my reasoning for it, lived in one place — so nobody could argue sequencing on a hunch after the fact.
Sequencing lives in someone's head until it's written down — and heads leave the room, get pulled onto other fires, or just forget. This matrix is what let a new engineer, three months in, understand why Wave 4 couldn't start early without asking me.
| Object / domain | Upstream dependency | Wave I assigned | Why it couldn't move earlier |
|---|---|---|---|
| DIM_ACCOUNT, DIM_DATE, DIM_PRODUCT | None — root objects | Wave 1 | Every downstream fact joins to these. Get them wrong and the error replicates across every wave that follows. |
| FACT_GL_TRANSACTIONS (Finance) | Shared dims + Chart of Accounts | Wave 2 | Finance's close calendar drove the earliest cutover-safe window — I sequenced it right after the shared dims, not before. |
| FACT_OPPORTUNITY (Sales/Marketing) | DIM_ACCOUNT, DIM_DATE | Wave 3 | Can't reconcile revenue against an account dimension that hasn't itself been validated yet. |
| FACT_SHIPMENT (Supply Chain) | DIM_PRODUCT, DIM_ACCOUNT | Wave 4 | Waits on the Wave 1 product-dimension rebuild landing clean. |
Scar tissue
What I Actually Decided Here
Decision: I overrode the domain-based migration order that Sales and Marketing both wanted, and held the line on a strict dependency-first wave strategy instead.
Friction: Domain leads wanted their functions prioritized immediately — nobody wants to hear "you're Wave 3" when they think their number matters most. I held the line on sequencing by upstream dependency, not by whoever asked loudest.
Outcome: It prevented upstream pipeline rework we would have paid for twice, and it meant Wave 1 laid a foundation the other four waves could actually build on without replaying the same fixes.