Drukarnia.BLOG

Your Dashboard Numbers Don't Match: A Developer's Guide to Debugging Data Pipelines

A dashboard shows 12,450 orders. The finance report shows 12,180. The operations team has a spreadsheet showing 12,392.

Everyone is looking at the same business, the same month, and supposedly the same data. Yet nobody agrees on the number.

This situation is more common than many engineering teams would like to admit.

When reports disagree, the first reaction is often to blame the BI tool, refresh the dashboard, or ask the database team to investigate. Sometimes that helps. Often, the actual problem is somewhere between the original transaction and the final report.

Data moves through multiple systems before it reaches a dashboard. It gets extracted, transformed, joined, filtered, aggregated, and sometimes loaded more than once. Every step introduces an opportunity for inconsistency.

The challenge is not simply moving data from one system to another. It is making sure that the meaning of the data remains consistent throughout the journey.

Let's explore the most common reasons dashboards disagree and how developers can investigate these problems systematically.

1. Start With the Definition of the Metric

Before investigating SQL queries, establish what the number is actually supposed to represent.

Consider a simple business metric: monthly revenue.

Different teams might calculate it differently.

The sales team may include all orders placed during the month. Finance might include only completed transactions. Operations may exclude cancelled orders but include orders that are still being fulfilled.

All three reports could be technically correct according to their respective definitions.

A revenue metric needs clear answers to questions such as:

  • Does revenue include taxes?

  • Are discounts deducted?

  • Are refunds subtracted?

  • Does the date represent order creation or payment completion?

  • Are cancelled orders excluded?

  • Are partial refunds handled separately?

  • Which currency conversion rate is used?

Without these definitions, developers end up comparing SQL queries that are solving slightly different problems.

A good practice is to maintain a central metric definition.

For example:

Monthly net revenue

The total value of completed customer payments during a calendar month, excluding taxes and subtracting refunds processed during the same reporting period.

The exact definition depends on the business, but everyone should use the same agreed-upon rules.

This is often the first step toward eliminating conflicting reports.

2. Incorrect Joins Can Multiply Your Records

One of the most common causes of incorrect reporting is join multiplication.

Consider two database tables.

Orders

order_id

customer_id

amount

101

10

500

102

11

750

103

12

300

Order Items

item_id

order_id

product

1

101

Laptop

2

101

Mouse

3

102

Keyboard

4

103

Monitor

An order can contain multiple items.

Now consider this query:

SELECT
    SUM(o.amount) AS total_revenue
FROM orders o
JOIN order_items i
    ON o.order_id = i.order_id;

The expected revenue is:

500 + 750 + 300 = 1,550.

But the query returns:

500 + 500 + 750 + 300 = 2,050.

The first order appears twice because it contains two items.

The join itself is valid. The problem is that the query is aggregating an order-level value after joining it to a table with a different level of detail.

This is known as join fanout.

How to identify join multiplication

Compare the number of rows with the number of distinct business identifiers.

SELECT
    COUNT(*) AS total_rows,
    COUNT(DISTINCT o.order_id) AS unique_orders
FROM orders o
JOIN order_items i
    ON o.order_id = i.order_id;

If the two numbers differ, investigate the relationship between the tables.

How to prevent it

The solution depends on the intended reporting grain.

You can aggregate the child table before joining, calculate revenue from the order table separately, or build a model that explicitly preserves the required grain.

For example:

SELECT
    SUM(amount) AS total_revenue
FROM orders
WHERE status = 'completed';

This works when the intended metric is order-level revenue.

The important lesson is to understand the grain of every table before combining them.

A technically valid SQL query does not automatically produce a correct business metric.

3. Duplicate Records Can Quietly Corrupt Reports

Duplicate records are another common problem in analytics pipelines.

They can appear for several reasons:

  • An API request is retried after a timeout.

  • A scheduled job processes overlapping time windows.

  • A source system exports the same record more than once.

  • A batch load restarts after partial completion.

  • An event is delivered multiple times.

  • A database synchronization process repeats previously processed changes.

The dangerous part is that duplicates do not always produce obvious errors.

A dashboard can continue working while revenue, customer counts, or transaction volumes gradually become inaccurate.

Detecting duplicates

Suppose every order should have a unique order identifier.

You can identify duplicates with:

SELECT
    order_id,
    COUNT(*) AS record_count
FROM orders
GROUP BY order_id
HAVING COUNT(*) > 1;

This identifies repeated identifiers, but it does not tell you which record should be retained.

That requires a business rule.

For example, if the source provides a reliable update timestamp, you might choose the latest version.

WITH ranked_orders AS (
    SELECT
        *,
        ROW_NUMBER() OVER (
            PARTITION BY order_id
            ORDER BY updated_at DESC
        ) AS row_num
    FROM orders
)
SELECT *
FROM ranked_orders
WHERE row_num = 1;

This is only safe when the latest timestamp represents the authoritative version and timestamp ties are handled deterministically. In a production pipeline, include a stable tie-breaker such as a source version or ingestion sequence.

Make ingestion idempotent

An idempotent process produces the same final state when the same operation is executed more than once.

This is important because real pipelines retry operations.

Rather than blindly inserting every incoming record, use a stable source identifier and an appropriate upsert strategy.

For event-based systems, deduplicate using a stable event identifier rather than assuming that two events with identical payloads are necessarily duplicates.

4. Incremental Data Loading Can Miss Important Changes

Full data extraction is straightforward conceptually: read the entire source dataset and load it into the destination.

But doing that every time becomes expensive as the dataset grows.

Most production systems use incremental loading.

Instead of loading everything, the pipeline processes records that have changed since the previous execution.

A common approach uses an update timestamp.

SELECT *
FROM customers
WHERE updated_at > :last_processed_timestamp;

This looks reasonable, but there are edge cases.

Imagine a customer record was last modified on Monday. On Thursday, an upstream correction changes that record, but the extraction process only reads newly created records.

The correction may never reach the analytics system.

Another problem appears when multiple records share the same timestamp or when a job fails after partially processing a batch.

Better incremental loading strategies

Use a reliable change marker

Prefer a source version, database log position, or another reliable change indicator where available.

Use controlled lookback windows

Reprocess a recent time range to catch delayed updates and timestamp-boundary issues.

Handle deletions explicitly

A record disappearing from a source does not automatically mean the destination will remove it. Deletion events or reconciliation logic are necessary.

Use upserts

Update existing records rather than assuming every incoming record is new.

Maintain checkpoints

Record the last successfully processed position, not merely the last attempted execution time.

For higher-volume systems, Change Data Capture (CDC) can provide a more reliable way to capture inserts, updates, and deletes from database changes.

The right approach depends on source capabilities, latency requirements, and the consequences of missing historical corrections.

5. Time Zones Can Change Your Monthly Numbers

Time-related problems are particularly frustrating because the underlying data may be correct.

Consider a transaction created at:

2026-11-01 00:30:00 UTC

In India, the same timestamp is:

2026-11-01 06:00:00 IST

Now consider a report operating in another business time zone.

A transaction near midnight might belong to a different reporting date depending on the time zone applied.

This becomes particularly important for:

  • Monthly revenue reports.

  • Daily order counts.

  • Subscription renewals.

  • Financial reconciliation.

  • Customer activity reports.

  • Global applications operating across multiple regions.

Use an explicit time-zone strategy

A practical approach is to:

  1. Store event timestamps in a consistent timezone, commonly UTC.

  2. Preserve the original event time and relevant source information.

  3. Convert timestamps to the appropriate business timezone when calculating reporting periods.

  4. Use consistent date-boundary rules.

  5. Document whether metrics use event time, processing time, or business date.

For example, if a report is intended to show November revenue in India, the reporting boundaries should be calculated using the Indian business timezone and then converted into the appropriate database timestamp boundaries.

Do not simply assume that a database server's local timezone matches the business reporting timezone.

6. Late-Arriving Data Can Change Historical Reports

Not every record arrives immediately.

Consider an online payment made on November 10.

The payment provider may send a settlement update on November 11. A refund could be processed on November 12. The final financial status may therefore become available days after the original transaction.

If a report only reads data once, it might never reflect these updates.

This is known as late-arriving data.

It can affect:

  • Payment reconciliation.

  • Financial reporting.

  • Customer activity analysis.

  • Inventory updates.

  • Delayed event processing.

  • Historical operational reports.

Use watermarks and reconciliation windows

A watermark represents how far a pipeline believes it has processed data.

However, a watermark should not be treated as proof that earlier data can never change.

One practical approach is to reprocess a rolling time window.

For example, a daily reporting pipeline might reprocess the previous few days to capture delayed updates.

The appropriate window depends on the source system's delivery behavior and business requirements.

For financial systems, periodic reconciliation against authoritative source totals may also be necessary.

A pipeline should make it possible to correct historical data rather than assuming that previously processed records are permanently final.

7. Schema Changes Can Break Pipelines Without Obvious Errors

Applications evolve.

A development team might rename a field, introduce a new status, change a numeric field's precision, or modify an API response.

These changes can affect downstream reports.

Consider a source system that originally stores a field called:

customer_status

Later, the application team introduces:

account_status

If the extraction pipeline still expects the original field, it may fail.

More dangerous situations occur when the pipeline continues running but silently produces incomplete data.

For example, a new order status might appear in the source system but not in the reporting logic.

A query that only recognizes completed, pending, and cancelled could incorrectly classify the new status.

Introduce data contracts

A data contract describes the expected structure and behavior of data exchanged between systems.

It can define:

  • Field names and data types.

  • Required and optional fields.

  • Unique identifiers.

  • Allowed values.

  • Relationships between entities.

  • Compatibility requirements.

  • Ownership and change procedures.

For important datasets, schema changes should be reviewed and tested before they affect downstream consumers.

This is especially useful in organizations where application development and analytics engineering are handled by different teams.

8. Add Automated Data Quality Checks

Developers already write unit tests for application code. Data pipelines deserve similar attention.

A pipeline that executes successfully is not necessarily producing correct data.

It may load incomplete records, duplicate identifiers, unexpected values, or broken relationships.

Automated data quality tests help detect these issues before they affect reports.

For example, dbt provides built-in generic data tests for uniqueness, non-null values, accepted values, and relationships. See the official dbt data testing documentation.

A simplified configuration might look like this:

models:
  - name: fact_orders
    columns:
      - name: order_id
        data_tests:
          - unique
          - not_null

      - name: customer_id
        data_tests:
          - not_null
          - relationships:
              arguments:
                to: ref('dim_customers')
                field: customer_id

      - name: order_status
        data_tests:
          - accepted_values:
              arguments:
                values:
                  - pending
                  - completed
                  - cancelled
                  - refunded

These tests establish some basic expectations about the resulting dataset.

You can also create custom tests for business rules.

For example:

  • An order's total amount should not be negative unless a defined adjustment is being recorded.

  • A completed order should have a completion timestamp.

  • A refund should reference an existing transaction.

  • A customer should not have conflicting active identifiers.

The goal is to make data quality measurable and repeatable instead of depending entirely on manual investigation.

9. Build Observability Into the Pipeline

Most engineering teams monitor application performance, server availability, API latency, and error rates.

Data pipelines need their own observability.

A useful monitoring system should answer three questions:

Did the pipeline run successfully?

Track job failures, execution duration, retries, and processing status.

Did the expected amount of data arrive?

Monitor record counts, rejected rows, duplicates, and unusual changes in volume.

Is the data still trustworthy?

Monitor freshness, null rates, unexpected values, referential integrity, and reconciliation differences.

A simple operational table could look like this:

Pipeline metric

What it tells you

Last successful run

Whether processing completed

Data freshness

How old the latest available data is

Rows processed

Whether expected volume arrived

Duplicate count

Whether records are being repeated

Rejected records

Whether validation is failing

Reconciliation variance

Whether source and target totals differ

Processing duration

Whether performance is degrading

A useful addition is a pipeline run identifier.

Every extracted, transformed, and published dataset should be traceable to the execution that produced it.

This makes it easier to investigate when a dashboard suddenly changes after a deployment.

10. Choose the Right Data Architecture for the Workload

Some data inconsistencies are symptoms of an architectural problem.

For example, teams may run complex analytical queries directly against transactional databases. Others may maintain multiple copies of the same data without clear ownership or synchronization rules.

The architecture should reflect the workloads involved.

An operational database is optimized for application transactions. An analytical platform is designed for querying, aggregation, and reporting across datasets.

A common architecture looks like this:

Operational Applications
          |
          v
   Source Databases
          |
          v
   Extraction / CDC
          |
          v
     Raw Data Layer
          |
          v
   Transformation Layer
          |
          v
    Curated Data Models
          |
          v
       BI Platform
          |
          v
    Reports and Dashboards

Each layer should have a clear responsibility.

The raw layer preserves source information. Transformation logic standardizes and validates it. Curated models provide business-ready data for reporting.

The choice of storage architecture also matters. A data warehouse vs data lake vs data lakehouse architecture comparison explains where these approaches differ and which workloads they support.

The important thing is not choosing the newest architecture. It is ensuring that data ownership, transformation logic, and reporting definitions remain consistent throughout the platform.

11. A Practical Debugging Workflow for Conflicting Reports

When two dashboards show different numbers, avoid changing queries randomly.

Use a repeatable investigation process.

Step 1: Identify the exact metric

Write down the business definition, reporting period, timezone, filters, and inclusion rules.

Make sure both reports are supposed to calculate the same thing.

Step 2: Identify the source of each report

Find out whether the reports use the same source tables, transformation models, and refresh schedules.

Two dashboards may look similar while relying on entirely different datasets.

Step 3: Compare record counts at each stage

Start at the source and move through the pipeline.

Compare:

  • Source records.

  • Extracted records.

  • Raw records.

  • Transformed records.

  • Final reporting records.

The first stage where the numbers diverge is a useful starting point for investigation.

Step 4: Check duplicate identifiers

Verify whether the same business record appears more than once.

Pay particular attention to joins and repeated ingestion.

Step 5: Validate time handling

Check the timestamp field, timezone conversion, date boundaries, and late-arriving records.

Step 6: Inspect transformation logic

Review joins, filters, aggregations, incremental processing, and business rules.

Step 7: Reconcile against the source

Choose a small sample of records and trace them individually from the operational system to the final report.

For financial metrics, compare totals against authoritative transaction or payment records.

Step 8: Add a regression test

Once the problem is resolved, add a validation rule that would detect the same issue in the future.

A debugging exercise should ideally improve the pipeline rather than simply fix one report.

Conclusion

When dashboards disagree, the problem is rarely solved by refreshing the page or changing a chart.

The real answer is usually somewhere in the data lifecycle.

Incorrect joins can multiply values. Duplicate ingestion can inflate counts. Incremental pipelines can miss historical updates. Timezone differences can shift transactions between reporting periods. Late-arriving records can change previously published figures.

These issues are not necessarily signs of poor engineering. They are common challenges in systems where information moves through multiple applications, databases, and analytical layers.

The important difference is whether the architecture can detect and explain them.

Build pipelines with explicit metric definitions, predictable transformation rules, idempotent ingestion, automated quality checks, reconciliation, and useful monitoring.

Most importantly, make every important business number traceable back to the records that produced it.

A trustworthy dashboard is not just a well-designed visualization. It is the final result of a data pipeline that engineers understand, test, and maintain.

Articles about local business and interesting people:

Share your ideas in a new publication.
We are waiting for your longread!
lokesh

lokesh

@3wSVF-PAvCgrO35

5Longreads
26Views
On Drukarnia since October 7

More from the author

You may also be interested in:

Comments (0)

Support the author first.
Write a comment!

You may also be interested in: