Waarom DCM Projects?
Stel je voor: je Snowflake-omgeving groeit. Meer tabellen, meer views, meer rollen, meerdere omgevingen (dev, test, prod). Zonder structuur ontstaat al snel een situatie waarbij niemand meer weet waarom een tabel bestaat, wie een schema heeft aangemaakt of hoe prod verschilt van test.
Het klassieke antwoord was een map met losse SQL-scripts die je handmatig uitvoerde — problematisch zodra scripts de verkeerde volgorde draaiden of al eens eerder waren uitgevoerd. Tools als Flyway en Liquibase verbeterden dit met een migration-log, maar verplaatsten het probleem naar migratiescripts die je nog steeds handmatig moest schrijven.
Snowflake DCM (Database Change Management) werkt anders: je beschrijft de gewenste eindtoestand van je objecten in definitiebestanden. De Snowflake CLI berekent het verschil met de live situatie en past alleen de noodzakelijke wijzigingen toe. Geen migratiescripts, geen volgordeproblemen — declaratief, zoals Terraform voor infrastructuur.
DCM in één zin
DCM is voor de structuur van je Snowflake-omgeving wat dbt is voor de inhoud ervan — en ze vullen elkaar aan in plaats van te concurreren. Zie ook dbt vs Snowflake DCM voor een uitgebreide vergelijking.
Snowflake Architectuur: de Basis
Om DCM goed te begrijpen, helpt het om de objecthiërarchie van Snowflake te kennen — want die weerspiegelt zich direct in de mapstructuur van een DCM Project.
- Account — de Snowflake-tenantlaag. Eén account per organisatie (of per omgeving in sommige opzetten).
- Database — een logische container voor schema's. Vergelijkbaar met een database in SQL Server of PostgreSQL.
- Schema — een logische container voor tabellen, views, stages, file formats en andere objecten.
- Tabel / View — de bekende dataobjecten.
- Warehouse — de rekenkracht (compute). Volledig losgekoppeld van opslag.
- Rol / Grant — het toegangsbeheer. Rollen worden aan gebruikers en warehouses gekoppeld.
Snowflake scheidt opslag en rekenkracht volledig. Tabellen leven in de cloud-opslag (S3/ADLS/GCS); Virtual Warehouses halen de data op bij een query. Je kunt de rekenkracht aan- en uitzetten, of meerdere warehouses naast elkaar draaien, zonder dat de data beweegt.
Al deze objecten — databases, schema's, tabellen, warehouses, rollen, stages — beheer je via DCM. Laten we dat opbouwen met een concreet voorbeeld.
Het Doorlopende Voorbeeld: JSON Eventdata
We bouwen een platform voor een webshop die klikevents in JSON-formaat ontvangt. Elke event ziet er zo uit:
{
"event_id": "evt_9f3a21",
"event_type": "product_view",
"timestamp": "2026-06-30T14:32:05Z",
"session_id": "sess_aa1b2c",
"user": {
"user_id": "usr_4421",
"country": "NL",
"platform": "mobile"
},
"product": {
"product_id": "prod_8812",
"category": "elektronica",
"price_eur": 249.99
},
"tags": ["sale", "homepage"]
}
De JSON-structuur verandert regelmatig: nieuwe velden komen erbij, bestaande velden verdwijnen of krijgen een ander formaat. Precies de situatie waar Snowflake's VARIANT-kolom voor is gemaakt.
DCM Project Structuur
Een DCM Project is een mapstructuur in Git. De Snowflake CLI leest die structuur en vertaalt hem naar Snowflake-objecten. Een typische opzet:
webshop-platform/ ├── dcm_project.yml # projectconfiguratie ├── databases/ │ └── WEBSHOP_DB/ │ ├── database.yml # database-definitie │ └── schemas/ │ ├── RAW/ │ │ ├── schema.yml │ │ ├── tables/ │ │ │ └── events_raw.yml │ │ └── stages/ │ │ └── events_stage.yml │ └── ANALYTICS/ │ ├── schema.yml │ └── views/ │ ├── v_events.yml │ └── v_events_tags.yml ├── warehouses/ │ └── WEBSHOP_WH.yml └── roles/ ├── LOADER_ROLE.yml └── ANALYST_ROLE.yml
Elk bestand beschrijft één object. De mapstructuur weerspiegelt de Snowflake-objecthiërarchie: database → schema → tabel/view/stage. Alles staat in Git — code reviews, versiegeschiedenis en rollback zijn standaard.
dcm_project.yml — de projectconfiguratie
# dcm_project.yml name: webshop-platform version: "1.0" default_environment: dev environments: dev: connection: dev_connection prod: connection: prod_connection
database.yml
# databases/WEBSHOP_DB/database.yml name: WEBSHOP_DB comment: "Centrale database voor webshop analytics"
schema.yml
# databases/WEBSHOP_DB/schemas/RAW/schema.yml name: RAW comment: "Landing zone voor ruwe brondata" managed_access: false
Tabel Definitie: JSON landen in een VARIANT-kolom
De landing-tabel voor onze JSON-events heeft één VARIANT-kolom voor de ruwe payload en een paar metadata-kolommen die Snowflake zelf vult bij het laden.
# databases/WEBSHOP_DB/schemas/RAW/tables/events_raw.yml name: EVENTS_RAW columns: - name: EVENT_ID type: VARCHAR(50) nullable: false comment: "Unieke event-ID, geëxtraheerd bij laden" - name: RAW_DATA type: VARIANT nullable: false comment: "Volledige JSON-payload — schema-vrij" - name: LOADED_AT type: TIMESTAMP_NTZ default: CURRENT_TIMESTAMP() comment: "Tijdstip van laden in Snowflake" - name: SOURCE_FILE type: VARCHAR(500) comment: "Naam van het bronbestand (S3/ADLS)" clustering_keys: [LOADED_AT] comment: "Ruwe JSON-events van de webshop frontend"
Wat is een VARIANT-kolom?
Een VARIANT-kolom in Snowflake slaat semi-gestructureerde data op: JSON, Avro, ORC, Parquet of XML. Het schema hoeft niet van tevoren vastgelegd te worden — elk record mag een andere structuur hebben. Snowflake indexeert de sleutels automatisch voor efficiënte querying.
Wanneer de JSON een nieuw veld krijgt — zeg een "discount_code" — hoef je de tabel niet te wijzigen. De VARIANT-kolom neemt het nieuwe veld gewoon op. Je past alleen de view aan die het veld parseert.
Stage Definitie: Data Laden vanuit de Cloud
Een Stage is een Snowflake-object dat wijst naar een externe opslaglocatie (S3, ADLS, GCS) of interne Snowflake-opslag. JSON-bestanden komen binnen via de stage, waarna COPY INTO ze in de landing-tabel laadt.
# databases/WEBSHOP_DB/schemas/RAW/stages/events_stage.yml name: EVENTS_STAGE type: external url: "s3://webshop-events/raw/" storage_integration: WEBSHOP_S3_INTEGRATION file_format: type: JSON strip_outer_array: true date_format: AUTO comment: "S3-stage voor inkomende JSON-events"
Met de stage op zijn plek laden we de JSON-events met een simpel COPY INTO:
-- Laden van JSON-events vanuit de stage COPY INTO RAW.EVENTS_RAW (EVENT_ID, RAW_DATA, SOURCE_FILE) FROM ( SELECT $1:event_id::VARCHAR(50), $1, METADATA$FILENAME FROM @RAW.EVENTS_STAGE ) FILE_FORMAT = (TYPE = JSON STRIP_OUTER_ARRAY = TRUE) ON_ERROR = CONTINUE;
JSON Opvragen in Snowflake
Snowflake biedt twee manieren om JSON te opvragen: puntnotatie voor directe veldextractie en FLATTEN voor het platslaan van arrays.
Puntnotatie: velden extracten
Met de :-operator navigeer je door de JSON-hiërarchie. De ::TYPE-syntax cast het resultaat naar het gewenste type.
-- Directe veldextractie uit VARIANT SELECT EVENT_ID, RAW_DATA:event_type::VARCHAR AS event_type, RAW_DATA:timestamp::TIMESTAMP_NTZ AS event_ts, RAW_DATA:session_id::VARCHAR AS session_id, -- Geneste objecten: user RAW_DATA:user:user_id::VARCHAR AS user_id, RAW_DATA:user:country::VARCHAR AS country, RAW_DATA:user:platform::VARCHAR AS platform, -- Geneste objecten: product RAW_DATA:product:product_id::VARCHAR AS product_id, RAW_DATA:product:category::VARCHAR AS category, RAW_DATA:product:price_eur::FLOAT AS price_eur, LOADED_AT FROM RAW.EVENTS_RAW WHERE RAW_DATA:event_type::VARCHAR = 'product_view'
FLATTEN: arrays platslaan
Het tags-veld is een array. LATERAL FLATTEN zet elke array-waarde op een aparte rij — vergelijkbaar met een UNNEST in PostgreSQL.
-- Tags-array platslaan: één rij per tag per event SELECT e.EVENT_ID, e.RAW_DATA:event_type::VARCHAR AS event_type, f.value::VARCHAR AS tag FROM RAW.EVENTS_RAW e, LATERAL FLATTEN(input => e.RAW_DATA:tags) f ORDER BY e.EVENT_ID, tag
PARSE_JSON: tekst naar VARIANT
Als JSON als tekst (VARCHAR) binnenkomt in plaats van als VARIANT, converteer je het met PARSE_JSON():
-- JSON-tekst naar VARIANT converteren SELECT PARSE_JSON('{"event_type": "add_to_cart", "amount": 2}'):event_type::VARCHAR -- resultaat: 'add_to_cart'
View Definities in DCM: JSON Normaliseren
In het DCM Project definiëren we twee views in het ANALYTICS-schema: één voor de platte event-data en één voor de platgeslagen tags. Views zijn de plek waar de JSON-normalisatie plaatsvindt — niet in de landing-tabel zelf.
v_events.yml — platte event-view
# databases/WEBSHOP_DB/schemas/ANALYTICS/views/v_events.yml name: V_EVENTS comment: "Platte view op ruwe JSON-events — klaar voor analytics" definition: | SELECT EVENT_ID, RAW_DATA:event_type::VARCHAR(50) AS event_type, RAW_DATA:timestamp::TIMESTAMP_NTZ AS event_ts, RAW_DATA:session_id::VARCHAR(50) AS session_id, RAW_DATA:user:user_id::VARCHAR(50) AS user_id, RAW_DATA:user:country::VARCHAR(10) AS country, RAW_DATA:user:platform::VARCHAR(20) AS platform, RAW_DATA:product:product_id::VARCHAR(50) AS product_id, RAW_DATA:product:category::VARCHAR(100) AS category, RAW_DATA:product:price_eur::FLOAT AS price_eur, LOADED_AT FROM WEBSHOP_DB.RAW.EVENTS_RAW
v_events_tags.yml — tags-view
# databases/WEBSHOP_DB/schemas/ANALYTICS/views/v_events_tags.yml name: V_EVENTS_TAGS comment: "Tags-array platgeslagen: één rij per tag per event" definition: | SELECT e.EVENT_ID, e.RAW_DATA:event_type::VARCHAR(50) AS event_type, e.RAW_DATA:timestamp::TIMESTAMP_NTZ AS event_ts, f.value::VARCHAR(100) AS tag FROM WEBSHOP_DB.RAW.EVENTS_RAW e, LATERAL FLATTEN(input => e.RAW_DATA:tags) f
Wanneer de JSON morgen een extra veld "discount_code" krijgt, voeg je dat gewoon toe aan de view-definitie in v_events.yml en voer je snow dcm deploy uit. De tabel hoeft niet gewijzigd te worden — de VARIANT-kolom absorbeert het nieuwe veld automatisch.
Warehouse en Rollen via DCM
DCM beheert ook de rekenkracht en het toegangsbeheer — de objecten die buiten de databasehiërarchie vallen.
Warehouse definitie
# warehouses/WEBSHOP_WH.yml name: WEBSHOP_WH size: X-SMALL auto_suspend: 120 # seconden inactief voor auto-suspend auto_resume: true min_cluster_count: 1 max_cluster_count: 2 # multi-cluster voor piekmomenten comment: "Warehouse voor webshop analytics queries"
Rol en grants definitie
# roles/ANALYST_ROLE.yml name: ANALYST_ROLE comment: "Lees-alleen toegang tot analytics-schema" grants: - privilege: USAGE on: object_type: DATABASE object_name: WEBSHOP_DB - privilege: USAGE on: object_type: SCHEMA object_name: WEBSHOP_DB.ANALYTICS - privilege: SELECT on: object_type: ALL VIEWS in: schema: WEBSHOP_DB.ANALYTICS - privilege: USAGE on: object_type: WAREHOUSE object_name: WEBSHOP_WH
DCM via de Snowflake CLI
De Snowflake CLI (snow) is het gereedschap om DCM-commando's uit te voeren. Installeer het via pip:
pip install snowflake-cli-labs snow --version
Plan: wat gaat er veranderen?
# Bereken het verschil tussen definitie en live omgeving — voert niets uit
snow dcm plan --project-path ./webshop-platform --env dev
De output laat precies zien welke objecten worden aangemaakt, gewijzigd of verwijderd — vergelijkbaar met terraform plan.
# Voorbeeldoutput van snow dcm plan:
+ WEBSHOP_DB (database, create)
+ WEBSHOP_DB.RAW (schema, create)
+ WEBSHOP_DB.RAW.EVENTS_RAW (table, create)
+ WEBSHOP_DB.RAW.EVENTS_STAGE (stage, create)
+ WEBSHOP_DB.ANALYTICS (schema, create)
+ WEBSHOP_DB.ANALYTICS.V_EVENTS (view, create)
+ WEBSHOP_DB.ANALYTICS.V_EVENTS_TAGS (view, create)
+ WEBSHOP_WH (warehouse, create)
+ ANALYST_ROLE (role, create)
Deploy: wijzigingen toepassen
# Voer de wijzigingen door naar de dev-omgeving snow dcm deploy --project-path ./webshop-platform --env dev # Naar productie deployen snow dcm deploy --project-path ./webshop-platform --env prod
Diff: huidige staat vs definitie
# Toon wat er in live afwijkt van de definitie (drift detectie)
snow dcm diff --project-path ./webshop-platform --env prod
Drift detectie is waardevol in productie: als iemand handmatig een view heeft aangepast buiten DCM om, toont snow dcm diff dat afwijking — en de volgende deploy corrigeert het terug naar de gedefinieerde toestand.
Schemawijziging in JSON: DCM in de Praktijk
De webshop voegt een nieuw veld toe aan de events: "discount_code". Hoe verwerken we dit?
Stap 1: niets aan de tabel doen
De EVENTS_RAW-tabel hoeft niet te worden gewijzigd. De VARIANT-kolom neemt het nieuwe veld automatisch op. Nieuwe events met "discount_code" en oude events zonder bestaan gewoon naast elkaar.
Stap 2: view aanpassen in de definitie
# v_events.yml — één regel toevoegen definition: | SELECT EVENT_ID, RAW_DATA:event_type::VARCHAR(50) AS event_type, RAW_DATA:timestamp::TIMESTAMP_NTZ AS event_ts, RAW_DATA:session_id::VARCHAR(50) AS session_id, RAW_DATA:user:user_id::VARCHAR(50) AS user_id, RAW_DATA:user:country::VARCHAR(10) AS country, RAW_DATA:user:platform::VARCHAR(20) AS platform, RAW_DATA:product:product_id::VARCHAR(50) AS product_id, RAW_DATA:product:category::VARCHAR(100) AS category, RAW_DATA:product:price_eur::FLOAT AS price_eur, RAW_DATA:discount_code::VARCHAR(50) AS discount_code, -- nieuw LOADED_AT FROM WEBSHOP_DB.RAW.EVENTS_RAW
Stap 3: committen en deployen
git add databases/WEBSHOP_DB/schemas/ANALYTICS/views/v_events.yml
git commit -m "feat: discount_code veld toegevoegd aan V_EVENTS"
git push
# Na code review via CI/CD:
snow dcm deploy --project-path ./webshop-platform --env prod
DCM ziet dat alleen de view is gewijzigd (CREATE OR ALTER VIEW) en voert uitsluitend die wijziging door. De tabel, het schema, het warehouse en de rollen blijven onaangetast. Dit is de kracht van declaratief beheer: je beschrijft de nieuwe gewenste toestand, DCM doet de rest.
Geen handmatig migratiescript nodig
Bij een klassieke aanpak zou je een ALTER VIEW-script moeten schrijven, bijhouden of het al is uitgevoerd en opletten dat je het niet twee keer draait. Met DCM schrijf je alleen de definitie bij — DCM berekent zelf wat er moet veranderen.
CI/CD: Automatisch Deployen
In een professionele opzet draaien DCM-deploys automatisch via een CI/CD-pipeline. De flow is altijd hetzelfde — ongeacht welk platform je gebruikt:
- Developer past een
.yml-definitie aan op een branch (bijv. nieuwe kolom in een view) - Pull Request openen → pipeline draait
snow dcm plan→ reviewers zien precies wat er gaat veranderen - Na goedkeuring en merge naar
main→ pipeline draaitsnow dcm deploynaar prod
Hieronder de pipelines voor de twee meest gebruikte platforms: GitHub Actions en Bitbucket Pipelines.
GitHub Actions
# .github/workflows/dcm-deploy.yml
name: DCM Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Installeer Snowflake CLI
run: pip install snowflake-cli-labs
- name: DCM Plan (preview)
run: snow dcm plan --project-path ./webshop-platform --env prod
env:
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }}
SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
deploy:
needs: plan
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Installeer Snowflake CLI
run: pip install snowflake-cli-labs
- name: DCM Deploy naar prod
run: snow dcm deploy --project-path ./webshop-platform --env prod
env:
SNOWFLAKE_ACCOUNT: ${{ secrets.SNOWFLAKE_ACCOUNT }}
SNOWFLAKE_USER: ${{ secrets.SNOWFLAKE_USER }}
SNOWFLAKE_PASSWORD: ${{ secrets.SNOWFLAKE_PASSWORD }}
De secrets sla je op onder Repository Settings → Secrets and variables → Actions. GitHub Actions biedt daarnaast Environments (dev/test/prod) met verplichte reviewers als extra goedkeuringslaag vóór de prod-deploy.
Bitbucket Pipelines
Werk je met Bitbucket? De pipeline-syntax verschilt, maar het principe is identiek. Het configuratiebestand heet bitbucket-pipelines.yml en staat in de root van je repo.
# bitbucket-pipelines.yml image: python:3.11 pipelines: pull-requests: '**': # triggert op elke PR - step: name: DCM Plan (preview) script: - pip install snowflake-cli-labs - snow dcm plan --project-path ./webshop-platform --env dev environment: SNOWFLAKE_ACCOUNT: $SNOWFLAKE_ACCOUNT SNOWFLAKE_USER: $SNOWFLAKE_USER SNOWFLAKE_PASSWORD: $SNOWFLAKE_PASSWORD branches: main: # triggert na merge naar main - step: name: DCM Deploy naar prod deployment: production script: - pip install snowflake-cli-labs - snow dcm deploy --project-path ./webshop-platform --env prod environment: SNOWFLAKE_ACCOUNT: $SNOWFLAKE_ACCOUNT SNOWFLAKE_USER: $SNOWFLAKE_USER SNOWFLAKE_PASSWORD: $SNOWFLAKE_PASSWORD
De variabelen zet je in Bitbucket onder Repository Settings → Repository variables. Wil je ook een handmatige goedkeuringslaag vóór de prod-deploy? Voeg dan trigger: manual toe aan de deploy-step — de pipeline pauzeert dan tot iemand op "Run" klikt in de Bitbucket UI.
branches:
main:
- step:
name: DCM Deploy naar prod
deployment: production
trigger: manual # wacht op handmatige goedkeuring
script:
- pip install snowflake-cli-labs
- snow dcm deploy --project-path ./webshop-platform --env prod
Meerdere omgevingen in Bitbucket
Bitbucket ondersteunt Deployments (Settings → Deployments) om dev, test en prod elk met eigen variabelensets te definiëren. Zo gebruikt dezelfde pipeline automatisch de juiste Snowflake-credentials per omgeving — zonder hardcoding.
Platform vergelijking
| Functie | GitHub Actions | Bitbucket Pipelines |
|---|---|---|
| Configuratiebestand | .github/workflows/*.yml |
bitbucket-pipelines.yml |
| Secrets beheer | Repository Secrets / Environments | Repository variables / Deployments |
| PR preview | on: pull_request |
pipelines: pull-requests |
| Handmatige goedkeuring | Environment protection rules | trigger: manual per step |
| Gratis minuten | 2.000/maand (public repos: onbeperkt) | 50/maand (daarna betaald) |
DCM vs Handmatige SQL-scripts: Samengevat
| Dimensie | Handmatige SQL-scripts | Snowflake DCM Projects |
|---|---|---|
| Versiebeheer | Handmatig in Git — scripts kunnen in verkeerde volgorde draaien | Definitiebestanden in Git — volgorde bepaalt DCM |
| Idempotentie | Vereist handmatige IF NOT EXISTS-checks |
Ingebouwd — CREATE OR ALTER is altijd veilig |
| Drift detectie | Geen — je weet niet of prod afwijkt van de scripts | snow dcm diff toont alle afwijkingen |
| Schemawijziging | Nieuw migratiescript schrijven | Definitie aanpassen, deploy uitvoeren |
| Meerdere omgevingen | Losse scriptverzamelingen per omgeving | Één definitie, meerdere environments |
| Code review | Moeilijk — scripts zijn imperatief | Eenvoudig — definitiebestanden zijn leesbaar |
| Leercurve | Laag (gewone SQL) | Gemiddeld (CLI, YAML, declaratief denken) |
Conclusie
Snowflake DCM Projects lossen een echt probleem op: Snowflake-omgevingen die groeien zonder structuur worden onbeheersbaar. Door de gewenste eindtoestand declaratief te beschrijven in definitiebestanden en die in Git te bewaren, krijg je versiebeheer, drift-detectie, reproduceerbare deployments en een duidelijk auditspoor — zonder dat je voor elke wijziging een migratiescript hoeft te schrijven.
Het JSON-voorbeeld laat zien hoe deze aanpak bijzonder goed werkt bij veranderlijke data: de VARIANT-kolom absorbeert schemawijzigingen in de brondata, en DCM zorgt dat de views die de JSON parseren altijd in de gewenste toestand zijn — in dev én prod.
Wil je DCM Projects inrichten voor jouw Snowflake-omgeving, of jouw huidige SQL-scriptaanpak migreren naar DCM? Neem contact op — ik help je graag op weg. Zie ook dbt vs Snowflake DCM voor de vergelijking met dbt en Snowflake DCM vs DAB voor een vergelijking met Databricks.
Veelgestelde vragen
Wat is Snowflake DCM?
Snowflake DCM (Database Change Management) is een declaratieve aanpak om Snowflake-objecten te beheren. Je beschrijft de gewenste eindtoestand in definitiebestanden; de Snowflake CLI berekent het verschil met de live omgeving en past alleen de noodzakelijke wijzigingen toe.
Wat is een DCM Project?
Een DCM Project is een mapstructuur die alle DCM-definitiebestanden van één logisch geheel bevat — doorgaans één database met haar schema's, tabellen, views en andere objecten. Het project staat in Git en wordt aangestuurd via de Snowflake CLI.
Hoe slaat Snowflake JSON op?
Snowflake slaat JSON op in een VARIANT-kolom. Je extraheert velden via puntnotatie (data:veld::TYPE) en slaat arrays plat met LATERAL FLATTEN. Het schema hoeft niet van tevoren vastgelegd te worden.
Wat is het verschil tussen DCM en dbt?
DCM beheert de objectstructuur van je platform: databases, schema's, rollen, warehouses en landing-tabellen. dbt transformeert de data erin: van ruwe brondata naar bruikbare analyticsmodellen. Ze zijn complementair en vullen elkaar aan.
Kan DCM omgaan met schemawijzigingen in JSON?
Ja. Omdat JSON in een VARIANT-kolom staat, hoef je de tabel niet te wijzigen als de JSON een nieuw veld krijgt. Je past alleen de view-definitie aan in het DCM Project en voert snow dcm deploy uit — DCM past uitsluitend de view aan.
Werkt DCM met CI/CD?
Ja. snow dcm plan geeft een preview van wijzigingen (zonder uitvoering) en snow dcm deploy voert ze door. Beide commando's werken in GitHub Actions, Bitbucket Pipelines, GitLab CI en Azure Pipelines. De flow is altijd hetzelfde: plan bij een pull request, deploy na merge naar main.