Managing 18 engineers across 5 migration waves while keeping legacy operations running — nothing here works if I can't see a blocker the day it happens.
Sprint cadence, triage protocols, and blocker clearing
I had 18 engineers spread across data engineering, BI, and QA, split into five concurrent wave teams. My job in this phase wasn't writing code — it was visibility and momentum: making sure everyone could see health at a glance, blockers surfaced before they became schedule risk, and decisions got made once and recorded, not re-litigated.
I ran each wave as its own mini-project: build the objects, convert the code, load history, reconcile, and fix. Sprints carried the work; the exit gate I set for each wave — "every object reconciled and signed off" — is what let it close out. I tracked progress as % mapped → % built → % reconciled per wave, because I wasn't going to accept "80% done" as an answer that meant nothing.
One screen, read in two minutes: health per workstream, what moved, what's blocked, and what needed a decision from someone above me.
Steering doesn't have time to read sprint boards, and I didn't want status to be whatever I said out loud in the meeting. One consistent screen, every two weeks, is what let a bad week get flagged early instead of surfacing as a missed milestone.
Sales + Mktg objects built; history loaded. 92% reconciled.
Revenue by Segment rebuild behind by 1 sprint — SME review slot slipped.
Reconciliation suite passing; report-parity in progress.
Champions onboarded for Sales; training content in build.
| Milestone | Status | Decision I needed |
|---|---|---|
| Wave 3 reconcile complete | At risk | Approval for extra SME hours to recover the BI slip |
| Wave 4 build start | On track | — |
Risks, assumptions, issues, dependencies — my living tracker. An issue is a risk that already happened; a dependency is what one team needs from another before they can move.
A risk I only track in my head dies with my memory of it. The RAID log is what caught the 6% revenue variance as an open issue with an owner, instead of a passing comment in a standup that got lost by Friday.
| Type | Item | Status | Owner · action |
|---|---|---|---|
| Issue | Revenue sum in Snowflake exceeds source by 6% | Open | Me · traced to snapshot grain — see decision D-07 below |
| Risk | SME availability for Wave 4 UAT | Monitoring | Change Lead · slots booked 3 weeks out |
| Assumption | Historical FX rates available for USD normalization | Confirmed | Data Gov · rate table sourced |
| Dependency | Wave 3 facts need Wave 1 shared dimensions | Met | Data Eng · dims signed off |
Scope changes went through impact assessment and my sign-off — not a Slack thread. Ambiguous calls got ruled by the business, logged, and I never let them get re-litigated.
| ID | Decision | Ruling | By |
|---|---|---|---|
| D-07 | Does "close amount" mean booked or forecasted revenue? | Booked. Forecast moves to a separate measure. This resolved the 6% variance I'd flagged. | Sales Finance SME |
| D-08 | Retire or migrate the legacy Quota v3 report? | Retire — confirmed duplicate, no 12-mo access. | Sales Ops |
Golden thread · the catch
My reconciliation suite flagged the Sales/Revenue close-amount sum running 6% high in Snowflake. It wasn't a load bug — it was a grain mismatch: the source carried one row per opportunity per daily snapshot, and the join fanned it out. I didn't fix it with code; I forced the D-07 ruling that fixed the definition and the grain. I caught it in test, not from a controller three weeks after go-live.
| Forum | Cadence | Purpose |
|---|---|---|
| Steering committee | Bi-weekly | Health, escalations, gate approvals |
| Workstream standup | Daily | Blockers, dependencies, sprint progress |
| Risk / RAID review | Weekly | Score changes, new issues, mitigations |
| Design authority | As needed | Ambiguous-field rulings, model decisions — chaired by me |
Not every pipeline earns a rewrite
I didn't let the team rewrite everything on principle, and I didn't let them lift-and-shift everything for speed either. I made the call per pipeline, against criteria I set up front.
| Signal | My call | Reasoning |
|---|---|---|
| Feeds a financial or revenue metric | Refactor | Worth the engineering cost to get the grain and logic right the first time, given zero-tolerance reconciliation. |
| High-volume, straightforward T-SQL | Lift-and-shift | Mechanically converted to Snowflake Scripting; not worth re-architecting logic that already worked. |
| Known performance bottleneck in legacy | Refactor | Carrying a bottleneck forward just moves the complaint to a new platform. |
Scar tissue
What I Actually Decided Here
Decision: When a legacy financial pipeline failed its performance targets in Wave 3, I decoupled it from the wave cutover entirely and shipped it as a targeted mini-patch two weeks later, rather than holding the whole wave hostage to one pipeline.
Friction: The Product Director pushed hard to stall the entire wave release until that single pipeline was resolved — from where they sat, an unresolved pipeline looked like an unfinished wave.
Outcome: I unblocked 90% of business users on schedule, and put a dedicated pair-programming pod on the one bottleneck instead of letting it hold everyone else hostage.