The failure mode that doesn't look like a failure

Most people picture a broken pipeline as a red X, a stack trace, a paging alert. The far more common, far more expensive failure looks like none of that: the job runs to completion, logs "success," and quietly produces garbage, because something upstream changed shape and nothing downstream was built to notice.

graph LR
    A[Upstream Source] -->|Field renamed,<br/>no announcement| B[Pipeline Code<br/>unchanged]
    B -->|record.get old_field_name| C[Returns None,<br/>no error raised]
    C --> D[Job Exits 0]
    D --> E[Dashboard shows<br/>a blank column]

A completely ordinary upstream change

An upstream API or source system renames a field, email_address becomes email, a routine cleanup on someone else's team, announced or not. A pipeline written against the old name:

def process_record(record):
    return {
        "id": record["id"],
        "email": record.get("email_address"),  # the field that just got renamed
        "amount": record["amount"],
    }

What actually happens when the upstream data changes shape

new_format_records = [
    {"id": 3, "email": "c@example.com", "amount": 60},
    {"id": 4, "email": "d@example.com", "amount": 90},
]
results = [process_record(r) for r in new_format_records]
{'id': 3, 'email': None, 'amount': 60}
{'id': 4, 'email': None, 'amount': 90}

real pipeline exit status: SUCCESS (no exception raised)
real null rate in 'email' column: 100%

.get("email_address") on a dictionary that no longer has that key doesn't raise an error, it returns None, quietly, by design, because that's exactly what .get() is built to do. The job runs to the end. The exit code is 0. Every downstream system that checks "did the pipeline succeed" gets a clean, honest "yes," while the actual data underneath is now 100% empty in the one column that just changed.

What "the job succeeded" actually tells you, here
Exit code check
passes
Actual data
100% null

Why exit codes alone were never enough

An exit code answers exactly one question: did the code run to completion without raising an exception. It says nothing about whether the data that came out the other end is any good, and a huge share of real, expensive data incidents live in exactly that gap, code that runs perfectly, on data that's silently wrong. Data engineering teams routinely report spending a large share of their time on exactly this category of problem, not crashes, quiet correctness failures discovered well after the fact, once someone downstream notices a report looks off.

The check that actually catches it

null_rate = df["email"].isna().mean()
THRESHOLD = 0.05

if null_rate > THRESHOLD:
    print(f"CHECK FAILED: null rate {null_rate:.0%} exceeds {THRESHOLD:.0%} threshold")
    raise SystemExit(1)
real null rate: 100%
REAL CHECK FAILED: null rate 100% exceeds 5% threshold -- pipeline halted

A five-line check, run immediately after the transform step, turns a silent, weeks-later discovery into an immediate, loud, same-day failure. The exit code is now 1, honestly reflecting that something is genuinely wrong, instead of 0 reporting a false all-clear.

The takeaway

A pipeline "succeeding" is a claim about the code, not the data, and the gap between those two things is exactly where this failure mode lives: real, silent, and expensive precisely because nothing about it looks like a failure until someone notices a number that doesn't add up. A direct, automated check on the data itself, not just the process that produced it, the same five lines shown here, is what turns an invisible multi-week gap into a same-day fix.