BXUP Track progress & test yourself — free
Data Engineering · Module · ~13 min read
DATA ENGINEERING · MODULE

Legacy Modernization with AI

This module installs the migration factory: assess before you promise, let a deterministic transpiler do the bulk, spend LLM and human judgment only on the residue, and gate every object on generated parity evidence a client's auditors can read.

Legacy migration — stored procedures to dbt, Informatica to lakehouse, Control-M to Airflow — is the largest single body of data engineering work in the services industry, and it has just changed shape. This module teaches the workflow that changed it: deterministic transpilers doing the bulk conversion, LLMs handling the residue and the archaeology, and generated parity suites providing the proof that lets a client sign off.

The scenario — 1,180 stored procedures, two bids

The estimate that lost. Meena is a senior data engineer at a Pune services firm. In March, her delivery unit bid on migrating a US insurer's Teradata estate — 1,180 BTEQ scripts and stored procedures, 400+ Informatica mappings — to Databricks. They estimated the way the last decade taught them: procedures per engineer per week, measured on the previous project, where engineers read each proc line by line, rewrote it by hand, and eyeballed a few row counts afterward. The bid came out at 30 months and a headcount the client read as a price tag. A competitor bid 11 months. Meena's firm lost, and the postmortem note said what everyone already knew: the competitor wasn't bidding cheaper engineers, it was bidding a different workflow.

The project that won. In June, the same unit bids a comparable modernization — a bank moving Teradata and SSIS to Snowflake — with the workflow this module teaches.

  1. WEEK 1

    Assessment, not conversion. SnowConvert AI's assessment run inventories every object, scores conversion complexity, and flags the constructs automation won't handle. Output: a spreadsheet of 1,340 objects, 71% marked as clean automated conversion.

  2. WEEKS 2–5

    The transpiler converts those 950 objects deterministically; engineers never open most of them.

  3. TUE 10:00

    The residue is where the humans and LLMs go: a 1,100-line pricing procedure with cursor loops and session-variable tricks lands on Meena's desk.

  4. TUE 10:40

    A long-context LLM pass has produced what she used to spend two days building by hand — a control-flow summary, a table of fourteen extracted business rules, a dependency list, and a note that lines 214–260 are dead code guarded by a flag retired in 2019.

  5. THAT AFTERNOON

    She verifies the rules against the client SME — the conversation that actually needs a human.

  6. BY THURSDAY

    The proc is rewritten as four dbt models.

  7. THE PARITY GATE

    Every converted table flows through generated reconciliation queries comparing row counts, per-column aggregates, and sampled rows between Teradata and Snowflake, with the results landing in a report the client's auditors can read.

  8. NINE MONTHS

    The migration finishes. The parity report — 1,340 objects, 100% row-count match, 14 documented and client-approved intentional differences — is what turns "we moved your code" into "you can decommission the old system."

Same firm, same engineers. The difference is that conversion became a factory, and the engineers moved to the two stations that pay: judgment on the residue, and proof at the gate.

The factory — transpiler first, LLM second, humans on the residue

The core workflow, in order, with no steps skipped:

  1. Assess before you promise. Run the automated assessment — SnowConvert AI's assessment reports, Databricks Lakebridge's analyzer — over the full source estate. Output: object inventory, complexity scores, automation-coverage estimate, and the flagged constructs that will need hands.
  2. Transpile deterministically. Let the transpiler convert everything it can: SQL dialect translation (T-SQL/Oracle/Teradata → Snowflake/BigQuery/Databricks SQL), SSIS-to-dbt conversion, schema DDL. Deterministic output is auditable output — the same input produces the same conversion, and a mapping bug found once is fixed everywhere.
  3. Categorize the residue. Everything the transpiler flagged or fumbled gets bucketed: unsupported constructs, dynamic SQL, procedural logic (cursors, loops, session state), and vendor-specific behavior with no target equivalent.
  4. LLM-refine per bucket, with context. Each residual object goes through an LLM pass — Claude or an equivalent frontier model in an approved channel — with a context pack: source object, target platform conventions, the transpiler's partial output and error notes, and one exemplar of a correctly converted sibling.
  5. Gate on parity, object by object. Nothing is "migrated" until its generated reconciliation suite passes (next section). Parity is the definition of done, not a QA phase after done.
  6. Cut over with a rollback window. Run old and new in parallel on live loads long enough for the parity suite to pass on data the migration team never saw.

Why not skip the transpiler and let an LLM convert everything? Because at estate scale, determinism is the feature. An LLM converts each of 1,000 procedures slightly differently, its occasional quiet semantic drift multiplies across objects, and no two runs are identical — which makes auditing a per-object job instead of a per-rule job. The transpiler handles the boring 70–80% identically every time; the LLM's judgment is spent only where judgment is needed. Both flagship tools are free — SnowConvert AI at no cost from Snowflake, Lakebridge open source — which matters when the workflow itself is the bid differentiator.

WorkWho does itWhy
Bulk dialect/DDL conversion, mapping-tool translationTranspilerDeterministic, auditable, fix-once semantics
Residual constructs, procedural rewrites, idiom translationLLM + engineerNeeds judgment per object; context pack makes it reviewable
Business-rule verification, intentional-difference decisionsEngineer + client SMENobody else has the authority
Parity evidenceGenerated suitesVolume makes manual checking a fiction
Key Takeaway:

Transpiler for the deterministic bulk, LLM for the residue, humans for judgment, parity for proof — in that order, and the assessment report before any of it.

Dialect translation — where the LLM refinement pass earns its keep

A concrete residue case shows the division of labor. The transpiler converts this Oracle idiom and flags what it can't prove:

-- Source (Oracle): sessionized events via MODEL clause — transpiler flags:
-- "MODEL clause not supported; manual conversion required"
SELECT account_id, event_ts, session_id
FROM events
MODEL PARTITION BY (account_id) DIMENSION BY (rn) MEASURES (event_ts, 0 AS session_id)
RULES (session_id[ANY] = CASE WHEN event_ts[cv()] - event_ts[cv()-1] > 1800
       THEN session_id[cv()-1] + 1 ELSE session_id[cv()-1] END);

The LLM refinement prompt gets the flagged source, the transpiler's note, the target platform, and — critically — the intent: "this sessionizes events with a 30-minute gap rule." The refined output is the standard window-function pattern every modern warehouse handles (LAG to detect gaps, running SUM over gap flags to number sessions), plus a generated equivalence test on a fixture with boundary cases: a gap of exactly 1,800 seconds, a single-event account, events with identical timestamps. Stating intent matters because idiom-for-idiom translation preserves structure, and what you want preserved is behavior — the LLM translating "what this achieves" produces better target code than the LLM translating "what this says."

The recurring dialect traps deserve a standing checklist in your refinement prompts, because they are semantic rather than syntactic:

  • Implicit type-castingTeradata's forgiving casts versus Snowflake's stricter ones.
  • Date arithmeticDATE + 1 means a day in one dialect and an error in another.
  • QUALIFY availabilitywhether the target dialect supports the clause at all.
  • Empty-string-vs-nullOracle treats '' as null; almost nobody else does.
  • Division semanticsinteger division truncates on one platform and not the other.

Each is exactly the kind of quiet difference that survives review, passes a row-count check, and fails a per-column aggregate — which is why the parity suite in the next section checks aggregates, not just counts.

Stored-proc archaeology — reading code nobody alive has read

The migration's hardest objects aren't hard because of syntax; they're hard because the business logic inside them is undocumented, the author left in 2016, and the proc has been patched by eleven hands since. Before rewriting anything, run the archaeology pass. The prompt, against a long-context model:

Here is a 1,100-line Teradata stored procedure: <full source>
Produce, in order:
1. CONTROL FLOW: numbered summary of execution paths, including every
   branch condition and what makes each path fire.
2. BUSINESS RULES: a table — rule, plain-English meaning, line numbers,
   confidence (high/medium/low). Flag any rule that looks like a bug
   preserved as behavior.
3. SIDE EFFECTS: every table written, session variable set, external call.
4. DEPENDENCIES: objects read, objects that likely read this proc's outputs.
5. DEAD CODE: unreachable or flag-disabled sections, with evidence.
6. QUESTIONS: what you cannot determine from the source alone.

Two things about the output discipline. First, the confidence column and the QUESTIONS section are not decoration — they are the interface between the LLM's reading and the client SME conversation, and the medium/low-confidence rules are precisely the meeting agenda. Second, the extracted business-rules table is a deliverable, not scaffolding: clients pay for documented business logic recovered from opaque code, and the rules doc outlives the migration. "Flag any rule that looks like a bug preserved as behavior" earns its place — every legacy estate contains behavior that is wrong, known to be wrong, and load-bearing, and the migration's most expensive mistake is silently fixing it.

The archaeology pass feeds design, not codegen. You decide whether the proc becomes four dbt models or one, which rules are still wanted, and what dies with the legacy system; only then does conversion start, with the rules table in the context pack. SnowConvert AI's assessment reports and Lakebridge's analyzer output slot in as structural context — they tell you what the proc touches; the LLM pass tells you what it means.

Pro Tip

Run the archaeology prompt on two procs the team already understands deeply before trusting it on unknowns — the errors it makes on known code calibrate how much to trust its confidence column on unknown code.

Parity is the product

A migration without reconciliation evidence is an opinion. The parity suite is generated — at 1,340 objects, hand-writing comparisons is a fiction nobody staffs — and it runs at three levels per table:

  1. Row counts, total and per partition/date-slice, source vs target.
  2. Column aggregatesSUM, MIN, MAX, null count for every numeric; distinct count and null count for every key and category column.
  3. Sampled row-level diffdeterministic sample (hash-mod on the primary key, so both sides pick the same rows), full-row comparison.

The generation is metadata-driven — feed the LLM the table's schema and ask for the suite, or use the tools built for exactly this: Lakebridge's reconcile module, datacompy for programmatic diffs, Great Expectations when you want the checks living on as permanent tests. A generated aggregate check looks like:

SELECT 'claims_monthly' AS tbl, 'target' AS side,
       COUNT(*) AS row_ct,
       SUM(paid_amount) AS sum_paid, COUNT(DISTINCT claim_id) AS claims,
       SUM(CASE WHEN paid_amount IS NULL THEN 1 ELSE 0 END) AS null_paid,
       MIN(service_date) AS min_dt, MAX(service_date) AS max_dt
FROM analytics.claims_monthly
WHERE service_date >= '2024-01-01';
-- Generated per-table from schema metadata; run both sides, diff the rows

The suite needs a tolerance policy written before results arrive, because cross-platform migration produces legitimate differences that will otherwise be litigated one Slack thread at a time:

  • Floating-point aggregatesdiffer in the last decimal.
  • Timestamp semanticsshift between platform timezone behaviors.
  • Collation changesreorder "equal" strings.
  • Trailing spacesTeradata's CHAR semantics versus Snowflake's.

Each difference class gets a ruling — tolerance band, normalization applied before comparison, or "intentional difference" logged with a client sign-off. That log of intentional differences is half the value of the parity report: it is the documented answer to "why doesn't this match?" for every future auditor.

Warning

A parity mismatch discovered by the client after cutover costs more than the entire generated test suite — parity is the definition of done for every object, not a QA phase scheduled after the interesting work.

Scheduler migrations — the dependency graph is the deliverable

Job schedulers age worse than code: a 15-year-old Control-M or AutoSys estate encodes the company's actual operational dependencies in a format three people can read. The migration workflow to Airflow 3 or Dagster:

  1. Export everythingjob definitions, calendars, conditions, file watchers, alert configs — into text the LLM can consume.
  2. Reconstruct the dependency graph first. Before generating any DAG code, have the LLM produce the graph as data (job → predecessors, triggers, calendars) and render it for human review. Legacy schedulers hide dependencies in conditions and file-watch conventions; the graph review is where you find the job that secretly gates month-end close.
  3. Translate in bulkjob definitions become DAG code — Astro IDE targets exactly this legacy-scheduler-to-Airflow path; Claude Code with an exemplar DAG and your conventions file does it tool-agnostically.
  4. Shadow-run before cutover: run the new scheduler's DAGs in no-op mode against the real calendar and diff fire times and ordering against the legacy scheduler's logs for a representative period, month-end included.

The trap in scheduler migrations is implicit coupling — Job B was scheduled at 03:00 because Job A "always finishes by 02:40," a dependency that exists in nobody's config. Ask the LLM to flag time-adjacent jobs sharing tables as suspected implicit dependencies, then confirm with humans; making implicit dependencies explicit is the lasting payoff of the whole exercise, because the new DAGs encode reality instead of folklore.

Scoping and estimating — the services-company craft

For India services firms, this workflow changes the commercial math, not just the engineering. Estimates now start from the assessment report, not from historical throughput: run SnowConvert AI or Lakebridge assessment over the (client-approved) source inventory during pre-sales, and build the estimate from the complexity distribution it returns.

Bucket (from assessment)Effort modelWho
Clean automated conversionMinutes per object — transpile, parity, doneFactory line, junior-heavy
Automated with flagged warningsReview + touch-up per objectMid-level engineers
Residual: procedural/dynamic/vendor-specificLLM-refinement workflow, hours-to-days per objectSenior engineers
Archaeology-grade (undocumented, business-critical)Archaeology pass + SME cycles + redesignSenior + client SME time — the schedule risk lives here
Parity + cutoverPer-table generated suites + tolerance rulingsRuns continuously, priced explicitly

Three estimating rules:

  • Discount vendor automation claims in bidsLakebridge's ~80% automation figure is a vendor claim across estates unlike yours; bid from your assessment's distribution, and pilot-convert a sample of 20 objects across buckets to calibrate hours before committing to numbers.
  • Price the parity phase and SME access explicitlythe archaeology bucket's schedule risk is client-availability risk, and unpriced SME dependency is where nine-month migrations become fourteen.
  • Put the parity report and business-rules documentation in the SOW as named deliverablesthey are the artifacts clients renew on.

Client-data compliance is a first-class constraint, not a footnote. Most MSAs prohibit pasting client schemas, code, or data into public AI tools, so establish the approved channel before the first prompt:

  1. Client-tenancy enterprise AI. Azure OpenAI, Bedrock, or Vertex in the client's account.
  2. Your employer's logged AI gateway. Second choice when no client tenancy exists.
  3. Local models. Ollama-class.
  4. Abstain. When none of the above is available.

For pre-sales work before any channel is approved, use synthetic stand-ins — rehearse prompts on invented schemas that mirror the shapes, never the content. Where pipelines touch personal data, DPDP Act obligations (and the client's own regimes — GDPR, HIPAA, RBI/SEBI localization for BFSI clients, the largest Indian services vertical) apply to migration work too: parity testing on real rows is personal-data processing, so scope it to the approved environment, or run reconciliation on masked or aggregate data where the MSA demands it.

Remember:

The approved-channel hierarchy — client-tenancy AI, employer gateway, local model, abstain — is decided before the first prompt containing anything client-derived, and "abstain" is a legitimate rung, not a failure.

Pro Tip

Your first 15 minutes on a migration Monday — if you have an assigned proc you don't fully understand, run the archaeology prompt on it and take the extracted business-rules table (with its confidence column) into your next SME call; if every proc is already understood, generate the parity aggregate suite for the table you're about to cut over.

Check yourself: The estate has 1,180 stored procedures and a frontier LLM could convert each one. Why does the winning workflow run a deterministic transpiler first and reserve the LLM for the residue?

▸Show answeroptional deep dive — click to expand

At estate scale, determinism is the feature. An LLM converts each of 1,000 procedures slightly differently, drifts semantically now and then, and no two runs match — so auditing becomes a per-object job. A transpiler converts the boring 70–80% identically every time: the same input yields the same output, a mapping bug found once is fixed everywhere, and auditing collapses to a per-rule job. That frees the LLM's judgment (and the engineer's) for exactly the residue where judgment is needed — procedural logic, dynamic SQL, vendor idioms — and every converted object is still gated on a generated parity suite, because parity, not "it converted," is the definition of done.

Where this lands in your week

  • On a migration project nowthis week's change is running the archaeology prompt on your two ugliest assigned procs and taking the business-rules tables into your next SME call; that alone typically halves the reverse-engineering cycle. This month's change is making parity generated and continuous instead of manual and terminal, and getting the tolerance policy ruled on before results exist.
  • In pre-sales or estimatingthe assessment-first workflow is the bid: inventory, complexity distribution, calibration pilot, explicitly priced parity. The free tooling (SnowConvert AI, Lakebridge) means the factory costs approximately nothing to stand up.
  • On any client workknow your approved AI channel cold, because the workflow above is only usable inside it.

This module anchors LAB-02 — Legacy migration sprint: reverse-engineer a 600-line legacy T-SQL stored procedure with an LLM, convert it to dbt models transpiler-first with LLM refinement, and prove parity with generated reconciliation queries. The artifact — an explanation doc, converted models, and a parity report showing a 100% match — is a miniature of the client deliverable this module's workflow produces at estate scale, and a directly portfolio-usable one. Do this at work this week → pb-05 Reverse-Engineer a Legacy Stored Procedure Before You Rewrite It, pb-07 Migrate a Legacy ETL Platform with Transpilers plus LLM Refinement, and pb-08 Generate Migration Parity and Reconciliation Test Suites.

This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in