dbt em 2026: transformações de dados, testes e perguntas de entrevista
Tutorial prático de dbt (data build tool): transformações SQL, modelagem em camadas, estratégias de teste e perguntas reais de entrevista para vagas de data engineering em 2026.

O dbt (data build tool) se tornou o framework padrão para transformar dados dentro dos data warehouses modernos, usado em produção por mais de 40.000 empresas em 2026. Este tutorial cobre os mecanismos centrais das transformações no dbt, as estratégias de teste e as perguntas que realmente aparecem nas entrevistas de data engineering.
O dbt cuida do T no ELT. Ele compila instruções SQL SELECT em DDL (CREATE TABLE, CREATE VIEW, MERGE) e as executa contra um warehouse — Snowflake, BigQuery, Redshift ou Databricks. Controle de versão, resolução de dependências, testes e documentação já vêm integrados.
Modelagem em camadas: staging, intermediário e marts
Um projeto dbt bem estruturado separa as transformações em três camadas. Essa abordagem, popularizada pela comunidade dbt, impõe modelos de responsabilidade única e facilita a depuração.
Os modelos de staging ficam mais próximos dos dados crus. Sua função é restrita: renomear colunas, converter tipos e filtrar linhas inúteis. Sem joins, sem agregações.
-- models/staging/stg_orders.sql
WITH source AS (
SELECT * FROM {{ source('ecommerce', 'raw_orders') }}
)
SELECT
id AS order_id,
customer_id,
CAST(order_date AS DATE) AS order_date,
CAST(amount AS DECIMAL(10, 2)) AS order_amount,
LOWER(status) AS order_status
FROM source
WHERE id IS NOT NULLOs modelos intermediários aplicam a lógica de negócio — joins, agregações, funções de janela. Eles referenciam os modelos de staging por meio de ref(), que registra as dependências no DAG.
-- models/intermediate/int_customer_orders.sql
WITH orders AS (
SELECT * FROM {{ ref('stg_orders') }}
),
customers AS (
SELECT * FROM {{ ref('stg_customers') }}
)
SELECT
c.customer_id,
c.customer_name,
COUNT(o.order_id) AS total_orders,
SUM(o.order_amount) AS lifetime_value,
MIN(o.order_date) AS first_order_date,
MAX(o.order_date) AS last_order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.customer_nameOs marts são a camada final de consumo — tabelas limpas e documentadas que dashboards e analistas consultam diretamente.
Essa abordagem em camadas significa que uma fonte quebrada afeta apenas o staging, não o pipeline inteiro. Cada camada pode ser testada de forma independente.
A função ref() e a resolução do DAG
ref() não é um alias. Chamar {{ ref('stg_orders') }} faz duas coisas: resolve o nome de tabela totalmente qualificado em tempo de compilação e registra uma aresta de dependência no grafo acíclico direcionado (DAG) do dbt. Sem ref(), o dbt não tem como determinar a ordem de execução.
Um erro comum é fixar os nomes de tabela no código em vez de usar ref(). Isso quebra o rastreamento de dependências e pode levar a modelos sendo executados antes de suas dependências a montante estarem prontas.
-- Ruim: referência fixa no código, sem rastreamento de dependência
SELECT * FROM analytics.stg_orders
-- Bom: ref() registra a dependência
SELECT * FROM {{ ref('stg_orders') }}O DAG também alimenta recursos como dbt run --select stg_orders+, que executa um modelo e tudo que está a jusante dele — útil para reconstruções direcionadas após uma mudança de esquema na fonte.
Materializações: escolher a estratégia de armazenamento certa
O dbt oferece quatro materializações integradas, cada uma adequada a diferentes padrões de acesso e volumes de dados.
| Materialização | Armazenamento | Ideal para | Compromisso |
|----------------|---------|----------|----------|
| view | Sem armazenamento (calculada na leitura) | Transformações leves, conjuntos pequenos | Leituras lentas em dados grandes |
| table | Reconstrução completa a cada execução | Modelos da camada mart, leituras rápidas | Reconstrói tudo, custo maior |
| incremental | Só anexa/mescla linhas novas | Tabelas fato grandes, fluxos de eventos | Lógica mais complexa, exige unique_key |
| ephemeral | Inserida como CTE, nunca materializada | Lógica reutilizável compartilhada entre modelos | Não consultável diretamente |
O padrão é view. Pode ser sobrescrito no bloco de config do modelo ou no dbt_project.yml:
-- models/marts/fct_daily_revenue.sql
{{
config(
materialized='incremental',
unique_key='revenue_date',
incremental_strategy='merge'
)
}}
SELECT
DATE(order_date) AS revenue_date,
SUM(order_amount) AS daily_revenue,
COUNT(DISTINCT customer_id) AS unique_customers
FROM {{ ref('stg_orders') }}
WHERE order_status = 'completed'
{% if is_incremental() %}
AND order_date > (SELECT MAX(revenue_date) FROM {{ this }})
{% endif %}
GROUP BY DATE(order_date)O bloco is_incremental() só roda durante builds incrementais — em um full refresh (dbt run --full-refresh), ele é ignorado e a tabela inteira é reconstruída.
Pronto para mandar bem nas entrevistas de Data Engineering?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
Testar a qualidade dos dados em cada camada
O dbt fornece duas categorias de testes: testes genéricos declarados em YAML e testes singulares escritos como arquivos SQL independentes que retornam linhas quando falham.
Os testes genéricos cobrem restrições estruturais:
# models/staging/_stg_models.yml
version: 2
models:
- name: stg_orders
columns:
- name: order_id
tests:
- unique # Sem IDs de pedido duplicados
- not_null # Toda linha tem um ID de pedido
- name: order_status
tests:
- accepted_values:
values: ['completed', 'pending', 'cancelled', 'refunded']
- name: customer_id
tests:
- not_null
- relationships: # Verificação de integridade referencial
to: ref('stg_customers')
field: customer_idOs testes singulares validam regras de negócio que os genéricos não conseguem expressar:
-- tests/assert_revenue_never_negative.sql
-- Retorna linhas onde a receita diária é negativa (deveria retornar 0 linhas)
SELECT
revenue_date,
daily_revenue
FROM {{ ref('fct_daily_revenue') }}
WHERE daily_revenue < 0Os testes unitários, introduzidos no dbt 1.8, validam a lógica de transformação com entradas controladas e saídas esperadas — sem necessidade de warehouse. Eles rodam diretamente na fase de teste do dbt.
A estratégia de testes deve seguir o fluxo de dados: checagens de frescor e validações de contagem de linhas no nível da fonte, testes de esquema no nível de staging (not_null, unique), integridade referencial no nível intermediário e asserções de lógica de negócio no nível de mart.
dbt Core v1.10 e o motor Fusion
O dbt Core v1.10 introduziu a flag --sample para os comandos run e build. Essa flag aplica amostragem baseada em tempo a ref() e source(), permitindo que os desenvolvedores validem transformações sobre um subconjunto de dados sem o custo de um build completo. Útil para iterar sobre modelos apoiados em tabelas fato com bilhões de linhas.
O motor dbt Fusion, uma reescrita do zero em Rust, agora é o padrão para projetos novos no dbt Cloud sobre Snowflake, BigQuery, Redshift e Databricks. O Fusion introduz versionamento semântico a partir da 2.0 e traz melhorias significativas de desempenho na compilação e na execução.
Outras adições notáveis de 2026: a especificação YAML do Semantic Layer para definições de métricas centralizadas, o Cost Insights (beta) para estimar a computação de warehouse por modelo, e os pacotes privados nativos agora em disponibilidade geral.
Macros e Jinja para lógica reutilizável
Quando o mesmo padrão SQL aparece em vários modelos, convém extraí-lo para uma macro. As macros são funções Jinja armazenadas no diretório macros/.
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name) %}
ROUND(CAST({{ column_name }} AS DECIMAL(10, 4)) / 100, 2)
{% endmacro %}Chamada em qualquer modelo:
-- models/staging/stg_payments.sql
SELECT
payment_id,
order_id,
{{ cents_to_dollars('amount_cents') }} AS amount_dollars,
payment_method
FROM {{ source('stripe', 'payments') }}Isso mantém a base de código DRY. A alternativa — copiar a fórmula de conversão em cada modelo que precisa dela — cria carga de manutenção e inconsistências.
Perguntas de entrevista que realmente são feitas
As entrevistas de data engineering em 2026 testam cada vez mais o conhecimento de dbt além do básico. Estas são as perguntas que diferenciam os candidatos, extraídas de padrões observados em vagas de empresas que usam stacks de dados modernos.
P: Um modelo incremental reprocessou a tabela inteira durante a noite. O que aconteceu?
A causa mais comum: a coluna unique_key continha valores nulos. Quando o dbt tenta um MERGE sobre uma chave nula, a correspondência falha para cada linha, então ele insere duplicatas. A segunda causa: alguém executou dbt run --full-refresh sem perceber que isso reconstrói a tabela do zero. A terceira: o filtro is_incremental() referenciava uma coluna que ainda não existe na tabela de destino (primeira execução vs. execuções subsequentes). A depuração começa verificando o SQL compilado em target/compiled/.
P: Como os testes devem ser organizados em camadas em um projeto dbt de produção?
As checagens de frescor da fonte rodam primeiro — se a fonte não foi atualizada, os modelos a jusante não deveriam rodar sobre dados obsoletos. Os testes de staging validam restrições de esquema: chaves primárias (unique + not_null), valores aceitos e conversões de tipo. Os testes intermediários verificam a integridade referencial através dos joins. Os testes de mart afirmam invariantes de negócio: receita >= 0, usuários ativos >= usuários pagantes, e a soma das partes igual ao total. Todos os testes rodam em CI nos pull requests contra um esquema de dev, e depois novamente após o deploy em produção com alertas.
P: Quando um modelo deve ser efêmero em vez de uma view?
Os modelos efêmeros inserem seu SQL como CTE nos modelos consumidores — útil para lógica reutilizável que não precisa ser consultada de forma independente. As views são calculadas na leitura e existem como objetos consultáveis. O compromisso: modelos efêmeros não podem ser testados diretamente (não têm tabela sobre a qual rodar asserções) nem aparecer em ferramentas de linhagem de dados. Use efêmeros para lógica auxiliar interna; use views para qualquer coisa que precise de testes ou monitoramento independente.
P: Explique a diferença entre source() e ref().
source() aponta para tabelas crus definidas em um arquivo sources.yml — são tabelas que o dbt não gerencia. ref() aponta para outros modelos dbt. Ambos registram dependências no DAG, mas source() também habilita as checagens de frescor (dbt source freshness), o que ref() não faz. Usar source() para tabelas crus e ref() para todo o resto não é opcional — é assim que o dbt sabe o que ele controla e o que não controla.
Para um conjunto mais amplo de perguntas de entrevista de data engineering, incluindo tópicos como a arquitetura de pipelines ETL vs ELT, os módulos de prática do SharpSkill sobre fundamentos de dbt e padrões avançados de dbt cobrem esses conceitos com exercícios interativos.
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Conclusão
- O dbt transforma os dados crus do warehouse por meio de instruções SQL SELECT, compiladas automaticamente em DDL — a estrutura em camadas staging/intermediário/mart mantém cada modelo focado em uma única responsabilidade
- A função
ref()é a espinha dorsal do gerenciamento de dependências: resolve os nomes de tabela e constrói o DAG que controla a ordem de execução - As materializações incrementais reduzem o custo em tabelas grandes, mas exigem um tratamento cuidadoso de
unique_key, dos valores nulos e do filtrois_incremental() - Os testes devem seguir o fluxo de dados — frescor da fonte, restrições de esquema no staging, integridade referencial no intermediário, asserções de lógica de negócio no nível de mart
- O dbt Core v1.10 traz a flag
--samplepara uma iteração mais rápida, enquanto o motor Fusion (baseado em Rust, versão 2.0) agora é o padrão no dbt Cloud - As perguntas de entrevista sobre dbt focam em depurar falhas reais (reprocessamento incremental, chaves nulas, fontes obsoletas) em vez de definições — a experiência prática com SQL compilado e pipelines de CI importa mais do que respostas decoradas
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Tags
Compartilhar
Artigos relacionados

Top 25 Perguntas de Entrevista para Engenharia de Dados em 2026
Guia completo com as 25 perguntas mais relevantes para entrevistas de engenharia de dados em 2026. Inclui SQL, Spark, Kafka, ETL/ELT, modelagem de dados e design de pipelines.

ETL vs ELT em 2026: Arquitetura de Pipelines de Dados Explicada
Comparação completa entre arquiteturas ETL e ELT para pipelines de dados em 2026, incluindo análise de custos, performance, exemplos de código e critérios de decisão para engenheiros de dados.

Apache Kafka para Engenheiros de Dados: Particionamento, Consumer Groups e Pipelines de Streaming
Guia completo de Apache Kafka para engenharia de dados: arquitetura KRaft, estrategias de particionamento, consumer groups, CDC com Debezium, exactly-once semantics, Share Groups e perguntas de entrevista com exemplos praticos em Python.