martechdevs has been acquired by Growth and Analytics Partners LLC

dbt Implementation Mistakes: Fix Stale Models, Incremental Errors and Fragile Deployments

Jafrin Akter Alo

Data Engineer and Co-founder, martechdevs

Last updated 12 min read

Blog cover: dbt Implementation Mistakes: Fix Stale Models, Incremental Errors and Fragile Deployments

A green dbt run proves that the selected work completed under its configured checks. It does not prove that yesterday's refund reached revenue, that an old invoice received a new field, or that tomorrow's job will still have enough hosted build allowance to run. A dependable implementation tests those operating assumptions directly.

Last updated: 14 September 2026.

Begin with one output somebody is responsible for

Choose a recurring business decision and trace the smallest model graph that supports it. For a marketing team, that might be paid acquisition to qualified pipeline or a daily view of net collected revenue.

Write the output grain, source of truth, freshness requirement, treatment of corrections and accountable owner. A model named revenue without those decisions is unfinished even if its SQL compiles.

Separate five failure types when investigating a messy project: source availability, transformation logic, model selection, execution environment and operating cost. Changing a materialization cannot repair a missing upstream export. Increasing a hosted allowance cannot make an incremental filter capture late corrections.

How the workflow fits together
A dependable dbt implementation tests the output contract, representative input, incremental equivalence, deployment selection, fresh authentication and recovery.
Open full diagram

Mistake 1: Confusing model files with monthly hosted usage

The hosted Developer plan currently includes 3,000 models monthly. Its billing documentation says subsequent runs are cancelled after that allowance is exceeded, until reset or upgrade. Account entitlements can differ under older terms. Check the actual account before making the free plan a production dependency.

The relevant meter is eligible successful builds, not the number of SQL files. dbt's pricing definition includes deployment orchestration, CI and API-triggered builds. Views, tables and incremental models count. Development builds, tests, seeds, snapshots and ephemeral models do not count toward this meter. Successful models in a job that later fails still count.

Consider an illustrative eighteen-model project rebuilt eight times daily for thirty days. That creates 4,320 successful builds. Fifteen CI runs rebuilding six models add ninety, for a total of 4,410. The repository has eighteen models but the schedule exceeds the standard Developer allowance by 1,410 builds.

If the business can tolerate four refreshes daily, the same calculation becomes 2,160 plus ninety, or 2,250. That leaves 750 builds for other eligible work under a 3,000-build allowance. Retries and additional deployments still need room.

Do not reduce freshness solely to fit a plan. Identify which outputs need frequent refreshes and select their necessary dependencies. Then compare a suitable hosted plan with the real operating cost of running locally. The free engine still needs scheduling, credentials, monitoring and maintenance. Our guide to free dbt and hosted costs separates those choices.

Mistake 2: Rebuilding every view while accidentally dropping its tests

A normal warehouse view reads its underlying data when queried; recreating an unchanged definition on every refresh is not always necessary. That does not mean every view can be excluded from every job.

Compiled view definitions can depend on source-driven macros, such as SQL generated from changing categories. Code changes also need deployment. dbt's cost-optimization guide distinguishes excluding view materialization from retaining view tests and deploying changed definitions.

Inventory why each repeatedly rebuilt view exists. Is it a stable projection? Does its compiled SQL change with source values? Which tests must still run when the underlying data changes? Which deployment job publishes its new definition?

Test the proposed selector by inspecting what it selects and what it omits. Verify both the model nodes and the tests that protect their outputs. A lower build count is not a useful optimization if it also removes the only check for duplicate customer IDs.

For a useful acceptance exercise, change a view expression in a development branch and confirm the release path updates it. Separately introduce a bad fixture row and confirm the retained test catches it without requiring every view definition to be rebuilt. Those are different responsibilities and should have separate evidence.

Mistake 3: Assuming a new column fills historical rows

The on_schema_change setting controls schema handling for incremental models. It does not decide the historical meaning of a newly added field. dbt's incremental configuration guide states that these behaviors do not backfill new-column values in existing records, and that schema-change tracking is limited to top-level columns.

Suppose you add acquisition_channel to an incremental customer model. New or reprocessed customers may receive a value while older rows remain empty. A dashboard can then show an apparently improving attribution rate that is actually the boundary between old and newly processed records.

Choose the historical policy before deploying: populate all recoverable history, populate a documented period, or leave earlier values explicitly unavailable. Estimate the work, verify the underlying source still exists and decide which downstream reports require restatement.

For a controlled contract, failing on unexpected schema changes may be preferable to automatic synchronization. For a reviewed additive change, an append or synchronization option may fit. Neither choice replaces a backfill plan. Nested structures need their own checks because a top-level schema check does not describe their internal changes.

A full refresh is a rebuild, not a harmless retry. Establish the affected models, required source history, permissions, cost and recovery path first. A trailing descendant selector can enlarge the scope substantially. Run a bounded comparison in an isolated environment before replacing an important production relation.

There is also a current compatibility trap: the incremental guide warns about Snowflake column-size changes affecting older dbt-snowflake adapters when models combine collation with sync_all_columns. It points v1 users to version 1.10.6 or later and identifies compatible platform tracks and v2. Check that specific condition before blaming your new field for an adapter failure.

Mistake 4: Using an incremental filter that only finds new business events

An invoice created last month can be corrected today. If the incremental filter only asks whether its creation date is newer than the last run, the correction never reaches the model.

Separate occurrence time, source update time and ingestion time. Define which timestamp is trustworthy for identifying changed records. A lookback window can absorb ordinary delays, but it needs a measured rationale and a way to repair changes outside it. Three days is not a universal guarantee.

Then define how changes become one current record. dbt's unique_key reference explains matching incoming records to existing rows. The key configuration does not replace uniqueness and non-null tests. A source can still deliver duplicate versions or ambiguous keys.

Use a namespaced business key where identifiers repeat across accounts. For invoices, (billing_account_id, invoice_id) is safer than assuming an invoice ID is globally unique. Resolve competing versions deterministically using authoritative revision information where available. A late-arriving old revision must not overwrite a newer accepted state simply because it arrived later.

Document delete behavior too. A record absent from the latest extract might be deleted, excluded by a source filter or missing because the extract failed. Those cases should not all trigger the same action. Prefer explicit deletion evidence where the source supports it, and test the downstream financial treatment.

The available incremental strategies vary by adapter and version. Row-level merge and partition replacement have different semantics. Choose from the actual correction and deletion requirements, then inspect the compiled operation rather than copying another warehouse's configuration.

Prove that incremental and full results agree

Use a frozen, representative input containing a new record, a late correction, a replay, a deletion marker, a refund and identifiers reused by different source accounts. Build the expected full result and the incremental sequence in isolated test relations.

The following original singular data test compares two example QA models. Create those models first from the same fixture and agreed invoice contract. Both must have unique, non-null account and invoice keys. net_amount_minor is an integer in the currency's minor unit; is_deleted is an explicit state, not inferred from absence.

Save this as a singular SQL test in the project's test directory. It returns missing or different records:

SQL
SELECT
    COALESCE(a.billing_account_id, e.billing_account_id) AS billing_account_id,
    COALESCE(a.invoice_id, e.invoice_id) AS invoice_id
FROM {{ ref('qa_invoices_incremental') }} AS a
FULL OUTER JOIN {{ ref('qa_invoices_expected') }} AS e
    ON a.billing_account_id = e.billing_account_id
   AND a.invoice_id = e.invoice_id
WHERE a.invoice_id IS NULL
   OR e.invoice_id IS NULL
   OR a.net_amount_minor IS DISTINCT FROM e.net_amount_minor
   OR a.currency IS DISTINCT FROM e.currency
   OR a.invoice_status IS DISTINCT FROM e.invoice_status
   OR a.is_deleted IS DISTINCT FROM e.is_deleted

This pattern uses null-safe comparisons available in BigQuery and Snowflake. Adapt it to your warehouse and contract. It does not generate the fixture or implement the incremental strategy for you.

dbt data tests report rows that violate an assertion; zero rows means this comparison passed. Include independent expected values, because an incremental model and a full model can share the same wrong business logic. dbt unit tests can exercise model logic with controlled inputs, while a sequence of actual incremental runs checks materialization behavior. Use both where the distinction matters.

Rerun the identical input and confirm nothing duplicates or changes unexpectedly. Then add a correction outside the normal lookback and verify the documented repair path. That final case tests the limitation your normal schedule cannot solve.

Mistake 5: Scheduling `state:modified` as if it detected new source rows

State comparison checks changes to project code, configuration and relevant metadata. It does not detect that twenty new purchases landed in an unchanged source table. A scheduled job using only state:modified can succeed while building nothing.

Use code-change selection for the appropriate CI or deployment purpose. Use a data-refresh selection or a deliberately configured data-aware orchestration mechanism for recurring production updates. Do not treat those mechanisms as interchangeable. dbt's state comparison caveats explicitly describe this scheduled-job failure.

Preserve the comparison manifest separately from the directory the current parse writes into. Otherwise you can overwrite the evidence you intended to compare against. Record which successful deployment produced the state artifact and keep it associated with that version.

Also inspect deferred references. A test with multiple parents can combine a modified development relation with an unmodified production relation. If the development data is sampled, a relationship failure may reflect inconsistent populations rather than broken production logic. Design the fixture and reference behavior deliberately; do not remove the relationship test simply to obtain a green run.

Acceptance should include two runs: one with changed code and one with unchanged code but new source data. Confirm that the intended models and tests run in each case and that the production output becomes fresh.

Mistake 6: Upgrading the engine and the project architecture together

Pin the current working environment before migration: engine, adapter, packages, profile configuration, job commands and target behavior. Keep the production baseline available while evaluating the new runtime in isolation.

The current v2 upgrade guide requires attention to supported adapters and authentication, package compatibility and deprecated behavior. Parsing is stricter in several areas, so a previously deferred error can appear earlier. That is a reason to investigate the failing construct, not automatically disable validation.

Work through one failure class at a time. Confirm connection support, resolve deprecations, inspect package compatibility, parse and compile, then run representative models and tests. Compare output grain, types, null handling, grants and incremental updates. A compiler success is not a data-equivalence result.

Avoid changing metric definitions during the same evaluation. If revenue changes while the engine, package and business logic all change, identifying the cause becomes much harder. Establish runtime equivalence first, then release intentional business changes with their own expected differences.

Record the rollback procedure and the artifacts needed to execute it. A previous executable is insufficient if deployment also changed table schemas or state artifacts. The recovery plan needs to cover the data and configuration the old runtime would encounter.

Mistake 7: Diagnosing every Snowflake connection failure inside dbt

A deployment can fail because the warehouse rejected its authentication before any model ran. Compare the dbt error with Snowflake login evidence and the account's enforcement timing.

Snowflake's authentication rollout moves legacy service users away from password sign-in on an account-specific schedule. A password reset does not fix an authentication method that is no longer permitted.

Check the method supported by your actual dbt engine, adapter and deployment environment. Update the connection through the approved credential-management path, then test a fresh session under the production role. A developer's interactive SSO session does not validate an unattended deployment identity.

Keep secrets out of project files and troubleshooting screenshots. Record credential ownership and rotation steps, but not the secret values. After authentication succeeds, verify object permissions and the target database and schema independently. Our Snowflake implementation troubleshooting guide provides the account-level diagnostic sequence.

Mistake 8: Treating “free” as a complete licensing decision

Specify the distribution your team runs. The current licensing FAQ distinguishes Apache 2.0 dbt OSS from the full distribution under the Product Licensing Agreement. The former fifteen-user limit for the free VS Code extension no longer applies. Old summaries of pre-release licensing are not a reliable implementation specification.

Internal transformation, exposing resulting data to customers and embedding a general-purpose transformation workflow are different product designs. The FAQ discusses those examples separately. Do not assume that building customer-facing software is always prohibited, or that a free installation gives unrestricted redistribution rights.

For an embedded product, review the current Product Licensing Agreement against the intended customer experience, including its redistribution and platform-access conditions. Put the selected distribution, version and applicable terms in the architecture record. Resolve the product-specific interpretation before shipping that integration.

Finish the implementation with fewer models and stronger evidence

Every model should have a purpose, grain, owner and consumer. A staging layer that stabilizes source names and types can earn its place. Five pass-through layers created because another company's repository has five layers need a clearer reason.

Review unused models and duplicated definitions before adding new abstractions. Build reusable logic when a real second consumer needs it, and keep the first important output understandable to the person operating it. The marketing warehouse design guide provides a practical source-to-output structure.

Estimate delivery by the unresolved decisions and required operating cycles. A small project with reliable sources and agreed metrics is different from a repair involving missing history, ambiguous identity and undocumented revenue rules. Ask a partner for a bounded scope with acceptance evidence and handover, not a promise based only on model count.

Checklist

  • Each published model has a clear grain, owner and business consumer.
  • Hosted build usage is forecast from actual selection and run frequency.
  • View exclusions retain required tests and a code-deployment path.
  • Schema changes include an explicit historical backfill decision.
  • Incremental behavior covers corrections, replay, deletes and late data.
  • Independent expected results and incremental/full comparisons pass.
  • New source data refreshes output even when no code changed.
  • State artifacts and deployment targets remain separate and traceable.
  • Engine migration preserves the agreed output contract.
  • Fresh production authentication and role permissions have been verified.
  • Distribution and licensing fit the intended product use.
  • Another team member can recover a failed run without inventing the procedure.

Our data warehouse and CDP implementation work can start with that bounded output and establish the models, checks and operating ownership around it. The useful deliverable is a reporting or activation workflow the team can change and recover with confidence.

Questions people ask

Why did my free hosted dbt jobs stop when I only have a few models?

The allowance counts eligible successful builds across repeated deployment and CI runs. A small graph refreshed frequently can exceed it. Inspect the account's actual terms and usage, then adjust unnecessary work or choose capacity that meets the required freshness.

Does `on_schema_change` populate a new column in old rows?

No. Schema handling and historical population are separate. Define which history should be populated, verify the source data exists and plan an appropriate bounded update or rebuild. Check nested fields separately rather than relying only on top-level schema detection.

Is `unique_key` enough to guarantee a correct incremental model?

No. Test key uniqueness and non-nullness, version ordering, late updates, replay and deletion behavior. A key tells the materialization how records match; it does not prove that your selected source rows represent the correct current state.

Should production refresh jobs use only `state:modified`?

Not when the requirement is to process new data with unchanged code. Use a selection or orchestration approach that satisfies that refresh requirement, and reserve code-change comparison for its intended deployment or CI purpose.

How do I know a dbt implementation is ready for handover?

Require reconciled output, representative failure tests, a verified production identity, an understood schedule and cost forecast, and a recovery procedure another owner can execute. Repository size and a green demonstration run are insufficient acceptance criteria.

Sources

  1. dbt: Plans and billing
  2. dbt: Successful models built and usage
  3. dbt: Optimize build costs and retain view tests
  4. dbt: Incremental configuration and schema changes
  5. dbt: unique_key reference
  6. dbt: Adapter-specific incremental strategies
  7. dbt: Data tests
  8. dbt: Unit tests
  9. dbt: State comparison caveats
  10. dbt: Upgrading to v2
  11. Snowflake: Authentication rollout
  12. dbt: Licensing FAQ
  13. dbt: Product Licensing Agreement

Written by

Jafrin Akter Alo

Data Engineer and Co-founder, martechdevs

Jafrin Akter Alo is a data engineer and co-founder of martechdevs, where the team has delivered 74+ martech, data and growth stack implementations.

Book a Free Audit

A free audit of your stack and what it needs. No pitch.

Thirty minutes, and you leave with a plan

We look at what you have, what it is costing you and what to do about it first.

  • 30 minutes
  • Video call, link on booking
  • No obligation

What we'll cover

  • Your current stack and where it hurts
  • Quick wins we can ship first
  • An integration roadmap
  • Timeline, scope and next steps

Calendar not loading? Open it in a new tab or email hello@martechdevs.com.