The failures that hurt most are the ones that don’t fail. A source system changes a column from an integer to a string, a join stops matching a few percent of rows, and every run stays green while the numbers drift.
Why tests don’t catch it
dbt tests check what you thought to check: not null, unique, accepted values, relationships. Schema drift tends to slip between them. A type change doesn’t make a column null. A dropped column that you never selected doesn’t break anything until someone needs it. A new enum value passes if nobody wrote an accepted-values test.
What to watch instead
- Column names and types in every source, compared run to run.
- Row counts per model against their usual pattern, not a fixed number.
- Join match rates on the joins that matter, such as orders to customers.
None of these need to read your data. Information schema and run results are enough for the first two, and a single count query handles the third.
If a row count drops by 2% and stays there, that is not noise. That is a join that stopped matching.
A real example
One of our customers, a district cooling provider, had a meter vendor change a reading identifier from an integer to a string. An inner join quietly dropped about 2% of readings for three weeks. Volume checks spotted the step change on the first day, and the fix was a one-line cast plus a row-count test.
If you do one thing after reading this, add a row-count check to the three models your finance team reads first. It is the cheapest insurance in data engineering.