You’re scoping out your first dbt project. You crack open the dbt best practices docs, and it’s… thorough. Maybe too thorough. Project structure guides, style guides, semantic layer manifests, Mesh architectures, real-time data patterns. It’s a lot for a team that just wants to get a few models into production without painting themselves into a corner.
The question hanging over every greenfield dbt adoption: do you follow this playbook wholesale, or do you treat it as a starting point and customize from there?
The honest answer is somewhere in the middle, and the nuance matters more than the dogma.
The Case for Just Following the Docs
Let’s give credit where it’s due. dbt Labs has built arguably the most comprehensive, maintainable documentation in the modern data stack. The how we structure our dbt projects guide alone covers staging, marts, intermediate layers, and the reasoning behind each choice. There’s also materialization guidance, idempotence best practices, and even opinions on Jinja formatting. The coverage is genuinely impressive.
The community consensus leans heavily toward “just follow the docs.” The dominant sentiment on developer forums echoes this: if you don’t have the experience to know what to change, don’t change anything. To go against the documentation is to invite guaranteed headaches. For most teams starting out, there’s real wisdom here, dbt’s best practices represent thousands of person-years of collective experience distilled into a coherent framework.
There’s a pragmatic truth to this approach. When a junior analyst onboards six months from now and asks why the project is structured this way, “because that’s how dbt Labs recommends it” is a satisfying answer. It’s defensible, documented, and doesn’t require tribal knowledge to understand. The dbt_project.yml reference provides a stable foundation that any dbt developer, regardless of background, can immediately navigate.
Where the Playbook Starts Feeling Thin
Here’s the thing about best practices: they’re designed for the median case. The moment your project exceeds a certain scale or complexity, the docs start showing their edges. The examples in dbt’s guides are intentionally simple, they demonstrate patterns, not scale. Push beyond a few hundred models, and you’ll find the documentation goes quiet.
One engineer who spent 4-5 years working on a 2,000+ model dbt project at enterprise scale puts it bluntly: the standard best practices work fine until you progress beyond the simplest of projects. The publicly documented patterns don’t cover what a realistic production project actually looks like. You’ll inevitably hit that point where the docs stop being a guide and start being a constraint.
The gap isn’t just about volume either. There are specific, practical decisions that the best practices don’t make for you. Take materialization. The docs recommend “always start with a view” and only materialize when query performance demands it. Good advice for a small project, but it ignores the reality that an incremental model on a large dataset behaves very differently depending on which materialization strategy you choose. Teams working with append-only data at scale often find that default incremental patterns (MERGE-based) create significant performance overhead compared to a pre-hook that deletes and then appends using materialized='incremental'. The docs flag the tradeoffs, but they don’t tell you which path fits your specific warehouse, data volume, or query patterns.
Treating Best Practices as a Floor
The more compelling perspective, shared by several veterans in the dbt community, is to treat the official guidance as a floor rather than a ceiling. Adopt the core structure, staging models, marts, consistent naming conventions, but recognize that you need to be more opinionated and prescriptive as your project scales.
This is where the real work of building a dbt project starts. Consider something as simple as documentation. The dbt way recommends column-level descriptions in your .yml files. Fine. But you can take it further by centralizing definitions:
# In your schema.yml
models:
- name: monthly_revenue
columns:
- name: customer_id
data_type: varchar
description: "{{ doc('customer_id') }}"
Define customer_id once in a separate .md file, and now that single definition can be reused across every model that touches it. When a column concept travels from table A to table Z through your pipeline, you’ve achieved true DRY documentation. The official best practices don’t tell you to do this, but they don’t forbid it either.
This is where gaps in dbt’s documentation practices despite best-in-class tooling start to emerge. dbt will happily generate beautiful lineage graphs and column-level descriptions, but the actual logic embedded in your WHERE clauses remains opaque to the casual reader. Your docs look perfect, the substance requires digging into the SQL.
Where You Should Absolutely Enforce Your Own Rules
This brings us to the critical realization: at scale, best practices aren’t just recommendations, they need to become enforced standards. The “should” needs to become “must”, and that requires automation.
Let me give you a concrete example. Incrementality. The dbt docs cover the basics of incremental materialization, but they don’t tell you when to use each strategy for specific model types. At scale, however, you should absolutely be enforcing storage and efficiency rules around how teams implement incrementality.
CI checks should prevent developers from using MERGE on large datasets that are actually append-only. Cross-database macros should be pinned down so that teams don’t accidentally introduce warehouse-specific syntax that breaks portability. Naming conventions should be validated automatically, not enforced through code review.
There’s a useful pattern here: encode your decisions into CI, and let the machines enforce them. You can use dbt’s own macros to create custom generic tests that enforce your standards. For example, you can write a custom test that checks for column null rates:
{% test not_null_proportion(model, column_name, threshold=0.95) %}
with validation as (
select
{{ column_name }},
count(*) as row_count
from {{ model }}
group by 1
),
null_check as (
select
sum(case when {{ column_name }} is null then row_count else 0 end) as null_count,
sum(row_count) as total_count
from validation
)
select * from null_check
where (null_count::float / null_count::float + total_count::float) < {{ threshold }}
{% endtest %}
The dbt docs on custom generic tests cover this pattern, but the point is that you can (and should) build your own library of tests that encode your project’s specific quality standards. The official best practices give you a starting point, your own requirements define what “good” actually means for your data.
There’s also a forward-looking argument for rigor here. If you ever want to let an AI agent refactor your models for cost savings or performance down the road, and trust me, that’s coming sooner than you think, that agent will rely on your tests to verify that its changes don’t break anything. Without comprehensive test coverage, you’re flying blind.
The Layer Naming Conundrum
Here’s where the docs get genuinely ambiguous, and the controversy gets spicy. The standard dbt structure uses staging, intermediate, marts (sometimes core and reporting). That’s mostly fine. But if you’re using Medallion architecture terminology, bronze, silver, gold, you’re in for a world of pain.
Different people in your company will interpret those layers differently. Bronze is usually well understood, but silver and gold can mean different things across teams. And here’s a modern wrinkle: AI tools will also default to different definitions depending on what they’ve ingested. If your team members are using AI assistants to understand your data model, and those tools interpret your layers differently depending on whatever they’ve read, your onboarding standards become inconsistent before you’ve even started scaling.
The answer isn’t to abandon Medallion terminology, it’s to be explicit about what your layers mean in your specific project. That’s a customization that goes beyond what the dbt best practices prescribe, and honestly, it’s the kind of decision that requires real thought about your organization’s context.
Staging Environments and the dbt Cloud Conundrum
One area where the “best practices” hit reality is deployment environments. The docs are clear: you should have a production environment, a staging environment, and ideally they should be configured thoughtfully. dbt’s documentation on deployment environments goes deep into how to set these up.

The challenge emerges when you’re working with dbt Cloud on a plan that supports only one project. Some plans support only one dbt project, while Enterprise-tier plans allow multiple projects with cross-project references through dbt Mesh. If you’re on a plan without Mesh support, your “best practice” options are constrained from the start.
For teams that haven’t hit the scale where Mesh becomes necessary, the dbt Mesh guidance explicitly recommends not building a multi-project architecture prematurely. The docs suggest incrementally adopting Mesh features as you scale. That’s pragmatic advice, but it can also slow down teams that know they’ll need the flexibility eventually.
The Copilot Factor
Speaking of the future: dbt’s platform ambitions are rewriting the data stack, and not everyone’s celebrating. The company’s expansion from a SQL transformation tool into a full platform (with Semantic Layer, Catalog, and AI features like dbt Copilot) means best practices are evolving alongside the tooling.
This raises an uncomfortable question: are you adopting dbt’s best practices, or are you adopting dbt Labs’ vision for how your entire data infrastructure should work? There’s a growing debate about whether the Fivetran-dbt merger signals a concerning shift in data stack control, and whether the open source trust that made dbt popular will survive the consolidation.
Here’s the pragmatic framing: the best practices serve the current tooling, not the other way around. If dbt’s platform features become more opinionated, and they will, your project structure needs to remain flexible enough to adapt. Baking vendor-specific patterns into your project structure too early can create migration pain if you need to switch later.
Building Your Own Standards Without Losing the Plot
So what does a mature, pragmatic approach to dbt best practices actually look like? It’s finding the sweet spot between conformity and customization.
Start with the official structure. staging for sources and standardization, intermediate for reusable building blocks, marts for business-defined entities. The dbt guidance on marts is sound: start with views, materialize when performance demands it. These conventions are well-understood and any dbt developer can navigate them instantly.
Customize where you have real requirements. If you’re processing append-only event streams, establish hard rules about materialization strategies. If you have specific data quality SLAs, build custom tests that enforce them. If you have domain-specific column definitions, centralize them in a docs store.
Enforce, don’t suggest. The best practices are suggestions. Your CI pipeline should be enforcement. Build a culture where standards are automated, not aspirational.
Keep the docs as a shared language. When someone asks “why is this project structured this way?” the answer should still be “because that’s the dbt way” as the baseline, with clear documentation of wherever you’ve deviated and why.
The Bottom Line
dbt’s best practices aren’t the industry standard because dbt Labs says so, they’re the standard because they’ve been forged in the fires of thousands of production implementations. But standards evolve, and your project’s needs are unique. The right testing strategies and documentation approaches are the ones that work for your team, your data, and your scale.
Treat the official guidance as your foundation, not your prison. Build higher where your requirements demand it. And when you make intentional deviations, document them as clearly as dbt documents its own opinionated choices. That’s how you end up with a dbt project that’s both maintainable and actually useful, instead of one that just looks good in the docs.
The best practice is to question the best practices. Everything else is just following the crowd.




