Why DevOps for Data?

A hand-edited production view, no history, no rollback. The story of why data teams need GitOps — and how Git, dbt, and Snowflake fit together.

  • #dbt
  • #snowflake
  • #gitops
  • #data-modeling
Part 1 of 5— GitOps with Snowflake & dbt
  1. Why DevOps for Data?
  2. Environment Setup: dbt + Snowflake Basics
  3. Version Control: Monorepo & CODEOWNERS
  4. Automating with CI: GitHub Actions for dbt
  5. Full GitOps Flow: Deploy, Promote, Rollback

It was Elin Björk’s fourth day as an Analytics Engineer at Nordicvinter Retail, a mid-size Nordic retail chain, when she watched her new manager Mikael go pale at his desk.

“These numbers can’t be right,” he muttered, flipping between two dashboard tabs. “Revenue is way off from last month’s readings. Either something’s gone bad in the data overnight, or…” He looked up. “Elin, last week Johan mentioned that he’d made some changes to revenue for a stakeholder request from our sales team, right?”

“Yes, just a quick fix,” Elin called back from across the office. “Johan mentioned that the Monday leadership numbers looked wrong, so he opened the query in a Snowflake worksheet and patched the join. Numbers looked right after that, and the sales team confirmed the accuracy.”

“Can we see what he changed?”

Silence.

The only person who knew exactly what had changed was Johan. And Johan’s two-week vacation had started on Friday — he was hiking somewhere in the fjords, phone off, unreachable until August. His fix existed in exactly one place: his memory.

There was no diff to look at. No history. No record of what the query looked like a week ago versus now. Johan had logged into Snowflake, edited a production view directly, and run it — the same way you might edit a spreadsheet cell. It fixed Monday’s number. It also quietly broke a downstream aggregation that three other reports depended on, and nobody would find out until Thursday.

Mikael’s next question was the one that mattered: “Can we roll it back?”

Roll back to what? There was no “before” saved anywhere.

Elin, Johan, Mikael, and Nordicvinter Retail are fictional — composites of teams I've worked with over the years. The incident, unfortunately, is entirely real and happens everywhere.

If you’ve ever fixed a data problem by editing production directly — and most of us have — this series is for you. Almost every data team hits this moment eventually, and it’s exactly the problem GitOps was built to solve.

Why I’m Writing This

I’ve spent close to a decade automating data pipelines, and I’ve been on both sides of that Monday morning — the person who patched production by hand, and later the person asking “can we roll it back?” That’s what turned me into a committed promoter of DevOps for data modeling: not the theory, but watching the same incident play out on team after team until the process changes.

These days the setup I keep coming back to is Snowflake and dbt for taming the complexity of data modeling, with deployment and quality checks handed off entirely to CI/CD automation. Humans design the models and review the diffs; the pipeline does the deploying and the testing. Once you’ve worked that way, going back to worksheet-edited production feels like handing out root access.

That’s what this series demonstrates, end to end: by the final part you’ll have built a functional CI/CD pipeline for a real data model — from an empty folder to automated, tested deployments. In this first post, we start with the foundation: what GitOps actually means, how Git, dbt, and Snowflake divide the work between them, and what Johan’s “quick fix” would have looked like on a team that works this way.

What GitOps Actually Means

GitOps isn’t a Snowflake feature or a dbt feature. It’s a discipline borrowed from software engineering, and the core idea is simple:

The desired state of your system lives in Git, not in someone’s head or in a live console.

Instead of logging into Snowflake and editing a view by hand, you edit a file in a Git repository. That file describes what the view should look like. A separate, automated process reads that file and makes Snowflake match it. If something breaks, you don’t guess — you look at the commit history and see exactly what changed, when, and who changed it.

That’s the whole idea. Everything else in this series is really just “how do we make that true in practice, with Snowflake and dbt specifically.”

The Fix That Should Have Happened

Abstract principles are easy to follow, so let’s make it concrete. Here’s what Johan’s Monday morning looks like on a team practicing GitOps.

The revenue view isn’t code living inside Snowflake — it’s a file in a shared repository. Johan opens that file, and his fix is a one-line change:

-- models/accounting_dm/org_revenue.sql
   from sales_orders o
-  join payments p
+  left join payments p
     on p.order_id = o.order_id

He saves it as a commit with a message explaining why:

Fix revenue undercount: keep orders with no payment row yet

The inner join dropped orders placed over the weekend whose payments
hadn't settled. Leadership dashboard showed Monday revenue ~8% low.

Then he opens a pull request. Before anything touches production, two things happen automatically: a CI pipeline builds the change in an isolated test environment and runs the data tests — including the ones guarding those three downstream reports — and a teammate looks at the diff. The broken aggregation that originally went unnoticed until Thursday? A failing test catches it in four minutes, on the pull request, before production ever changes.

And Mikael’s question — “can we roll it back?” — becomes boring. Every previous version of the view is a commit in the history. Rolling back is reverting a commit, not reconstructing a query from memory.

Same fix, same ten minutes of work. The difference is that every safeguard is part of the workflow instead of depending on Johan having a careful day.

Vocabulary You’ll Need (No Prior Git Experience Assumed)

A handful of terms will come up constantly, so let’s define them once, plainly:

  • Repository (“repo”) — a folder whose entire history is tracked. Every change is saved as a checkpoint you can return to.
  • Commit — a saved checkpoint, with a message describing what changed and why.
  • Branch — a parallel copy of the repo where you can make changes without touching the main version yet.
  • Pull Request (PR) — a request to merge your branch’s changes into the main branch, usually after someone reviews it.
  • CI/CD — automated processes that run when you push code (CI: continuous integration — testing your changes automatically; CD: continuous delivery/deployment — automatically applying approved changes).
  • Declarative vs. imperative — declarative means “here’s what the end result should look like” (a dbt model file); imperative means “here’s the exact sequence of steps to run” (an ad-hoc SQL script run by hand). GitOps is fundamentally declarative.

Where Snowflake and dbt Fit

Think of the whole system as four boxes in a line:

Developer → Git → CI/CD → Snowflake

  • Snowflake is the data platform — where data actually lives and where queries actually run.
  • dbt is the transformation layer — it’s how you define, in version-controlled files, what your tables and views should look like, using plain SQL plus some templating.
  • Git holds those dbt files as the source of truth.
  • CI/CD is the automation that takes what’s in Git and applies it to Snowflake, on a schedule or on every approved change.

Johan’s mistake, translated into this picture: he edited box four directly, skipping boxes two and three entirely. GitOps is the discipline of never doing that — every change flows through Git first.

A Quick Detour: Why Snowflake, Specifically?

Before going further, it’s worth knowing what else exists, since Snowflake is one of several major players in what’s broadly called the “cloud data warehouse” (or increasingly, “lakehouse”) space:

  • Snowflake — separates storage and compute so you pay for query power only when you’re actually querying. Runs on AWS, Azure, or GCP. Strong at handling many people querying at once without slowing each other down.
  • Databricks — built around the “lakehouse” idea, blending data lake flexibility with warehouse-style SQL. Popular where data science/ML workloads sit alongside analytics.
  • Google BigQuery — fully serverless; you don’t manage any infrastructure, and pricing is largely per-query. Tightly integrated with the rest of Google Cloud.
  • Amazon Redshift — AWS’s native warehouse, historically cluster-based (though a serverless option now exists). Common where a company is already deeply invested in AWS.
  • Azure Synapse Analytics — Microsoft’s warehouse offering, a natural fit for organizations already running Power BI, Azure, or Dynamics 365.

They all solve the same underlying problem — separating where data is stored from the compute used to query it — but differ in pricing model, cloud lock-in, and workload strengths. Nordicvinter chose Snowflake for its multi-cloud flexibility and because their team wasn’t already locked into one cloud provider. Your reasons might differ, and that’s fine — the GitOps principles in this series apply regardless of which warehouse sits in that fourth box.

The Takeaway

Three things worth carrying out of this post:

  • GitOps means the truth lives in Git. Production reflects what’s in the repo — never the other way around.
  • The safety comes from the workflow, not from discipline. Diffs, reviews, automated tests, and one-command rollbacks are what you get for free once changes flow through Git.
  • The tools each do one job. dbt describes what your tables should look like, Git remembers every version, CI/CD applies it to Snowflake. No single tool does GitOps — the pipeline between them does.

What’s Next

Watching Mikael panic over a change nobody could see, Elin didn’t conclude “work harder and keep it all in your head” — that was precisely what had just failed, and it was hiking in the fjords. What she took away was simpler and colder: we need a process where this kind of change is impossible to make by accident.

But before any of that process can exist, there needs to be something worth version-controlling in the first place.

In Part 2, we get hands-on: setting up a free Snowflake environment, installing dbt, and building the first model — deliberately without Git yet, so the two new concepts don’t blur together. Only once there’s a real dbt project will it make sense to put it under version control in Part 3.

Until then, here’s a question worth an honest answer: if someone changed a production view at your company this morning, would you be able to see the diff? If the answer is no, you’re exactly who this series is for.


Next: Part 2 — Environment Setup: dbt + Snowflake Basics