Blog
Articles
How to Catch Data Pipeline Breaking Changes Before They Ship

How to Catch Data Pipeline Breaking Changes Before They Ship

Articles
June 17, 2026
Team Foundational
Subscribe to our Newsletter
Get the latest from our team delivered to your inbox
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Ready to get started?
Try It Free

A breaking change in a data pipeline rarely announces itself. A renamed column in a Postgres table, a dropped field in an upstream API response, a dbt model that changes its grain, a Spark job that silently starts null filling a column it used to populate. None of these throw an error at deploy time. They surface hours or days later as a broken dashboard, a failed ML feature, or a support ticket, by which point the root cause is buried under every commit made since. Catching these changes before they ship means analyzing the dependency graph at the code level, before the pull request merges, not waiting for a downstream symptom to trigger an investigation. This post walks through where breaking changes actually originate, what it takes to catch them pre merge, and how that changes the cost of a schema change from an incident to a code review comment.

Where Breaking Changes Actually Originate

Breaking changes rarely start in SQL. They start in the code that produces the SQL: a Python transformation that reshapes a dataframe, an ORM model in Java or Python that changes how an application writes to its database, a dbt model whose grain shifts after a join is added, or a Spark job whose schema drifts after a library upgrade. By the time a query runs against the resulting table, the change is already baked in. Catching it there means the incident has already started. Catching it in the code that produced the change, before the pull request merges, is what actually prevents it.

What a Pre Merge Impact Check Needs to See

A useful pre merge check needs visibility into more than the file being changed. It needs the full downstream dependency graph, built from actual code analysis rather than a static schema snapshot, covering the languages and layers a given team actually uses.

LayerWhat breaks here
SQL and dbt modelsRenamed or dropped columns, changed grain, altered join keys
Python and Spark pipelinesReshaped dataframes, silent type coercion, dropped fields in a transformation step
ORM layers (Java, Python)Application level schema changes that never touch a migration script reviewed by the data team
AI and feature pipelinesA feature definition change that silently alters what a model is trained or served on

Without coverage across all four, a pre merge check only catches the changes that happen to route through SQL, which in most engineering organizations is a shrinking share of where pipeline logic actually lives.

What Good Pre Merge Detection Looks Like

Foundational, a data and AI governance platform, analyzes pipeline source code directly rather than relying on query logs or a static catalog snapshot, so a schema or logic change is checked against its actual downstream impact at the pull request stage, across SQL, Python, Spark, and ORM layers alike. At Lightricks, this kind of pre merge analysis prevents roughly 100 issues each month across more than 150 pull requests, according to Eyal El-Bahar, VP of BI and Analytics. At Vio, a similar approach cut potential issues by 52 percent and reduced cycle time by 37.5 percent.

Frequently Asked Questions

How do you catch data pipeline breaking changes before they ship?

Catching a breaking change before it ships requires analyzing the downstream dependency graph at the code level, covering SQL, Python, Spark, and ORM layers, and running that check at the pull request stage rather than after deployment. That requires source code analysis rather than query log parsing, since many breaking changes originate in pipeline or application code that never appears in a query log.

What causes most data pipeline breaking changes?

Most breaking changes originate outside SQL: a Python transformation that reshapes a dataframe, an ORM model change in the application layer, or a Spark job whose schema drifts after a dependency upgrade. By the time the resulting change reaches a warehouse query, the root cause is already several steps removed.

Can dbt tests catch breaking changes before they ship?

dbt tests catch violations of rules you have already defined for models you have already written. They do not catch a breaking change introduced upstream in a Python pipeline or an application's ORM layer, since those changes happen outside dbt's model graph entirely.

What is the cost difference between catching a breaking change pre merge versus in production?

A breaking change caught in code review costs a comment and a fix before merge. The same change caught in production costs an incident: a broken dashboard, a corrupted downstream table, or a support escalation, plus the time spent tracing the root cause back through every commit made since the change shipped.

Building This Into Your Workflow

Catching breaking changes before they ship is a prevention problem, not a monitoring problem, and it depends on visibility into the pipeline and application code where most of these changes actually originate. See how this fits into a broader data incident prevention strategy, or explore Foundational's approach to data governance end to end.

code snippet <goes here>
<style>.horizontal-trigger {height: calc(100% - 100vh);}</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/ScrollTrigger.min.js"></script>
<script>
// © Code by T.RICKS, https://www.timothyricks.com/
// Copyright 2021, T.RICKS, All rights reserved.
// You have the license to use this code in your projects but not to redistribute it to others
gsap.registerPlugin(ScrollTrigger);
let horizontalItem = $(".horizontal-item");
let horizontalSection = $(".horizontal-section");
let moveDistance;
function calculateScroll() {
 // Desktop
 let itemsInView = 3;
 let scrollSpeed = 1.2;  if (window.matchMedia("(max-width: 479px)").matches) {
   // Mobile Portrait
   itemsInView = 1;
   scrollSpeed = 1.2;
 } else if (window.matchMedia("(max-width: 767px)").matches) {
   // Mobile Landscape
   itemsInView = 1;
   scrollSpeed = 1.2;
 } else if (window.matchMedia("(max-width: 991px)").matches) {
   // Tablet
   itemsInView = 2;
   scrollSpeed = 1.2;
 }
 let moveAmount = horizontalItem.length - itemsInView;
 let minHeight =
   scrollSpeed * horizontalItem.outerWidth() * horizontalItem.length;
 if (moveAmount <= 0) {
   moveAmount = 0;
   minHeight = 0;
   // horizontalSection.css('height', '100vh');
 } else {
   horizontalSection.css("height", "200vh");
 }
 moveDistance = horizontalItem.outerWidth() * moveAmount;
 horizontalSection.css("min-height", minHeight + "px");
}
calculateScroll();
window.onresize = function () {
 calculateScroll();
};let tl = gsap.timeline({
 scrollTrigger: {
   trigger: ".horizontal-trigger",
   // trigger element - viewport
   start: "top top",
   end: "bottom top",
   invalidateOnRefresh: true,
   scrub: 1
 }
});
tl.to(".horizontal-section .list", {
 x: () => -moveDistance,
 duration: 1
});
</script>

Catch Breaking Changes Before They Ship

See how source code analysis flags pipeline breaking changes at the pull request.

Catch Breaking Changes Before They Ship

See how source code analysis flags pipeline breaking changes at the pull request.

Catch Breaking Changes Before They Ship

See how source code analysis flags pipeline breaking changes at the pull request.

Share this post
Subscribe to our Newsletter
Get the latest from our team delivered to your inbox
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Ready to get started?
Try It Free

Govern data and AI at the source code