dbt ships with first-class machinery for safely applying the same code to different environments. In dbt Core that means profiles.yml targets and Jinja config; in dbt Cloud it means the combination of Environments (Development / Deployment) and Jobs.
This article walks through the places that commonly trip people up in production and on the exam — schema isolation, permissions, Slim CI via deferral, and toggling test severity — with concrete examples and operational patterns.
dbt treats "environments" at two layers. Locally / on the CLI you switch connections and schemas via profiles.yml outputs and targets (e.g. dev / stg / prod). In dbt Cloud, an Environment (Development or Deployment) bundles credentials, permissions, and the target project, and each Job is bound to a specific Environment.
The key is making physical isolation (at the database / catalog / schema / dataset level) explicit. That prevents developers from accidentally overwriting production objects. As a baseline, use separate Database/Schema in Snowflake, separate Datasets in BigQuery, and separate Catalog/Schema in Databricks (Unity Catalog).
| Environment | Purpose | Physical placement (example) | Permission policy |
|---|---|---|---|
| dev | Individual development and experimentation | SNOWFLAKE: DB=ANALYTICS, SCHEMA=DEV_<user> | Developers can CREATE; production data is READ-only |
| stg | Integration testing and rehearsal | BIGQUERY: project=analytics-stg, dataset=core_stg | Only a small set of members can WRITE |
| prod | Consumption and delivery | DATABRICKS: catalog=prod, schema=marts | Only CI/CD can WRITE; viewers get READ |
Typical environment isolation pattern (logical → physical)
dbt_project.yml: reflect the environment name in the schema (safe collision avoidance)
models:
+materialized: table
# Custom: force target.name to be appended to the schema name
macro-paths: ["macros"]
The most common cause of incidents is dev and prod living in the same schema. Always append target.name in the generate_schema_name macro to prevent physical object collisions. For individual development, issuing a per-developer schema like dev_<user> is the safe pattern.
Match the conventions to each warehouse: Snowflake has a 2-tier Database/Schema model, BigQuery uses Project/Dataset, and Databricks (Unity Catalog) uses Catalog/Schema/Volume. Isolating prod at the highest tier you can afford makes role management much easier.
| Naming approach | Example | Pros / caveats |
|---|---|---|
| schema = <base>_<target> | core_dev / core_stg / core_prod | Simple and easy to remember. Decide whether you can live with a prefix on prod too. |
| Per-user schema | dev_alice, dev_bob | Great for parallel development. Requires permission management and cleanup discipline. |
| Hierarchy-first isolation | Snowflake: DB=ANALYTICS_DEV/STG/PROD | The safest option. Cost management and access control are crystal-clear. |
Example macros/generate_schema_name.sql
{% macro generate_schema_name(custom_schema_name, node) -%}
{%- set default_schema = target.schema -%}
{%- if custom_schema_name is not none -%}
{{ default_schema }}_{{ custom_schema_name }}_{{ target.name }}
{%- else -%}
{{ default_schema }}_{{ target.name }}
{%- endif -%}
{%- endmacro %}
In dbt Core / CLI, define dev / stg / prod under profiles.yml outputs, and switch between them with target. Each output spells out the connection, schema/dataset, and role/permission. Pull environment variables with env_var.
Across Snowflake, BigQuery, and Databricks, restrict prod writes to CI/CD (service accounts) and keep developers read-mostly. This prevents accidental updates and the leakage of secrets.
profiles.yml (example: Snowflake)
my_project:
target: dev
outputs:
dev:
type: snowflake
account: {{ env_var('SF_ACCOUNT') }}
user: {{ env_var('SF_USER') }}
password: {{ env_var('SF_PASSWORD') }}
role: DEV_ROLE
database: ANALYTICS
warehouse: DEV_WH
schema: core
stg:
type: snowflake
account: {{ env_var('SF_ACCOUNT') }}
user: {{ env_var('CI_USER') }}
password: {{ env_var('CI_PASSWORD') }}
role: STG_ROLE
database: ANALYTICS_STG
warehouse: CI_WH
schema: core
prod:
type: snowflake
account: {{ env_var('SF_ACCOUNT') }}
user: {{ env_var('DEPLOY_USER') }}
password: {{ env_var('DEPLOY_PASSWORD') }}
role: PROD_ROLE
database: ANALYTICS_PROD
warehouse: PROD_WH
schema: marts
In dbt Cloud, an Environment bundles connection details, role, and deploy permissions, and every Job is bound to a specific Environment. The Development Environment is what developers use in the IDE; the Deployment Environment is for CI/CD and scheduled runs.
Protect the prod Job with an approval flow and restricted roles, and adopt a two-tier pattern: run a per-PR staging Job (CI) first, then trigger the prod Job. UI labels may change over time, but the design principle of binding and separating Environments and Jobs is stable.
Example Job command (Cloud/CLI shared idiom)
dbt build --target prod --select tag:marts+
Rebuilding everything on every PR is expensive. Slim CI combines deferral and state to reference past production (or staging) artifacts while rebuilding only the changed surface area. The result is fast, stable validation.
A dbt Cloud Job has a setting equivalent to "Defer to a previous run state" where you point at the latest prod artifacts as the reference. On the CLI, use the --defer and --state options.
Typical Slim CI (PR) command
dbt build \
--target stg \
--select state:modified+ \
--defer \
--state path/to/prod_artifacts
Switch test severity and GRANTS based on the environment. In prod, let critical tests fail the run; in dev, prioritize speed with warnings or disabled tests. dbt tests can make enabled/severity conditional on the environment via Jinja, and model grants can branch on target.name too.
For breaking changes (such as dropping columns), finish data validation in staging, then move forward safely in prod with a staged rollout or a rename-and-migrate approach. Trigger scheduled Jobs from a successful prior staging run, and roll back by pointing at a pinned artifact version.
Example tests/grants config per environment
# tests (schema.yml)
version: 2
models:
- name: fct_orders
columns:
- name: order_id
tests:
- not_null:
name: not_null_order_id
enabled: "{{ target.name != 'dev' }}"
severity: "{{ 'error' if target.name == 'prod' else 'warn' }}"
# grants (model config in dbt_project.yml or models/*.yml)
models:
marts:
+grants:
select: "{{ ['ANALYST_ROLE'] if target.name == 'prod' else [] }}"
Analytics Engineer
問題 1
On a PR's CI, you want to reference past production artifacts and rebuild only the changed surface area. Which command is most appropriate?
正解: A
The Slim CI playbook is to select changes plus their dependencies with state:modified+, and use --defer with --state to reference production artifacts so unchanged nodes are not rebuilt. B writes directly to prod, C is a full rebuild and too expensive, and D does not build anything so it is insufficient.
What is the difference between a dbt Cloud Environment and a profiles.yml target?
The profiles.yml target controls the connection and schema switch for CLI/local usage, while a dbt Cloud Environment bundles credentials, roles, and runtime on the cloud side and is tightly bound to Jobs. The underlying idea is the same, but they apply at different layers.
Is it OK to skip staging and deploy straight from dev to prod?
Not recommended. Staging is essential for validating schema changes, dependencies, and data-quality tests. Especially for breaking changes and incremental models, Slim CI diff validation in stg is the main safeguard against production incidents.
Is it safe to run prod and stg as separate schemas inside the same Database/Project?
It meets the minimum bar, but for stronger isolation of production permissions, cost, and retention, splitting at the higher tier is safer (a separate Database in Snowflake, a separate Project in BigQuery, or a separate Catalog in Databricks). Separate the hierarchy too whenever possible.
Practice with certification-focused question sets
無料で問題を解いてみるNicheeLab Editorial Team
NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.
dbt Models: SQL-Defined Transformation Units (2026)
Model fundamentals — SELECT-based definitions, naming, refs,...
dbt Analytics Engineering Exam: Complete Guide (2026)
Pass the AE Certification — scope, weighting, sample questio...
dbt Cloud vs dbt Core: Feature & Cost Comparison (2026)
Honest comparison of dbt Cloud vs. dbt Core — IDE, scheduler...
dbt Project Structure: models/seeds/macros Layout (2026)
Recommended dbt project layout — models, seeds, macros, snap...
dbt_project.yml Explained: Every Config (2026)
Every dbt_project.yml setting that matters — paths, vars, ma...