DEV Community

Cover image for dbt snapshots are not an audit log
Aniket Abhishek Soni
Aniket Abhishek Soni

Posted on AI-assisted

dbt snapshots are not an audit log

The persistent myth that dbt snapshots act as a foolproof audit log is a dangerous misunderstanding of how slowly changing dimensions (SCDs) function in modern data warehouses.

Many engineers treat snapshots as a catch-all solution for "tracking changes." If a table changes, they point a dbt snapshot at it and assume they have a perfect history of every state transition. However, dbt snapshots are designed to track the current state of a dimension over time, not to capture the high-frequency event stream required for an audit.

What happened

In 2022, the engineering team at Monzo shared a postmortem regarding a data integrity incident where a miscalculation in their ledger system led to incorrect balances. While this specific incident involved application-level logic rather than a dbt snapshot, it serves as a critical reminder of the difference between "state" and "event."

When systems rely on snapshots to reconstruct history, they often fail to capture the sequence of events that occurred between snapshot runs. If your snapshot frequency is set to once per day, and a record is updated three times within those 24 hours, the snapshot will only record the state as it existed at the moment of the snapshot run. You have lost the intermediate transitions. If those transitions were critical for compliance or debugging, your "audit log" is fundamentally incomplete.

A close-up of an old, open ledger with handwritten notes
Photo by camera obscura on Unsplash

The underlying failure pattern

The failure occurs because dbt snapshots (as documented in dbt Labs' official docs) are built to implement SCD Type 2. They compare the current state of a source record against the last recorded state.

Consider this hypothetical configuration:

{% snapshot user_status_snapshot %}

{{
    config(
      target_schema='snapshots',
      unique_key='user_id',
      strategy='check',
      check_cols=['status', 'subscription_level'],
    )
}}

select * from {{ source('raw', 'users') }}

{% endsnapshot %}
Enter fullscreen mode Exit fullscreen mode

When dbt snapshot runs, it performs a comparison. If the status column changed from 'active' to 'pending' and back to 'active' between two runs, the snapshot engine sees no difference. The historical record of that 'pending' state is lost.

Furthermore, snapshots are designed to be idempotent and batch-oriented. They are not designed to handle high-velocity write-ahead logs. Relying on them for an audit trail assumes that the source system provides a clean, consistent view of state at the time of execution. If the source system itself is subject to race conditions or eventual consistency—common in distributed architectures—the snapshot simply inherits that ambiguity.

a purple background with a black and blue circle surrounded by blue and green cubes
Photo by Deng Xiang on Unsplash

How to defend against it

To build a reliable audit trail, you must move away from state-based snapshots and toward event-based logging.

First, treat your data sources as event streams. Instead of relying on a users table that represents the current state of a user, ingest the underlying transaction or change logs. If you are using a database like PostgreSQL, tools like Debezium capture these row-level changes (CDC) and stream them into your warehouse. This ensures that every state transition is recorded as an immutable event, regardless of how many times a record changes in a single hour.

Second, separate your analytical snapshots from your audit requirements. It is perfectly acceptable to use dbt snapshots for building SCD Type 2 dimensions for your BI tools. These are designed for "as-of" reporting, where you want to know what a user's subscription level was on a specific date. They are not, however, an acceptable replacement for a granular audit log.

Third, enforce immutability in your storage layer. If you are using technologies like Apache Iceberg, leverage table properties to maintain history via snapshots or versioning. When you need to audit a change, query the table history directly rather than relying on a dbt-managed table that has discarded intermediate states.

If you must use dbt for auditing, you are likely using the wrong tool. An audit log requires:

  • Completeness: Every state change must be captured.
  • Ordering: The sequence of changes must be preserved.
  • Provenance: The origin and timestamp of the event must be immutable.

dbt snapshots provide none of these guarantees by default. They are a convenience for modeling, not a mechanism for compliance.

Conclusion

The distinction between capturing state and capturing events is the difference between an analytical convenience and a system of record. When you configure a dbt snapshot, you are choosing to manage the complexity of SCD Type 2 dimensions. You are not choosing to archive your system's history.

Use dbt snapshots to simplify your JOINs and power your dashboards. Use event-driven architectures and immutable logs when you need to prove exactly what happened, and when. Never confuse the two, or you will eventually find yourself trying to debug a "missing" state transition that your snapshot infrastructure was never designed to preserve in the first place.

Cover photo by Bruno Guerrero on Unsplash.


This article was drafted with AI assistance and is intended for technical review and fact-checking before publication.

Top comments (0)