Author: Jugal Shah

  • Building a Data Lake That Earns Its Keep: Lessons from Billions of IoT Rows

    Most data lakes are where data goes to be forgotten. Ours had a different job: catch faulty devices and faulty production components before downtime cost real money — across billions of rows of IoT data from a manufacturing operation.

    Here’s what building that system taught me about data engineering that actually earns its keep.

    Start with the decision, not the data

    The classic mistake is ingesting everything first and asking questions later. We did the opposite: the lake existed to answer one question — what is about to fail, and what should we do about it? Every ingestion choice, every schema decision, every pipeline stage served that question.

    A data lake without a decision to support is just expensive storage. Define the decision first, and the architecture mostly designs itself.

    Ingestion at billion-row scale is a different sport

    When you’re ingesting billions of IoT rows, the problems stop being about query performance and start being about survival:

    • Throughput over elegance. The pipeline has to absorb bursts without falling over. Backpressure handling and idempotent writes matter more than clever transformations.
    • Schema drift is constant. Devices get firmware updates, sensors get replaced, fields appear and disappear. If your pipeline breaks every time a device sneezes, you don’t have a pipeline — you have a pager generator. Build for evolution: permissive ingestion, strict validation downstream.
    • Late and out-of-order data is normal. IoT networks are unreliable by nature. Your windowing and watermarking strategy isn’t an optimization; it’s the difference between correct and misleading.

    The pipeline is the product

    The core of the system wasn’t the lake itself — it was the data engineering pipeline running continuously over it, checking for faulty IoT devices and faulty manufacturing components and reporting them. This is the part most “data lake” projects underinvest in: they build the storage and declare victory.

    Storage is table stakes. The value is in the continuous compute — the jobs that turn raw rows into signals. If nobody is running queries that change decisions, you don’t have a data platform. You have a data attic.

    Then put AI on top — not underneath

    Once the pipeline reliably surfaced faults, we connected an LLM system using RAG over the pipeline’s findings. Instead of just an alert saying “component X looks wrong,” engineers got a diagnosis of the fault plus possible solutions.

    The ordering matters. AI on top of a broken pipeline just generates confident-sounding nonsense faster. The RAG layer worked because the retrieval corpus — the fault findings — was trustworthy. Data quality wasn’t a separate initiative; it was the foundation the AI stood on.

    This is the pattern I keep seeing: teams want the AI layer first and the data engineering “later.” Later never comes, and the AI disappoints. Build the pipeline, earn trust in the data, then add intelligence.

    From reactive to predictive is an organizational shift, not a technical one

    The technical outcome: faults surface early, with AI-generated guidance on what to do about them. But the real shift was operational — the team moved from reactive firefighting to predictive maintenance.

    That shift didn’t happen when the pipeline went live. It happened when the engineers trusted it enough to act on its findings without double-checking everything manually. Trust took time and a track record. There’s no shortcut: the system has to be right, repeatedly, in public, before people change how they work.

    The checklist

    If you’re building a data lake that needs to earn its keep:

    1. Define the decision the lake serves before ingesting a single row.
    2. Design ingestion for bursts, schema drift, and late data — not the happy path.
    3. Invest in the continuous pipeline, not just the storage.
    4. Get the data trustworthy before adding AI on top.
    5. Measure success by changed decisions, not by rows stored.

    A data lake is not a strategy. It’s infrastructure in service of decisions. Build it that way, and it pays for itself. Build it any other way, and it’s just a very expensive attic.

    — Jugal

  • Welcome to The Agentic Enterprise

    If you’re reading this in your inbox, you’ve been subscribed to aiDeeva for a while — thank you. You’ve been getting new posts as they publish. Today I’m giving this newsletter a proper name and a proper promise.

    The Agentic Enterprise is a monthly note on production AI, agentic systems, and the data architecture behind them — written for technology leaders shipping AI that has to work.

    What you’ll get, once a month:

    • What I’m learning from running AI systems in production — evals, cost control, human-in-the-loop design
    • The data architecture work that makes agents actually useful
    • Honest takes on where agentic AI earns its keep, and where it doesn’t

    What you won’t get: hype, fluff, or daily noise.

    If you’ve been here since the SQL Server days — welcome back, and yes, the deep database content isn’t going anywhere. It turns out two decades of data architecture is exactly what the agent era needs.

    Start here if you’re new: The Agentic AI hub

    — Jugal

  • RAG in Production: What the Demos Don’t Tell You

    The demo is always magical. You point a retrieval-augmented generation system at a pile of documents, ask a hard question, and get a fluent, sourced, correct answer. The room nods. Someone says “we should ship this.” And then production happens — and production has questions the demo never asked.

    I have built RAG systems that run in production, including one layered over a manufacturing data pipeline processing billions of IoT rows — where the system doesn’t just retrieve documents, it diagnoses faulty equipment and suggests fixes. The gap between that and a demo is everything I am about to describe.

    Chunking Is Where Demos Go to Die

    Every RAG demo works because someone carefully prepared the documents. In production, nobody prepares the documents. They arrive as PDFs with broken formatting, Confluence pages with five years of edits, ticket histories, manuals scanned sideways. Your chunking strategy meets this reality on day one.

    Chunking — how you split documents into retrievable pieces — is the highest-leverage decision in the system, and it’s the one most teams spend the least time on. Chunk too large and the retriever returns bloated, imprecise context; the model drowns in irrelevant text and the answer quality drops. Chunk too small and you shred meaning across fragments; the retriever finds pieces but the model can’t assemble the full picture. Overlap between chunks helps, but it’s a bandage, not a strategy.

    What actually works: chunking driven by document structure, not fixed sizes. Respect natural boundaries — sections, procedures, Q&A pairs. Different document types get different strategies; the approach that works for product manuals will mangle ticket histories. And critically: evaluate chunking choices against real questions, not vibes. Build a small set of representative questions with known-good answers, and measure retrieval quality as you tune. The teams that treat chunking as an empirical problem ship better systems than the teams that treat it as a configuration choice.

    One more thing demos hide: metadata. In production, retrieval quality often depends less on the embedding model and more on the metadata attached to each chunk — document type, date, product version, access level. A system that can filter “only show me troubleshooting guides for firmware 4.x” will outperform a system with a fancier model and no metadata, every time.

    Your Data Is Stale Before You Ship

    The demo uses a static document set. Production data moves. New documents arrive, old ones get updated, some get deleted — and every change creates a window where your system confidently answers from outdated information.

    This is a pipeline problem, not a model problem. You need ingestion that keeps up: change detection on source systems, incremental re-indexing (not nightly full rebuilds once the corpus is large), and deletion handling — because a document that was removed for being wrong must actually stop being retrieved. I have seen production systems answer questions from documents that were deleted months earlier, because nobody built the deletion path.

    Freshness requirements vary by use case, and you should define them explicitly. A system answering questions about product documentation might tolerate daily updates. A system layered over operational data — like the manufacturing pipeline I built, where equipment telemetry streams continuously — needs freshness measured in minutes, not days. Define the freshness SLA for your use case, then design the pipeline to meet it, and monitor it like any other SLA. Stale answers delivered confidently are worse than no answers at all.

    Versioning matters too. When a document updates, do you replace the chunks or version them? For regulated or safety-critical answers, you may need to show which version of a document an answer came from. Build this in early; retrofitting provenance onto a running system is painful.

    Latency and Cost at Real Query Volume

    The demo answers one question at a time, with nobody watching the clock or the meter. Production answers thousands, concurrently, with users who leave if it takes too long and finance teams who notice the bill.

    Latency in RAG is a chain: embedding the query, retrieving candidates, re-ranking, assembling context, generating the answer. Each step adds milliseconds, and the generation step dominates. Practical levers: retrieve fewer, better chunks (quality retrieval beats quantity); cache aggressively — common questions, common documents, even embedding computations; consider smaller, faster models for the generation step when the retrieved context is strong (a good retriever with a small model often beats a mediocre retriever with a large one); and stream responses so users see progress instead of staring at a spinner.

    Cost follows the same chain. Embedding is cheap per document but not free at scale — re-embedding an entire corpus because you changed chunking strategy is a real line item. Generation is the expensive step, priced per token, and long retrieved contexts make every query costly. The discipline is the same as any production system: measure cost per query, set budgets, and treat context-window bloat the way you’d treat any other resource leak.

    The architecture insight: design for the query volume you’ll have in a year, not the demo volume you have today. The retrieval infrastructure that handles a hundred queries a day gracefully may fall over at ten thousand. Load-test the full chain early — it’s much cheaper than re-architecting under pressure.

    Guardrails Aren’t Optional

    A demo that hallucinates is embarrassing. A production system that hallucinates is a liability. Guardrails are the difference, and they need to be engineered, not hoped for.

    Grounding and citations. The system should show its work — every substantive claim traceable to retrieved sources. This isn’t just good UX; it’s how users learn to trust the system, and how you debug it when answers go wrong. If an answer can’t cite its sources, that should be visible, not hidden.

    PII and sensitive data. Your corpus contains things the model should not repeat — personal data, credentials in old tickets, confidential business information. Redaction at ingestion, access-aware retrieval (users only retrieve documents they’re permitted to see), and output filtering are all necessary. Access control deserves emphasis: a RAG system that retrieves across permission boundaries is a data leak with a chat interface. Filter by the user’s permissions at retrieval time, not after generation.

    Evaluation as a guardrail. Build an evaluation harness and run it continuously — not just during development. A small set of golden questions with expected answers, run against every change to prompts, models, chunking, or data:

    for question, expected_sources in eval_set:
        answer, sources = rag_pipeline.ask(question)
        score_retrieval(sources, expected_sources)  # right documents?
        score_faithfulness(answer, sources)          # grounded in them?
        flag_pii(answer)                            # nothing leaked?

    When eval scores drop, you know before users tell you. The teams that run evals in CI ship with confidence; the teams that don’t ship with hope.

    Human escalation paths. Some questions shouldn’t be answered by the system — low-confidence retrievals, safety-critical topics, anything where a wrong answer is expensive. Design the handoff: when confidence is low, say so and route to a human. “I don’t know” is a feature, not a failure.

    Treat retrieved content as untrusted input. This one surprises teams: your documents can attack your system. A document containing instructions (“ignore previous instructions and…”) gets retrieved, placed in context, and the model may follow it — this is indirect prompt injection, and it works against production systems today. Defenses include separating retrieved content from instructions structurally, scanning retrieved chunks for instruction-like language, and never letting retrieved text drive privileged actions (sending email, changing data, executing commands) without human confirmation. If your RAG system can do things rather than just answer, this moves from important to critical.

    When RAG Is the Wrong Answer

    After all this, an honest admission: RAG is not always the right architecture, and knowing when to say so is part of doing this work well.

    RAG excels when the knowledge is large, changes over time, and needs to be cited — documentation, policies, operational knowledge. It struggles when the task requires reasoning over structured relationships rather than retrieving passages. “Which components fail together, and why?” is a question about relationships — and that’s where knowledge graphs earn their place.

    In the manufacturing system I mentioned, RAG alone wasn’t the answer. The pipeline detects faulty IoT devices and components from billions of telemetry rows — but diagnosis benefits enormously from a knowledge graph of the equipment: which components connect to which, which failures cascade, what the maintenance history shows. The graph captures structure; RAG captures narrative. Together, they answer questions neither handles alone: the graph finds what’s related, RAG explains what it means. If your problem has rich entity relationships — equipment, suppliers, systems, dependencies — consider the graph as a complement, not a competitor, to retrieval.

    And sometimes the answer is fine-tuning, or a smaller specialized model, or — heresy — a well-built search interface without a language model at all. The right architecture follows the problem. Anyone selling RAG as the universal answer is selling, not engineering.

    The Bottom Line

    Production RAG is a systems problem wearing a model’s clothes. The demo shows you the model; production tests your chunking, your data pipelines, your latency budget, your guardrails, your evals, and your judgment about when RAG is even the right tool. Get those right and you have something genuinely valuable. Skip them and you have a demo that bills by the token.

    If you’re building RAG for real users — or staring at a pilot that isn’t surviving contact with production — reach out through the contact page. I’ve built these systems where they have to work, and I’m glad to help you think through yours.

    Related: the Generative AI Foundations learning path, and case studies including an IoT + RAG manufacturing system.

  • Legacy to Cloud-Native: A Database Modernization Playbook

    Database modernization is where cloud migrations go to get hard. Applications can be containerized, rehosted, refactored — but databases carry the data, the transactions, and the business’s tolerance for downtime, which is usually near zero. I have worked on massive database migration programs, including estates of tens of thousands of SQL Server and Oracle databases, and I have seen the same patterns decide success or failure every time.

    This is the playbook I wish someone had handed me at the start: four phases, in order, with the pitfalls marked.

    Phase 1: Assess — Know Your Estate

    You cannot modernize what you have not inventoried. This sounds obvious, and it is the phase most often rushed — which is why it causes the most damage when skipped.

    Build a real inventory. Not the spreadsheet from three years ago. Automated discovery of every database instance: version, edition, size, workload type, owner (or “owner unknown,” which is itself data). In large estates, expect to find databases nobody knew existed. I have seen discovery phases surface a meaningful percentage of previously unknown instances. Each one is a migration you didn’t plan for — or worse, a database you would have left behind.

    Map dependencies. Databases are never standalone. Applications connect to them, ETL jobs feed them, reports read from them, linked servers chain them together. Dependency mapping is tedious, unglamorous work, and skipping it is the number one cause of cutover surprises. For each database, you need to know: what connects to it, what breaks if it moves, and what breaks if its connection string changes.

    Profile the workloads. Not all databases want the same future. Profile each workload on the dimensions that drive the modernization decision: transaction volume and patterns, availability requirements, data size and growth, latency sensitivity, compliance constraints, and how much the application can change. A reporting database and a core transactional system will modernize differently, and the assessment phase is where you learn which is which.

    Grade your data quality going in. Modernization is the worst possible time to discover that the data is a mess — duplicate records, inconsistent formats, orphaned rows. You don’t have to fix everything before migrating, but you need to know what you’re carrying, because every data quality issue gets harder to fix after it has been moved, transformed, and loaded into a new platform.

    The output of this phase is not a document. It is a decision-ready inventory: every database classified, dependencies mapped, workloads profiled. Everything downstream depends on this being right. Spend the time here; it is the cheapest place to find problems.

    Phase 2: Decide — One Strategy Per Workload, Not Per Project

    The classic mistake is picking one modernization strategy for the whole program — “we’re replatforming everything” — and then discovering that workloads have opinions. The right approach is one strategy per workload, chosen deliberately. The industry’s “6 Rs” framework, applied specifically to databases:

    Strategy What it means for databases Best when…
    Retain Leave it where it is The database is stable, cheap to run, and tied to systems you aren’t touching. Not everything needs to move.
    Rehost Lift-and-shift to cloud VMs You need speed, the database version is supportable, and you’ll optimize later. Fastest path, highest running cost.
    Replatform Move to a managed cloud database service You want to shed operational burden (patching, failover) without changing the engine. The sweet spot for many workloads.
    Repurchase Replace with SaaS The database supports a commodity function (and the business agrees). Rare for core systems, common at the edges.
    Rearchitect Redesign for cloud-native (distributed, serverless, new data model) The workload needs elasticity or scale the old architecture can’t give. Highest effort, highest payoff.
    Retire Decommission Discovery found it and nobody can explain what it’s for. Celebrate these — every retirement is budget recovered.

    A few practical notes on choosing. Replatform (managed database services) is underused by teams who overestimate how different it is — for most standard workloads, it removes entire categories of operational work with minimal application change. Rearchitect is overused by teams who underestimate it — changing data models and consistency guarantees under a running business is the hardest kind of engineering; reserve it for workloads where the payoff justifies the risk. And Retain is a legitimate strategy, not a failure — a stable, cheap database that isn’t in the way doesn’t need a migration story.

    Document the decision per workload with the reasoning. Six months later, when someone asks “why did we replatform this instead of rearchitecting?”, the answer should be written down, not reconstructed from memory.

    Phase 3: Execute — Waves, Not Big Bangs

    Nobody modernizes thousands of databases at once. You do it in waves — and how you design the waves determines whether the program feels controlled or chaotic.

    Sequence by risk, not by enthusiasm. The first wave should be meaningful but forgiving: real production workloads, but not the ones where downtime makes the news. Early waves build the team’s muscle memory and prove the patterns. Save the hardest, most critical databases for when the team has done it twenty times, not twice.

    Standardize the wave pattern. Every wave should follow the same motion: pre-migration validation (backups verified, rollback plan tested), the migration itself (using the same tooling and runbooks every time), post-migration validation (data integrity checks, application smoke tests, performance baselines compared), and a defined bake-in period before the wave is declared done. The runbook gets better every wave. By wave five, the team is fast because the process is proven, not because they’re rushing.

    Validate like you don’t trust yourself. Data integrity checks after migration are non-negotiable: row counts, checksums on critical tables, application-level reconciliation for financial data. Performance baselines matter too — capture before-and-after metrics for key queries, because “it works” is not the same as “it works as well as before,” and users will notice the difference before you do.

    Plan the cutover, then plan the rollback. Every migration needs a cutover plan with a defined window, a communication plan, and success criteria — and a rollback plan that has been tested, not just written. The rollback plan you haven’t tested is a hope, not a plan. Most rollbacks never happen. The ones that do happen save careers.

    Mind the data transfer. Moving terabytes between on-premises and cloud (or between regions) takes longer than the project plan assumes, every time. Model transfer time realistically, including the final delta sync during cutover. For very large databases, physical transfer or dedicated connectivity beats the public internet by enough to change the schedule.

    Phase 4: Operate — The Migration Isn’t Over at Cutover

    Here is where programs quietly fail: the migration “succeeds,” everyone celebrates, and then nobody does the unglamorous work that makes the new estate actually better than the old one.

    Decommission the old estate. This is the step most often skipped and the most expensive to skip. Every old database left running “just in case” is infrastructure, licensing, and operational attention you’re still paying for. Set decommission dates during planning, and treat missed decommission dates as the budget issue they are.

    Rewrite the runbooks. The new estate has different failure modes, different tooling, different escalation paths. Runbooks written for the old world are worse than useless — they’re misleading. This is also the moment to build in the automation you wished you’d had: the fleet-scale lesson is that anything manual at scale doesn’t happen.

    Optimize — the second migration. Remember the lift-and-shift waste problem: workloads moved as-is are carrying their on-premises sizing into per-hour billing. The post-migration optimization pass (right-sizing, storage tiering, reserved capacity for stable workloads) is where a large share of the migration’s economic value is actually captured. Schedule it as part of the program, not as a someday-maybe.

    Transfer the knowledge. The migration team knows things the operations team doesn’t. Structured handover — not a document dump, but walkthroughs of the new architecture, the known quirks, and the decisions made along the way — is what prevents the new estate from becoming the next legacy estate.

    Pitfalls I’ve Seen (So You Don’t Have To)

    A short list, earned the hard way:

    Skipping dependency mapping. The cutover that broke a reporting chain nobody knew existed. Map first, migrate second.

    Big-bang cutovers. Migrating everything in one weekend because the plan looked cleaner. Waves exist for a reason.

    Untested rollback plans. The plan said “restore from backup.” Nobody had timed the restore. It took longer than the cutover window.

    Declaring victory at cutover. The migration succeeded; the optimization, decommissioning, and runbook work never happened. Two years later, they’re paying for two estates.

    Letting perfect be the enemy of moved. Some teams spend years designing the ideal target architecture while the legacy estate decays. A good migration executed beats a perfect migration planned.

    Forgetting the people. DBAs whose identity is tied to the old platform need a path to the new one — training, involvement, ownership. Technology migrations fail on people more often than on technology.

    The Bottom Line

    Database modernization is a program, not a project: assess honestly, decide per workload, execute in waves, and operate deliberately afterward. The technology choices matter less than the discipline — the teams that inventory carefully, validate relentlessly, and finish the unglamorous last 10% are the ones whose migrations actually pay off.

    If you’re staring at a legacy estate and wondering where to start — or you’re mid-migration and something feels off — reach out through the contact page. I’ve been through this at scales most teams never see, and I’m happy to help you think through yours.

    Related: case studies on enterprise migration modernization.

  • The Real Cost of Cloud Migration — and How to Control It

    Every cloud migration I have ever seen has two budgets: the one in the business case, and the real one. The gap between them is where careers get uncomfortable. It is rarely caused by one big mistake. It is caused by a dozen small leaks that nobody owned — over-provisioned instances, architectures chosen for speed instead of cost, license traps, and the quiet sprawl of resources nobody remembers spinning up.

    I have spent 10 years at a global cloud provider working on massive application and data migrations, and I have led migration programs from both sides of the table. This post is about where migration money actually goes, and how to keep control of it.

    Where Migration Budgets Actually Leak

    Let me walk through the leaks I see most often, roughly in order of how much damage they do.

    Lift-and-shift waste. The fastest way to migrate is to move workloads as-is. It is also the most expensive way to run them. An on-premises server sized for peak-plus-headroom becomes a cloud instance billed by the hour at that same generous size — except now the headroom costs real money every month, forever. Lift-and-shift is a valid migration strategy; it is a terrible operating strategy. The mistake is treating the migration as finished at cutover instead of as step one of optimization.

    Over-provisioning by default. Engineers size for safety. Without cost feedback, “safe” means the next size up. Multiply that instinct across hundreds of workloads and you are paying a meaningful premium for comfort. I have seen estates where the majority of instances averaged a small fraction of their provisioned capacity. That gap is pure waste, and it is invisible until someone measures it.

    Idle and forgotten resources. Dev and test environments that never shut down. Proof-of-concept infrastructure from a project that ended two years ago. Snapshots nobody will ever restore. Storage volumes detached from deleted instances, billing quietly in the background. At enterprise scale, this category alone can fund a team.

    Data transfer. Everyone models compute and storage. Far fewer model data transfer — between regions, between availability zones, and out to the internet. For data-heavy workloads, transfer costs can rival compute. Architecture decisions made without transfer pricing in mind (chatty cross-region replication, for example) create bills that surprise everyone.

    The long tail of “temporary.” Migration tooling, jump hosts, temporary databases for validation, dual-running old and new environments “just until we’re sure.” Each is justified individually. Collectively, they extend the period where you are paying for two estates instead of one. The old estate must have a decommission date, or “temporary” becomes permanent.

    Architecture Decisions Are Cost Decisions

    Here is the uncomfortable truth: by the time the FinOps team sees the bill, the costs were already decided — months earlier, in architecture reviews where nobody asked about money. Cost is not a billing problem. It is a design problem.

    Every major architecture choice has a cost dimension. Managed database services versus self-managed on compute instances: the managed service looks more expensive per hour until you count the engineering time, the patching, the failover testing, and the 3 AM pages. Sometimes self-managed wins; sometimes it loses badly. The point is that the comparison has to happen before the decision, not after the invoice.

    Compute architecture is one of the biggest levers available. I have led complete migrations from traditional x86-based estates to ARM-based processor architectures — the kind of fleet-wide architecture change that sounds terrifying and turns out to be methodical work: assess workload compatibility, migrate in waves, validate performance at each step. The economics are compelling because processor choice affects every workload, every hour. It is the rare optimization that compounds across the entire estate instead of applying to one application.

    Storage tiering is another. Production databases on high-performance storage, fine — but backups, logs, and infrequently accessed data on the same tier is money left on the table. Lifecycle policies that move data to cheaper tiers automatically are the difference between a policy and a wish.

    The principle: put a cost line on every architecture decision document. Not a precise forecast — a directional one. “This choice costs roughly X relative to the alternative, because Y.” It takes ten minutes and changes the conversation permanently.

    The License Trap

    Licensing deserves its own section because it ruins more migration business cases than any technical issue.

    Enterprise software licensed per core, per processor, or per user was priced for a world of fixed on-premises hardware. In the cloud, where cores are elastic and instances come and go, the old licensing math breaks — usually against you. I have seen organizations discover mid-migration that their database licenses, perfectly economical on owned hardware, became punitive when mapped to cloud instance sizing.

    The traps to watch: licenses that don’t transfer to the cloud the way you assumed (read the actual terms, not the sales deck); “license-included” managed services that look expensive until you price the alternative honestly; and the worst one — migrating first and doing license analysis second, when your negotiating leverage is gone.

    Do the license analysis during the assessment phase, workload by workload. For each one, the question is simple: what does this license cost here versus there, including every alternative (bring-your-own-license, license-included services, open-source substitution, rearchitecting away from the licensed product)? This analysis has saved programs I have worked on from genuinely painful surprises. It is unglamorous work. Do it anyway.

    Building Cost Accountability Into the Program

    Knowing where money leaks is not enough. Someone has to own stopping the leaks, continuously. That is what a working FinOps practice looks like inside a migration program — not a dashboard, but accountability.

    Tag everything, enforce it technically. A tagging policy that lives in a wiki is decoration. Enforcement means provisioning fails when required tags are missing. A minimal starting point:

    {
      "required_tags": ["owner", "cost-center", "migration-wave", "environment"],
      "enforcement": "deny-provisioning-if-missing",
      "review_cadence": "weekly"
    }

    Four tags. With them, you can answer “who spent what on which wave” — without them, you are guessing.

    Review cost per migration wave. Don’t wait for quarterly business reviews. After each wave of workloads migrates, review its actual cost against the estimate within weeks. Variances found early are corrections; variances found at year-end are explanations.

    Showback before chargeback. Start by showing teams what they spend. Most engineers have never seen the bill for their architecture choices, and the first time they do, behavior changes without any mandate. Chargeback (actually billing internal teams) is stronger medicine — use it where showback doesn’t move the needle.

    Assign a cost owner per workstream. Not a committee. A named person whose job includes the cost outcome of their migration wave, with cost as an explicit success criterion alongside timeline and functionality. What gets owned gets managed.

    Right-size on a schedule, not on courage. Build a recurring right-sizing review into the operating model — monthly for the first six months post-migration, quarterly after. Right-sizing is not a one-time event; workloads change, and the estate drifts back toward waste without it.

    Don’t Buy Discounts Before You Know Your Shape

    One more lever deserves attention because the timing matters: committed-use discounts and reserved capacity. Cloud providers will sell you significant discounts in exchange for committing to a level of usage — and for stable, well-understood workloads, this is straightforward savings.

    The trap is committing too early. Teams fresh off a migration, still running oversized lift-and-shift instances, lock in discounts on a footprint they haven’t optimized yet — and then spend the next one to three years getting a great price on waste. The right sequence is: migrate, optimize (right-size, tier storage, kill the idle), then commit on the stable remainder. The discount applies to whatever you commit to, so make sure you’re committing to the efficient version of your estate, not the just-migrated version.

    Related: savings plans and reservations are a finance decision as much as a technical one. They need someone tracking utilization of the commitment itself — an underutilized reservation is just waste with a discount label. Review commitment coverage quarterly, same cadence as right-sizing. The two practices belong together.

    What Good Looks Like

    A migration program with cost under control doesn’t look dramatic. It looks boring: estimates that hold, waves that land near their cost targets, a decommissioned old estate, and a FinOps review that takes thirty minutes because there are no surprises.

    Getting there requires treating cost as a first-class migration metric from day one — alongside timeline, scope, and risk. It requires architecture reviews that ask about money. And it requires the discipline to optimize after cutover instead of declaring victory and moving on.

    The organizations that do this well share one trait: someone senior decided that the migration’s success would be measured in part by its economics, and said so out loud, early. Everything else — the tagging, the wave reviews, the right-sizing — follows from that decision.

    The Bottom Line

    Cloud migration costs are controllable, but only if cost is designed in, not bolted on. Audit your licenses before you migrate, put cost on every architecture decision, enforce tagging technically, review each wave’s spend while you can still correct it, and never let “temporary” infrastructure become permanent.

    If your migration budget is leaking — or you’re planning a migration and want the business case to survive contact with reality — reach out through the contact page. This is the part of migrations I’ve spent years getting right, and I’m glad to help you think through yours.

    Related: case studies, including a fleet-wide compute cost migration.