Data Quality: What Can Actually Go Wrong
You’ve built an elegant data pipeline. Your data model is clean. Queries are fast. Your dashboard shows revenue up 50% this quarter. The Business controller makes decisions based on it.
And then, three weeks later, you discover: the revenue calculation was wrong the whole time because someone entered “500,000” as “500,00” in a legacy system, and nobody caught it.
That’s a data quality problem. And here’s the scary part: nobody noticed until you dug deeper. Your pipeline ran perfectly. Your warehouse loaded perfectly. Your SQL was correct. But the data itself was broken — silently, invisibly, for weeks.
Think of it like a supply chain
Imagine you’re running a restaurant and ordering ingredients.
- Good supply chain: You order 100 tomatoes. They arrive. You inspect them — most look good, but you spot 3 that are rotten. You remove them. You use 97 good tomatoes. Dinner is great.
- Bad supply chain: You order 100 tomatoes. They arrive in unmarked boxes. You dump them in the fridge without checking. You don’t know if any are rotten until a customer bites into a bad one during dinner. Now you have an angry customer and a problem.
Your data pipeline is the supply chain. If you don’t check quality before it reaches your customers (dashboards, reports, decisions), you’ll serve them rotten data without knowing it.
What Is Data Quality?
Data quality is about having accurate, complete, consistent, and timely data that you can trust. It answers: Can I rely on this data to make decisions?
A dataset has good quality when:
- Accurate: Data matches reality. 10 sales really means 10 sales, not a data entry error.
- Complete: All required data is present. You have a phone number for every customer (or you know it’s missing).
- Consistent: Data follows standards. Dates are always YYYY-MM-DD format, not mixed between MM-DD-YYYY and other formats.
- Timely: Data reflects the current state. Yesterday’s sales show up today, not a month later.
- Valid: Data meets constraints. An age isn’t -5 or 1000. An email looks like an email.
When any of these fail, you have data quality issues.
What Can Go Wrong? Common Data Quality Problems
1. Missing or Null Values
Data simply isn’t there.
Customers table:
| customer_id | name | email | phone |
|---|---|---|---|
| 1001 | Alice Chen | alice@email.com | NULL |
| 1002 | Bob Smith | NULL | 555-1234 |
| 1003 | Carol Lee | carol@email.com | 555-5678 |
What goes wrong: You send a marketing email to all customers. Two of them never receive it because their email is missing. Your email campaign metrics are wrong.
Why it happens:
- Old data where the field didn’t exist yet
- User left the field blank during signup
- Data extraction failed for that field
- Integration bug dropped data
2. Duplicates
Same data appears multiple times.
Customers table:
| customer_id | name | email |
|---|---|---|
| 1001 | Alice Chen | alice@email.com |
| 1002 | Alice Chen | alice@email.com | ← Duplicate!
| 1003 | Bob Smith | bob@email.com |
What goes wrong: You calculate total customers as 3, but really there are only 2. You think Alice is two people and send her two birthday emails.
Why it happens:
- Manual data entry mistakes
- Merging data from multiple sources
- Bugs in ETL pipelines that copy data twice
- System doesn’t enforce uniqueness
3. Inconsistent Formats
Same type of data stored in different formats.
Orders table:
| order_date | amount |
|---|---|
| 2024-08-12 | 100.50 |
| 08/12/2024 | 100.5 | ← Different format!
| 12-Aug-2024 | $100.50 | ← Different format!
What goes wrong: When you query “orders from August,” you get different results depending on which date format the data was stored in. Your sum of amounts is wrong because $100.50 gets treated as text, not a number.
Why it happens:
- Data comes from multiple sources (different systems store dates differently)
- No validation when data is entered
- Manual data entry from different people using different formats
- Legacy systems with old formats
4. Outliers and Anomalies
Data that doesn’t make sense in context.
Customer spending:
| customer_id | annual_spending |
|---|---|
| 1001 | $2,500 |
| 1002 | $3,200 |
| 1003 | $9,999,999 | ← Wait, what?
| 1004 | $1,800 |
What goes wrong: Your average customer value calculation is way off. You’re focusing on one “huge” customer who probably entered “9999999” as a typo instead of “99.99.”
Why it happens:
- Typos in data entry (extra zeros, decimal points in wrong places)
- System accepting data outside reasonable bounds
- Currency conversion errors
- Sensor/API errors producing garbage values
5. Stale Data
Data that’s outdated or obsolete.
Yesterday's pipeline ran successfully.
Today's new data never loaded because the source system went down.
Your dashboard shows yesterday's data.
But your Business controller thinks it's today's data.
They make decisions based on incomplete information.
What goes wrong: A dashboard looks current but is actually 24 hours old. You don’t know the difference until things fall apart.
Why it happens:
- Pipeline failed silently (nobody was monitoring it)
- Data source went offline
- ETL job got stuck and nobody noticed
- No alerts in place to detect missing data
6. Violating Business Rules
Data that’s technically valid but breaks your business logic.
Orders table:
| order_id | order_date | delivery_date |
|---|---|---|
| 101 | 2024-08-12 | 2024-08-10 | ← Delivered before ordered?!
| 102 | 2024-08-12 | 2024-08-12 |
| 103 | 2024-08-01 | 2024-09-15 |
What goes wrong: Your analysis of delivery times is garbage because order 101’s data makes no sense. When did it actually ship?
Why it happens:
- Incorrect manual corrections
- Bug in application code
- Data loaded out of order
- Business rules changed but old data wasn’t updated
How Bad Data Spreads Through Your System
flowchart LR
A["Bad Data<br/>Enters Source System"] -->|Extracted| B["Pipeline"]
B -->|Loaded| C["Warehouse"]
C -->|Queried| D["Dashboard"]
D -->|Business controller sees it| E["Wrong Decision"]
The scary part: Each step looks fine. The pipeline ran without errors. The warehouse loaded without errors. The SQL query ran without errors. But bad data contaminated everything downstream.
This is why data quality is so critical — bad data doesn’t announce itself. It flows silently through your system like a virus.
Catching Data Quality Problems
1. Schema Validation
Check that data matches expected format.
Customer email should be in format: word@word.com
Customer age should be between 0 and 150
Order amount should be positive number
Where to implement: During extraction or loading. Reject data that doesn’t fit.
2. Completeness Checks
Verify required fields aren’t missing.
"SELECT COUNT(*) FROM customers WHERE email IS NULL"
If this returns > 0, you have a problem.
3. Uniqueness Checks
Look for duplicates.
"SELECT customer_id, COUNT(*) FROM customers
GROUP BY customer_id HAVING COUNT(*) > 1"
If this returns anything, you have duplicates.
4. Range Checks
Catch outliers and impossible values.
"SELECT * FROM orders WHERE order_amount < 0
OR order_amount > 1000000"
Negative amounts and million-dollar orders from tiny customers = problems.
5. Consistency Checks
Validate data across related tables.
"SELECT * FROM orders WHERE customer_id NOT IN
(SELECT customer_id FROM customers)"
If this returns anything, you have orders for non-existent customers.
6. Alerts on Missing Data
Monitor your pipeline and alert if data stops flowing.
"If yesterday's pipeline ran but loaded 0 rows, send alert"
"If latency > 24 hours, send alert"
A Real-World Scenario
flowchart TD
A["Sales Team Enters Orders<br/>into Legacy System"] -->|Friday 5pm<br/>Batch extract| B["Raw Data File<br/>CSV with mixed date formats"]
B -->|Saturday night<br/>ETL job| C["Warehouse loads<br/>10,000 orders"]
C -->|Sunday morning| D["Dashboard updates<br/>Shows $4.2M revenue"]
E["Business controller checks dashboard<br/>Monday morning"] -->|Makes decision| F["Plans expansion based<br/>on $4.2M revenue"]
D --> E
G["Tuesday morning<br/>Data engineer checks logs"] -->|Finds| H["500 orders have null<br/>amounts because field<br/>was optional"]
G -->|Finds| I["200 duplicate orders<br/>from system error Friday"]
H -->|Actual revenue| J["$3.1M not $4.2M"]
I -->|Actual revenue| J
J -->|Oops| K["Wrong decision made<br/>based on wrong data"]
F -.->|depends on| K
The real revenue was $3.1M, not $4.2M. But nobody caught it for 4 days. In that time, the Business controller committed to an expansion plan based on bad data.
Preventing Silent Breakage
The key is building data quality checks into your pipeline, not just discovering problems after the fact.
flowchart LR
A["Raw Data"] -->|Validate:| B["Quality Checks<br/>Run automatically"]
B -->|All good| C["Load to Warehouse"]
B -->|Problems found| D["Alert Data Team<br/>Block bad data"]
D -->|Investigate & Fix| E["Retry once fixed"]
E --> C
C -->|Now trusted| F["Dashboard & Reports"]
A Simple Mental Model
| Problem | Impact | Detection | Prevention |
|---|---|---|---|
| Missing values | Incomplete analysis | COUNT NULLs | Require field on input |
| Duplicates | Wrong counts, double-counting | GROUP BY & HAVING COUNT>1 | Enforce uniqueness constraint |
| Format inconsistencies | Query fails or wrong results | Sample & inspect data | Standardize on input |
| Outliers | Skewed averages, wrong insights | Statistical checks | Validate against business rules |
| Stale data | Old insights treated as current | Monitor pipeline latency | Alert if data doesn’t arrive |
| Business rule violations | Nonsensical data | Consistency checks across tables | Add constraints in warehouse |
The Takeaway
Bad data doesn’t need to crash your system to break it. It silently spreads through your pipeline, gets loaded into your warehouse, powers your dashboards, and influences decisions — all without anyone noticing until it’s too late. That’s why data quality isn’t an afterthought; it’s a core part of every data model and pipeline. Build validation checks that run automatically. Monitor for stale or missing data. Catch problems before they reach dashboards. Because garbage data doesn’t just mean bad reports — it means bad decisions. And bad decisions are far more expensive than preventing bad data in the first place.