DEV Community

Cover image for I built the "anti-Elementary" for dbt data quality and here's why
Robson Müller
Robson Müller

Posted on

I built the "anti-Elementary" for dbt data quality and here's why

Elementary is great. I use it. But after writing YAML for the 200th anomaly test, I asked myself: why am I teaching the tool what "normal" looks like when the data already knows?

So I built Scherlok — a zero-config data quality monitor that learns your data's patterns and detects anomalies automatically. No YAML. No thresholds. No rules to maintain.

It just shipped on the dbt Package Hub, so you can install it as a native dbt package today. Here's what makes it different, and when you should use which.

The core difference

Elementary asks you to define what's normal:

# schema.yml — per model, per test, per column
models:
  - name: fct_orders
    tests:
      - elementary.volume_anomaly:
          timestamp_column: created_at
          time_bucket:
            period: day
          sensitivity: 3
Enter fullscreen mode Exit fullscreen mode

You configure the timestamp column, the time bucket, the sensitivity, the training period, the detection method. For every model. For every test. It's powerful, but it's work — and the configurations accumulate.

Scherlok inverts the model:

scherlok connect postgres://user:pass@host/db
scherlok investigate    # profiles everything
scherlok watch          # detects anomalies
Enter fullscreen mode Exit fullscreen mode

Three commands. Done. No YAML, no per-model configuration, no timestamp columns to specify. Scherlok profiles every table and column automatically, learns the distributions, and flags when something deviates from the learned baseline.

An honest comparison

I'm not going to pretend Scherlok is better at everything. Here's where each tool wins:

Elementary Scherlok
Setup time 30-60 min (package + config per model) 5 min (connect + investigate)
Configuration Granular per-model YAML Zero (auto-discovers everything)
Detection methods Multiple algorithms, configurable Shewhart control limits (mean ± kσ)
dbt integration depth Deep (runs inside dbt) CLI + native dbt package
Dashboard Beautiful hosted UI (Elementary Cloud) Self-contained HTML file
Lineage-aware alerts Yes Yes (reads manifest.json)
AI explanations No Yes (Claude, opt-in)
MCP server No Yes (AI agents can run checks)
Non-dbt usage Requires dbt Works with or without dbt
Price Free package, paid Cloud Free, forever
Connectors dbt adapters Postgres, BigQuery, Snowflake, MySQL, DuckDB

Use Elementary when you want fine-grained control over every detection parameter, need the hosted dashboard for non-technical stakeholders, or your team already has the YAML config in place.

Use Scherlok when you want anomaly detection running in minutes without writing configuration, need it outside of dbt, or want AI-powered explanations on your alerts.

The dbt package — best of both worlds

As of v0.9.0, Scherlok ships as a native dbt package. This means you can use Scherlok's tests inside your existing schema.yml without leaving dbt:

# packages.yml
packages:
  - package: rbmuller/scherlok
    version: [">=0.9.0", "<1.0.0"]
Enter fullscreen mode Exit fullscreen mode
# schema.yml
models:
  - name: fct_orders
    tests:
      - scherlok.volume_anomaly:
          sensitivity: 3.0
      - scherlok.row_count_between:
          min_value: 100
    columns:
      - name: email
        tests:
          - scherlok.not_null_proportion:
              max_rate: 0.01
      - name: updated_at
        tests:
          - scherlok.recency:
              days: 2
Enter fullscreen mode Exit fullscreen mode

Six tests available:

Instant (no setup, works on first dbt test):

  • not_null_proportion — NULL rate exceeds threshold
  • row_count_between — row count outside bounds
  • recency — data is stale
  • unique_proportion — cardinality drops

Auto-learning (builds baseline over time):

  • volume_anomaly — row count outside Shewhart control limits
  • null_anomaly — NULL rate spike vs historical

The auto-learning tests use an incremental scherlok_metrics model that runs with dbt run and captures row counts per model. After 5 runs, the anomaly tests activate automatically. Before that, they pass silently — first runs are baseline, not false alarms.

The CLI — where Scherlok really shines

The dbt package is one distribution channel. The CLI is where the "zero-config" promise delivers the most value.

Connect to any warehouse

scherlok connect postgres://user:pass@host/db
scherlok connect bigquery://project-id/dataset
scherlok connect snowflake://account/database/schema
scherlok connect mysql://user:pass@host/db
scherlok connect duckdb:///path/to/file.db
Enter fullscreen mode Exit fullscreen mode

Profile everything in one command

$ scherlok investigate

  Profiling 12 tables...
  ✓ users         — 45,231 rows, 8 columns
  ✓ orders        — 1,203,847 rows, 15 columns
  ✓ products      — 892 rows, 12 columns
  ...
  Done. Profiles saved.
Enter fullscreen mode Exit fullscreen mode

Detect and alert

$ scherlok watch --webhook $SLACK_URL --explain

  🔴 CRITICAL  orders    volume_drop     Row count dropped 52%
  🟡 WARNING   users     null_increase   Column "email": NULL rate 2.1% → 18.7%

  💡 Hypothesis: The orders volume drop correlates with stg_orders
     upstream. The source table likely stopped receiving data from
     the payments API after the 2026-08-09 deploy.
Enter fullscreen mode Exit fullscreen mode

The --explain flag is opt-in. When anomalies fire, Scherlok sends the anomaly batch (not your data — just the anomaly messages) to Claude for a root-cause hypothesis. On dbt projects, it includes upstream lineage so cascading failures get traced to the source model. Costs under $0.003 per run. If the API fails, the alert is delivered unchanged.

CI in one line

- run: scherlok ci $DATABASE_URL --fail-on critical --webhook $SLACK_URL
Enter fullscreen mode Exit fullscreen mode

AI agents can use it directly

Scherlok ships an MCP server since v0.7.0. Claude Code or Claude Desktop can profile tables and detect anomalies without you typing commands:

{
  "mcpServers": {
    "scherlok": {
      "command": "scherlok-mcp",
      "env": { "SCHERLOK_CONNECTION": "postgresql://..." }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The agent gets list_tables, investigate, watch, status, history, and check as tools. Credentials stay server-side. Every operation is read-only.

Technical decisions

A few choices that might be interesting if you're building in this space:

Shewhart over ML. I chose Shewhart control limits (mean ± kσ) over fancy ML models. It's boring, debuggable, and transparent. When a test fires, you can explain why in one sentence: "row count was 3.2 standard deviations below the 30-day average." No black box. The tradeoff: no seasonality modeling. If your data has strong weekly patterns, you'll get false positives on Mondays. For most tables, Shewhart catches 90% of real incidents with zero tuning.

SQLite for profiles. Your baseline data lives in ~/.scherlok/profiles.db. You can inspect it with sqlite3, copy it to another machine, or sync it to S3/GCS/Azure for team use. No hosted service required.

Read-only by contract. Scherlok never writes to your warehouse. The test suite enforces this. Your DBA will appreciate it.

One repo, two channels. The same GitHub repo (rbmuller/scherlok) ships as a PyPI package (pip install scherlok) and a dbt package (package: rbmuller/scherlok). pyproject.toml and dbt_project.yml coexist at the root without conflicts.

When NOT to use Scherlok

Being honest about limitations:

  • Strong seasonality patterns — Shewhart control limits don't model day-of-week or seasonal cycles. You'll need to tune sensitivity higher or use Elementary's timestamp-aware algorithms.
  • Custom business rules — "revenue must match between systems A and B" is a rule, not an anomaly. Use dbt test or Great Expectations for that.
  • Non-technical stakeholders — Elementary Cloud's dashboard is built for people who don't live in the terminal. Scherlok's HTML dashboard is useful but developer-oriented.

Try it

pip install scherlok
scherlok connect YOUR_DATABASE_URL
scherlok investigate
scherlok watch
Enter fullscreen mode Exit fullscreen mode

Or as a dbt package:

packages:
  - package: rbmuller/scherlok
    version: [">=0.9.0", "<1.0.0"]
Enter fullscreen mode Exit fullscreen mode

MIT licensed. 344 tests. 5 connectors. Zero config.

Top comments (0)