dbt w 2026 roku: transformacje danych, testowanie i pytania rekrutacyjne
Praktyczny przewodnik po dbt: modelowanie warstwowe, materializacje, testowanie jakości danych, makra Jinja i pytania rekrutacyjne dla inżynierów danych 2026.

dbt (data build tool) stał się standardowym frameworkiem do transformacji danych wewnątrz nowoczesnych hurtowni — w 2026 roku korzysta z niego ponad 40 000 firm w środowiskach produkcyjnych. Niniejszy artykuł omawia kluczowe mechanizmy transformacji w dbt, strategie testowania jakości danych oraz pytania, które rzeczywiście pojawiają się na rozmowach kwalifikacyjnych z zakresu inżynierii danych.
dbt obsługuje literę T w podejściu ELT. Kompiluje instrukcje SQL SELECT do DDL (CREATE TABLE, CREATE VIEW, MERGE) i wykonuje je w hurtowni danych — Snowflake, BigQuery, Redshift lub Databricks. Kontrola wersji, rozwiązywanie zależności, testowanie i dokumentacja są wbudowane.
Modelowanie warstwowe: Staging, Intermediate i Marts
Dobrze zaprojektowany projekt dbt dzieli transformacje na trzy warstwy. Podejście to, spopularyzowane przez społeczność dbt, wymusza zasadę pojedynczej odpowiedzialności modeli i znacząco upraszcza debugowanie.
Staging — modele najbliższe surowym danym. Ich zakres jest ściśle ograniczony: zmiana nazw kolumn, rzutowanie typów i filtrowanie nieprawidłowych wierszy. Bez złączeń, bez agregacji.
-- 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 NULLIntermediate — modele stosujące logikę biznesową: złączenia, agregacje, funkcje okna. Odwołują się do modeli staging poprzez funkcję ref(), która rejestruje zależności w grafie 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_nameMarts — warstwa końcowa przeznaczona do konsumpcji. Czyste, udokumentowane tabele, z których bezpośrednio korzystają dashboardy i analitycy.
Takie podejście warstwowe oznacza, że awaria źródła wpływa wyłącznie na warstwę staging, a nie na cały pipeline. Każda warstwa może być testowana niezależnie.
Funkcja ref() i rozwiązywanie grafu DAG
ref() to nie alias. Wywołanie {{ ref('stg_orders') }} wykonuje dwie operacje: rozwiązuje pełną kwalifikowaną nazwę tabeli w czasie kompilacji oraz rejestruje krawędź zależności w skierowanym grafie acyklicznym (DAG) dbt. Bez ref() dbt nie ma możliwości określenia kolejności wykonania modeli.
Częstym błędem jest kodowanie nazw tabel na sztywno zamiast korzystania z ref(). Powoduje to przerwanie śledzenia zależności i może prowadzić do sytuacji, w której modele uruchamiają się przed gotowością ich zależności upstream.
-- Bad: hardcoded reference, no dependency tracking
SELECT * FROM analytics.stg_orders
-- Good: ref() registers the dependency
SELECT * FROM {{ ref('stg_orders') }}Graf DAG napędza również funkcje takie jak dbt run --select stg_orders+, która uruchamia model i wszystko, co znajduje się poniżej w hierarchii zależności — przydatne przy ukierunkowanych przebudowach po zmianie schematu źródła.
Materializacje: wybór odpowiedniej strategii przechowywania
dbt oferuje cztery wbudowane materializacje, z których każda jest dostosowana do różnych wzorców dostępu i wolumenów danych.
| Materialization | Storage | Best for | Trade-off |
|----------------|---------|----------|----------|
| view | No storage (computed on read) | Lightweight transforms, small datasets | Slow reads on large data |
| table | Full table rebuild each run | Mart-layer models, fast reads | Rebuilds everything, higher cost |
| incremental | Appends/merges new rows only | Large fact tables, event streams | More complex logic, needs unique_key |
| ephemeral | Inlined as CTE, never materialized | Reusable logic shared across models | Cannot be queried directly |
Domyślną materializacją jest view. Zmiana następuje poprzez blok konfiguracyjny w modelu lub w pliku 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)Blok is_incremental() wykonuje się wyłącznie podczas przyrostowych buildów — przy pełnym odświeżeniu (dbt run --full-refresh) jest pomijany, a cała tabela zostaje przebudowana od podstaw.
Gotowy na rozmowy o Data Engineering?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Testowanie jakości danych na każdej warstwie
dbt udostępnia dwie kategorie testów: testy generyczne deklarowane w plikach YAML oraz testy jednostkowe (singular tests) zapisywane jako samodzielne pliki SQL, które zwracają wiersze w przypadku niepowodzenia.
Testy generyczne obejmują ograniczenia strukturalne:
# models/staging/_stg_models.yml
version: 2
models:
- name: stg_orders
columns:
- name: order_id
tests:
- unique
- not_null
- name: order_status
tests:
- accepted_values:
values: ['completed', 'pending', 'cancelled', 'refunded']
- name: customer_id
tests:
- not_null
- relationships:
to: ref('stg_customers')
field: customer_idTesty jednostkowe walidują reguły biznesowe, których testy generyczne nie są w stanie wyrazić:
-- tests/assert_revenue_never_negative.sql
SELECT
revenue_date,
daily_revenue
FROM {{ ref('fct_daily_revenue') }}
WHERE daily_revenue < 0Unit tests, wprowadzone w dbt 1.8, umożliwiają walidację logiki transformacji z kontrolowanymi danymi wejściowymi i oczekiwanymi wynikami — bez konieczności korzystania z hurtowni. Wykonują się bezpośrednio w fazie dbt test.
Strategia testowania powinna podążać za przepływem danych: sprawdzanie świeżości źródeł i walidacja liczby wierszy, testy schematów na warstwie staging (not_null, unique), integralność referencyjna na warstwie intermediate oraz asercje logiki biznesowej na warstwie marts.
dbt Core v1.10 i silnik Fusion
dbt Core v1.10 wprowadził flagę --sample dla poleceń run i build. Flaga ta stosuje próbkowanie czasowe do ref() i source(), umożliwiając programistom walidację transformacji na podzbiorze danych bez kosztu pełnego buildu. Szczególnie przydatne przy iterowaniu nad modelami opartymi na tabelach faktów z miliardami wierszy.
Silnik dbt Fusion, napisany od podstaw w Rust, jest obecnie domyślnym silnikiem dla nowych projektów w dbt Cloud na Snowflake, BigQuery, Redshift i Databricks. Fusion wprowadza wersjonowanie semantyczne począwszy od wersji 2.0 i przynosi znaczące usprawnienia wydajności kompilacji i wykonywania.
Inne istotne nowości 2026 roku: specyfikacja Semantic Layer YAML do scentralizowanych definicji metryk, Cost Insights (beta) do szacowania kosztu obliczeniowego hurtowni dla poszczególnych modeli oraz natywne pakiety prywatne, które są teraz ogólnie dostępne.
Makra i Jinja jako logika wielokrotnego użytku
Gdy ten sam wzorzec SQL pojawia się w wielu modelach, należy wyekstrahować go do makra. Makra to funkcje Jinja przechowywane w katalogu macros/.
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name) %}
ROUND(CAST({{ column_name }} AS DECIMAL(10, 4)) / 100, 2)
{% endmacro %}Wywołanie w dowolnym modelu:
-- models/staging/stg_payments.sql
SELECT
payment_id,
order_id,
{{ cents_to_dollars('amount_cents') }} AS amount_dollars,
payment_method
FROM {{ source('stripe', 'payments') }}Takie podejście utrzymuje bazę kodu zgodną z zasadą DRY. Alternatywa — kopiowanie formuły konwersji do każdego modelu, który jej potrzebuje — generuje narzut utrzymaniowy i niespójności.
Pytania rekrutacyjne, które faktycznie padają na rozmowach
Rozmowy kwalifikacyjne z zakresu inżynierii danych w 2026 roku coraz częściej sprawdzają wiedzę o dbt wykraczającą poza podstawy. Poniżej przedstawiono pytania, które odróżniają kandydatów — opracowane na podstawie wzorców obserwowanych w firmach korzystających z nowoczesnych stosów danych.
P: Model przyrostowy przetworzyła ponownie całą tabelę w nocy. Co się stało?
Najczęstsza przyczyna: kolumna unique_key zawierała wartości NULL. Gdy dbt wykonuje operację MERGE na kluczu z wartością NULL, dopasowanie nie następuje dla żadnego wiersza, co skutkuje wstawieniem duplikatów. Druga przyczyna: ktoś uruchomił dbt run --full-refresh, nie zdając sobie sprawy, że przebudowuje tabelę od podstaw. Trzecia: filtr is_incremental() odwoływał się do kolumny, która jeszcze nie istnieje w tabeli docelowej (pierwsze uruchomienie vs kolejne). Debugowanie rozpoczyna się od sprawdzenia skompilowanego SQL w katalogu target/compiled/.
P: W jaki sposób powinno się warstwować testy w produkcyjnym projekcie dbt?
Najpierw uruchamiane są testy świeżości źródeł — jeśli źródło nie zostało zaktualizowane, modele downstream nie powinny przetwarzać nieaktualnych danych. Testy na warstwie staging walidują ograniczenia schematu: klucze główne (unique + not_null), dopuszczalne wartości i rzutowania typów. Testy na warstwie intermediate sprawdzają integralność referencyjną między złączeniami. Testy na warstwie marts weryfikują niezmienniki biznesowe: przychód >= 0, aktywni użytkownicy >= płacący użytkownicy, suma części równa się całości. Wszystkie testy uruchamiane są w CI na pull requestach wobec schematu deweloperskiego, a następnie ponownie po wdrożeniu w produkcji z alertingiem.
P: Kiedy model powinien być ephemeral, a kiedy view?
Modele ephemeral osadzają swój SQL jako CTE w modelach konsumujących — przydatne dla logiki wielokrotnego użytku, która nie musi być odpytywana niezależnie. Widoki (views) obliczają się przy odczycie i istnieją jako odpytywalne obiekty. Kompromis: modeli ephemeral nie da się testować bezpośrednio (nie mają tabeli, na której można uruchomić asercje) i nie pojawiają się w narzędziach do śledzenia liniowości danych. Ephemeral stosuje się dla wewnętrznej logiki pomocniczej; views dla wszystkiego, co wymaga niezależnego testowania lub monitoringu.
P: Wyjaśnij różnicę między source() a ref().
source() wskazuje na surowe tabele zdefiniowane w pliku sources.yml — są to tabele, którymi dbt nie zarządza. ref() wskazuje na inne modele dbt. Oba rejestrują zależności w grafie DAG, ale source() dodatkowo umożliwia sprawdzanie świeżości (dbt source freshness), czego ref() nie oferuje. Stosowanie source() dla surowych tabel i ref() dla wszystkiego innego nie jest opcjonalne — w ten sposób dbt wie, co kontroluje, a czego nie.
Szerszy zestaw pytań rekrutacyjnych z inżynierii danych, obejmujący tematy takie jak architektura potoków ETL vs ELT, jest dostępny w modułach ćwiczeniowych SharpSkill dotyczących podstaw dbt oraz zaawansowanych wzorców dbt, które zawierają interaktywne zadania.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Podsumowanie
- dbt transformuje surowe dane w hurtowni za pomocą instrukcji SQL SELECT kompilowanych automatycznie do DDL — warstwowa struktura staging/intermediate/marts zapewnia, że każdy model odpowiada za jedną odpowiedzialność
- Funkcja
ref()stanowi fundament zarządzania zależnościami: rozwiązuje nazwy tabel i buduje graf DAG kontrolujący kolejność wykonania - Materializacje przyrostowe (incremental) redukują koszty na dużych tabelach, ale wymagają starannej obsługi
unique_key, wartości NULL oraz filtruis_incremental() - Testowanie powinno podążać za przepływem danych — świeżość źródeł, ograniczenia schematu na warstwie staging, integralność referencyjna na warstwie intermediate, asercje logiki biznesowej na warstwie marts
- dbt Core v1.10 wprowadza flagę
--sampleprzyspieszającą iterację, a silnik Fusion (napisany w Rust, wersja 2.0) jest teraz domyślny w dbt Cloud - Pytania rekrutacyjne dotyczące dbt koncentrują się na debugowaniu rzeczywistych awarii (ponowne przetwarzanie przyrostowe, klucze NULL, nieaktualne źródła), a nie na definicjach — praktyczne doświadczenie ze skompilowanym SQL i potokami CI ma większe znaczenie niż wyuczone odpowiedzi
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Tagi
Udostępnij
Powiązane artykuły

Apache Spark 4 w 2026 roku: Nowe funkcje, Structured Streaming i pytania rekrutacyjne
Kompleksowy przewodnik techniczny po Apache Spark 4 z omowieniem trybu ANSI SQL, typu danych VARIANT, Real-Time Mode Streaming, Spark Connect oraz najwazniejszych pytan rekrutacyjnych na stanowiska Data Engineering.

25 najczęściej zadawanych pytań na rozmowie z Data Engineeringu w 2026
Kompletny przewodnik po 25 najczęściej zadawanych pytaniach na rozmowach kwalifikacyjnych z data engineeringu w 2026 roku. Obejmuje SQL, ETL/ELT, Spark, Kafka, modelowanie danych, orkiestrację pipeline'ów i projektowanie systemów z szczegółowymi odpowiedziami i przykładami kodu.

ETL vs ELT w 2026: Architektura potoków danych od podstaw
Porównanie ETL i ELT dla nowoczesnych potoków danych. Różnice architektoniczne, kompromisy wydajnościowe i zastosowania z Snowflake, BigQuery i dbt.