CI/CD voor Data Engineering

Waarom CI/CD voor data anders is dan voor applicatiecode, dbt Slim CI met state comparison om alleen gewijzigde modellen te bouwen, omgevingspromotie, secrets management, en waarom rollbacks bij data pipelines fundamenteel lastiger zijn.

11 min leestijd Gemiddeld Bijgewerkt: 2026-08-15 DevOps

Introductie

CI/CD-principes zijn in eerdere hoofdstukken al toegepast op specifieke situaties — het adf_publish-patroon in Azure Data Factory, Databricks Repos in Databricks, workspace-Git-integratie in Microsoft Fabric. Dit hoofdstuk behandelt de generieke principes erachter, en vooral waarom CI/CD voor data pipelines wezenlijk anders is dan voor gewone applicatiecode — een verschil dat vaak wordt onderschat door teams die CI/CD-praktijken direct overnemen uit software engineering zonder de data-specifieke complicaties te herkennen.

Waarom CI/CD voor Data Anders Is

Applicatiecode is doorgaans stateless in de zin die hier telt: een nieuwe versie deployen vervangt de oude volledig, en een rollback naar de vorige versie is meestal net zo simpel als het vorige artefact opnieuw deployen. Data pipelines zijn dat niet — ze werken op stateful tabellen die historie opbouwen. Een gewijzigd transformatiemodel dat een fout bevat, kan al foute data hebben weggeschreven vóórdat het probleem wordt opgemerkt; een "rollback" van de code lost het probleem in de tabel zelf niet op, zoals verderop in dit hoofdstuk wordt uitgewerkt.

Een tweede verschil: testen tegen realistische datavolumes is duur en traag. Een unit test voor een webapplicatie draait in milliseconden; een dbt-model tegen een productiekopie van een tabel met miljarden rijen testen, kost warehouse-compute en tijd die niet past in een snelle CI-feedbackloop. Dit dwingt tot compromissen — kleinere testsets, steekproeven, of slimmer selecteren wát je test — die bij applicatiecode zelden nodig zijn.

De Stadia: Lint, Test, Build, Deploy

Een typische CI/CD-pijplijn voor een dbt-project doorloopt vier stadia, elk met een specifiek data-engineering-doel.

Lint — SQL-stijl en -correctheid controleren vóórdat er iets draait, met tools als sqlfluff, dat SQL-conventies (consistente hoofdlettergebruik, geen SELECT *, consistente inspringing) afdwingt op dezelfde manier als een Python-linter code-stijl afdwingt.

Test — de dbt-tests uit Medallion Architecture en dbt Fundamentals daadwerkelijk uitvoeren tegen een test- of staging-omgeving, niet tegen productie.

Build — de modellen daadwerkelijk materialiseren in een tijdelijke of staging-schema, om te verifiëren dat de SQL syntactisch en logisch correct compileert en uitvoert op het doelplatform.

Deploy — bij succes, de wijziging daadwerkelijk naar productie promoten — het adf_publish-mechanisme uit Azure Data Factory is hier een platformspecifiek voorbeeld van; voor dbt is dit doorgaans simpelweg dbt run tegen het productie-target.

# .github/workflows/dbt_ci.yml (vereenvoudigd)
name: dbt CI
on: [pull_request]
jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install dbt-snowflake
      - run: sqlfluff lint models/ --dialect snowflake
      - run: dbt build --target ci

dbt Slim CI: Alleen Bouwen wat Veranderde

Een naïeve CI-pijplijn draait dbt build tegen het volledige project bij elke pull request — correct, maar traag zodra een project honderden modellen bevat, en onnodig: een wijziging aan één silver-model raakt zelden meer dan een handvol downstream gold-modellen. Slim CI lost dit op met state comparison: dbt vergelijkt de huidige staat van het project met een opgeslagen manifest.json van de laatst succesvolle productierun, en bouwt/test alleen de modellen die daadwerkelijk zijn gewijzigd, plus hun downstream-afhankelijkheden:

dbt build --select state:modified+ --state ./prod-manifest

state:modified selecteert modellen waarvan de gecompileerde SQL is gewijzigd ten opzichte van het productie-manifest; de + erachter breidt dat uit naar alles wat daarvan afhangt (dezelfde ref()-graph-logica uit dbt Fundamentals, hier gebruikt om selectief te bouwen in plaats van om uitvoeringsvolgorde te bepalen). Voor een project met honderden modellen kan dit het verschil betekenen tussen een CI-run van twintig minuten en een van twee — direct relevant voor hoe snel een team feedback krijgt op een pull request, en dus hoe vaak ze bereid zijn kleine, veilige wijzigingen te maken in plaats van grote, risicovolle batches.

Genereer dbt-modellen met de juiste tests direct

Zodat elke wijziging al vanaf het begin CI-klaar is.

Open dbt Generator

Omgevingspromotie: Dev, Staging, Productie

De dev/prod-scheiding via profiles.yml-targets uit dbt Fundamentals is de basis; een volledige CI/CD-flow voegt daar een tussenliggende staging-omgeving aan toe, waar wijzigingen automatisch worden gedeployed en getest vóórdat een mens ze goedkeurt voor productie:

Feature branch → PR geopend → CI draait Slim CI tegen 'ci'-target
                                        ↓ (bij succes)
                              Merge naar main → auto-deploy naar 'staging'
                                        ↓ (na handmatige goedkeuring/smoke test)
                              Release-tag → deploy naar 'prod'

Dit patroon — automatisch tot staging, bewuste, vaak handmatige stap naar productie — balanceert snelheid (de meeste feedback komt automatisch en snel) met voorzichtigheid (de laatste stap naar productie, waar een fout het duurst is om te herstellen, blijft een bewuste beslissing).

Secrets Management: Nooit in Code

Elke CI/CD-pijplijn heeft toegang nodig tot warehouse-credentials, API-sleutels, en andere gevoelige configuratie — en die horen nooit in code of in platte configuratiebestanden die met het project worden meegecommit. GitHub Actions, Azure DevOps en vergelijkbare platformen bieden een ingebouwde, versleutelde secrets store, waarbij de CI-pijplijn secrets als omgevingsvariabelen injecteert tijdens de run, zonder ze ooit in logs of in de repository zelf bloot te leggen:

- run: dbt build --target prod
  env:
    DBT_SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PROD_PASSWORD }}

Voor complexere organisaties is een dedicated secrets-manager (Azure Key Vault, HashiCorp Vault, AWS Secrets Manager) de volgende stap — met als voordeel centrale rotatie en auditlogging van wie welk secret wanneer heeft opgehaald, iets wat een simpele CI-platform-secrets-store meestal niet biedt.

Rollbacks: Waarom Data Pipelines Fundamenteel Lastiger Zijn

Bij een applicatie is een rollback meestal eenvoudig: deploy de vorige versie van de code opnieuw. Bij een data pipeline is de code maar de helft van het probleem — als een gewijzigd model al enkele uren lang foute data naar gold.revenue_by_category heeft geschreven vóórdat het probleem werd opgemerkt, lost het terugzetten van de oude code die al geschreven foute data niet met terugwerkende kracht op.

Dit is precies waarom de medallion-architectuur uit Medallion Architecture zo waardevol is in een CI/CD-context: omdat bronze onveranderlijk blijft, kan een rollback bestaan uit (1) de code terugzetten naar de vorige, correcte versie, én (2) de betrokken silver/gold-tabellen herbouwen vanaf bronze met die herstelde code — iets dat zonder een bewaarde, ongewijzigde bronze-laag simpelweg niet mogelijk zou zijn. Voor Delta Lake-tabellen specifiek biedt time travel uit Delta Lake een extra, snellere optie: terugkeren naar een eerdere tabelversie zonder een volledige herberekening, mits het probleem recent genoeg is en binnen de VACUUM-retentie valt.

Zero-Downtime Deployments: Tabellen Wisselen zonder Onderbreking

Een gold-tabel die halverwege een TRUNCATE + INSERT-deploy staat, is voor elke query die op dat moment binnenkomt tijdelijk leeg of incompleet — zichtbaar voor downstream-dashboards en -consumenten als een storing, ook al is de deploy zelf "gelukt". De gangbare oplossing is een swap-patroon: bouw de nieuwe versie van de tabel volledig onder een tijdelijke naam, en wissel pas daarna, in één atomaire operatie, de naam om met de bestaande tabel:

-- Bouw de nieuwe versie volledig, onder een tijdelijke naam
CREATE OR REPLACE TABLE gold.revenue_by_category_new AS
SELECT ... FROM silver.orders ...;

-- Atomaire wissel: geen moment waarop de tabel leeg of half-gevuld zichtbaar is
ALTER TABLE gold.revenue_by_category_new SWAP WITH gold.revenue_by_category;
DROP TABLE gold.revenue_by_category_new;   -- bevat nu de oude versie

Dit garandeert dat elke query die de tabel bevraagt, óf de volledige oude versie óf de volledige nieuwe versie ziet — nooit een tussentijdse, incomplete staat. Voor dbt-projecten wordt dit patroon vaak automatisch toegepast via de standaard table-materialization, die intern al een vergelijkbare create-and-swap-aanpak hanteert in plaats van een bestaande tabel direct te legen en opnieuw te vullen — relevant om te weten omdat het verklaart waarom een dbt-run zelden zichtbare downtime veroorzaakt, zelfs bij volledige herberekening van een druk bevraagde tabel.

Testdata: het Spanningsveld tussen Realisme en Snelheid

CI-runs die tegen een volledige productiekopie testen, zijn traag en kostbaar; runs tegen volledig verzonnen, kleine testsets missen vaak precies de edge cases (rommelige waarden, onverwachte NULL's, schaalgerelateerde problemen) die een productie-incident veroorzaken. Twee gangbare compromissen adresseren dit.

Seeds en gecureerde sample-sets — een klein, met opzet samengesteld bestand (dbt seeds, zie dbt Fundamentals) dat bekende edge cases bevat: een order met een NULL-klant-ID, een extreem grote bestelling, een record met een ongebruikelijk teken in een tekstveld. Dit test gerichte scenario's snel, zonder een volledige datakopie nodig te hebben.

Gemaskeerde productiesteekproeven — een representatieve, willekeurige steekproef van echte productiedata, met gevoelige velden gemaskeerd of gepseudonimiseerd (dezelfde technieken als bij de ETL/ELT-compliance-afweging uit ETL vs ELT) vóór het als CI-testdata wordt gebruikt. Dit vangt realistische datakwaliteitsproblemen op die een handmatig samengestelde seed-set makkelijk mist, zonder de compliance-risico's van ongemaskeerde persoonsgegevens in een minder streng beveiligde CI-omgeving.

De praktische vuistregel: gebruik seeds voor gerichte, bekende edge cases die je bewust wilt blijven testen, en een periodiek ververste, gemaskeerde steekproef voor bredere, realistischere dekking — niet het een óf het ander, maar beide voor een ander doel.

Best Practices

  • Gebruik Slim CI zodra een project meer dan een handvol modellen bevat — de tijdsbesparing is direct voelbaar en stimuleert kleinere, veiligere pull requests.
  • Automatiseer deploys tot staging, houd productie-deploys bewust (handmatige goedkeuring of een expliciete release-stap) — snelheid waar het kan, voorzichtigheid waar het telt.
  • Gebruik nooit hardcoded credentials, ook niet "tijdelijk voor het testen" — dat tijdelijke wordt vrijwel altijd vergeten en blijft staan.
  • Behandel bronze als je rollback-vangnet. Een medallion-architectuur die herbouwbaarheid garandeert (zie Medallion Architecture) is een directe, praktische CI/CD-asset, geen abstract architectuurprincipe.
  • Test lint- en syntaxfouten vroeg en goedkoop, vóór je overgaat tot dure build/test-stadia tegen echte compute.
  • Gebruik het swap-patroon (of vertrouw op dbt's ingebouwde equivalent) voor elke druk bevraagde gold-tabel, zodat deploys nooit zichtbaar zijn als tijdelijke storing voor eindgebruikers.
  • Combineer gerichte seeds met een periodiek ververste, gemaskeerde productiesteekproef voor CI-testdata, in plaats van te kiezen voor slechts één van de twee.

Veelgemaakte Fouten

  • Het volledige project bij elke PR bouwen in plaats van Slim CI te gebruiken, wat CI-runs onnodig traag maakt en teams ontmoedigt om vaak, in kleine stappen te committen.
  • Rechtstreeks naar productie deployen zonder een staging-tussenstap, waardoor de eerste keer dat een wijziging tegen realistische data draait, ook meteen de eerste keer is dat het productie raakt.
  • Secrets in profiles.yml of vergelijkbare configuratiebestanden hardcoderen en per ongeluk meecommitten naar een repository.
  • Rollback van alleen de code verwachten zonder de reeds geschreven foute data te herstellen — een veelgemaakte misvatting die pas bij het eerste echte incident pijnlijk duidelijk wordt.
  • Geen lint-stap vóór de dure build/test-stadia, waardoor triviale syntaxfouten pas laat in de pijplijn — en dus langzamer — worden ontdekt.
  • Direct TRUNCATE + INSERT gebruiken op een druk bevraagde tabel in plaats van een swap-patroon, waardoor gebruikers tijdens elke deploy een leeg of incompleet resultaat kunnen zien.
  • Alleen handmatig samengestelde, kleine testsets gebruiken, waardoor CI stelselmatig de rommelige edge cases mist die alleen in echte productiedata voorkomen.

Performance Tips

  • Gebruik state:modified+ in elke PR-CI-run, en reserveer een volledige dbt build voor een periodieke (bijvoorbeeld nachtelijke) validatie tegen het complete project.
  • Cache dependency-installaties (Python packages, dbt packages) tussen CI-runs waar het CI-platform dat ondersteunt, om herhaalde downloadtijd te vermijden.
  • Parallelliseer onafhankelijke CI-stadia (lint en een deel van de tests kunnen vaak gelijktijdig draaien) in plaats van alles strikt sequentieel uit te voeren.
  • Gebruik een kleinere, representatieve testset voor CI-builds in plaats van een volledige productiekopie, en bewaar zware, volledige-datavolumetests voor een aparte, minder frequente validatiecyclus.

Interviewvragen

"Waarom is CI/CD voor data pipelines fundamenteel anders dan voor applicatiecode?" Let op: data pipelines zijn stateful (een rollback van code lost al geschreven foute data niet op) en testen tegen realistische volumes is duur/traag — twee complicaties die bij de meeste applicatiecode niet spelen.

"Wat is dbt Slim CI, en hoe werkt state comparison?" Let op: het vergelijken van de huidige projectstaat met een opgeslagen productiemanifest om alleen gewijzigde modellen (plus hun downstream-afhankelijkheden via +) te bouwen en testen, in plaats van het hele project.

"Hoe zou je een rollback aanpakken als een gewijzigd dbt-model al enkele uren foute data heeft geschreven?" Let op: de code terugzetten is niet genoeg — de betrokken tabellen moeten herbouwd worden vanaf een onveranderlijke bronze-laag (of hersteld via Delta Lake time travel), niet alleen de codewijziging ongedaan gemaakt.

"Hoe zou je secrets beheren in een CI/CD-pijplijn voor een dataplatform?" Let op: nooit hardcoded in code of configuratiebestanden; gebruik de ingebouwde secrets-store van het CI-platform of een dedicated secrets-manager, geïnjecteerd als omgevingsvariabelen tijdens de run.

"Beschrijf een typische omgevingspromotie-flow voor een dbt-project." Let op: feature branch → PR met Slim CI → merge naar main met auto-deploy naar staging → bewuste, vaak handmatige stap naar productie — automatisering waar veilig, een menselijke controle waar de impact het grootst is.

"Hoe zorg je ervoor dat een tabel-deploy geen tijdelijke downtime of incomplete data veroorzaakt?" Let op: het swap-patroon — de nieuwe versie volledig onder een tijdelijke naam bouwen, dan atomair van naam wisselen met de bestaande tabel, zodat elke query óf de volledige oude óf de volledige nieuwe versie ziet, nooit een tussenstaat.

"Hoe zou je CI-testdata samenstellen zonder de kosten en risico's van een volledige productiekopie?" Let op: een combinatie van gerichte seeds voor bekende edge cases en een periodiek ververste, gemaskeerde productiesteekproef voor bredere, realistischere dekking — niet volledig verzonnen data en niet ongemaskeerde productiedata.

Relevante Documentatie

Samenvatting

CI/CD voor data engineering bouwt voort op dezelfde principes als applicatie-CI/CD — lint, test, build, deploy, geautomatiseerde omgevingspromotie — maar met twee fundamentele complicaties die specifieke oplossingen vereisen: data pipelines zijn stateful, waardoor een codewijziging terugdraaien niet automatisch de al geschreven foute data herstelt, en testen tegen realistische volumes is duur, wat Slim CI's state-comparison-aanpak (alleen gewijzigde modellen bouwen) tot een noodzakelijke, niet optionele optimalisatie maakt zodra een project groeit. Medallion architecture's onveranderlijke bronze-laag is hier niet alleen een architectuurprincipe maar een direct bruikbaar rollback-mechanisme, en het swap-patroon zorgt ervoor dat elke deploy zelf al onzichtbaar blijft voor eindgebruikers, los van of er later alsnog een rollback nodig blijkt. Met CI/CD als proces behandeld, verschuift het volgende hoofdstuk naar het fundament eronder: Git zelf, en de branchingstrategieën die deze hele workflow mogelijk maken.

Hulp nodig bij het bouwen van een modern dataplatform?

DataPartner365 helpt organisaties met Microsoft Fabric, Snowflake, Databricks, dbt, Azure, data-architectuur en CI/CD.

Neem contact op met DataPartner365