Migrer de dbt vers Dataform : un cadre de décision pratique

La facture de licence dbt Cloud arrive et quelqu’un dans l’équipe pose la question : « On ne pourrait pas simplement utiliser Dataform ? C’est gratuit. »

À 100 $/utilisateur/mois, une équipe analytics de 10 personnes paie 12 000 $ par an pour dbt Cloud. Dataform offre des capacités de transformation comparables à coût de licence nul pour les utilisateurs BigQuery. Le calcul semble convaincant. Mais les décisions de migration méritent un examen plus approfondi qu’une simple comparaison de coûts.

Ce guide couvre le processus de migration pour les projets dbt simples et complexes, établit la correspondance entre les concepts des deux outils et détaille quand la migration a du sens (et quand elle n’en a pas).

Pourquoi les équipes envisagent de migrer de dbt vers Dataform

Trois facteurs alimentent généralement les discussions sur la migration :

Les coûts de licence. Les 100 $/utilisateur/mois de dbt Cloud s’accumulent vite. Les équipes qui paient pour des fonctionnalités qu’elles n’utilisent pas pleinement (comme la couche sémantique ou le CI avancé) ressentent la pression le plus fortement. dbt Core reste gratuit, mais vous perdez les améliorations de vitesse de compilation de dbt Fusion et les fonctionnalités CI/CD natives.

La consolidation GCP. Dataform vit à l’intérieur de la console BigQuery. Pour les équipes déjà engagées dans GCP, avoir les transformations gérées aux côtés du warehouse simplifie les opérations. L’intégration native IAM, les métadonnées Dataplex et l’orchestration Cloud Composer créent une stack cohérente.

Le tournant de 2025. La fusion dbt-Fivetran annoncée en octobre 2025 signale une consolidation du secteur. Certaines équipes s’inquiètent de changements tarifaires ou de pivots stratégiques. Pendant ce temps, Google continue d’investir régulièrement dans Dataform : pas de développement de fonctionnalités agressif, mais une maintenance fiable et des certifications de conformité (SOC 1/2/3, HIPAA, ISO 27001).

Aucun de ces facteurs seul ne justifie une migration. La question est de savoir si votre situation spécifique fait pencher la balance.

Avant de commencer : le test de réalité de la migration

Les délais de migration varient considérablement selon la complexité du projet :

Profil du projetDélaiRépartition de l’effort
Petit (~20 modèles, pas de macros custom)1-2 semaines80 % automatisé, 20 % validation
Moyen (~50-100 modèles, quelques macros)2-4 semaines60 % automatisé, 40 % conversion de macros
Grand (100+ modèles, usage intensif de macros)2-3 moisRéécriture manuelle de la logique programmatique
Entreprise (packages, pipelines ML)3-6 moisExécution parallèle, validation par les parties prenantes

Ce que vous allez perdre

Avant de vous engager dans la migration, comprenez les lacunes de l’écosystème :

  • Écosystème de packages : dbt dispose de 200+ packages sur hub.getdbt.com. Dataform n’a pas de hub de packages centralisé.
  • Profondeur des tests : dbt_expectations fournit 50+ tests. Les assertions intégrées de Dataform couvrent l’unicité, les valeurs nulles et les conditions sur les lignes. C’est tout.
  • Maturité CI/CD : dbt Cloud offre le Slim CI en quelques clics. Dataform nécessite une configuration manuelle avec Cloud Build ou GitHub Actions.
  • Outillage éditeur : L’extension dbt Power User compte plus d’1M d’installations. Il n’existe pas d’extension Dataform comparable pour Cursor ou VS Code.
  • Valeur sur le marché de l’emploi : Les compétences dbt apparaissent dans la plupart des offres d’emploi analytics engineer. L’expertise Dataform reste de niche.

Si ces pertes sont significatives pour votre équipe, la migration pourrait coûter plus qu’elle ne rapporte.

Correspondance des concepts dbt vers Dataform

Les concepts fondamentaux se transposent, mais la syntaxe diffère substantiellement.

Syntaxe de référence

dbt :

SELECT
customer_id,
customer__name,
customer__email,
customer__status
FROM {{ ref('base__source__customers') }}
WHERE customer__status IN {{ var('active_statuses') }}

Dataform :

SELECT
customer_id,
customer__name,
customer__email,
customer__status
FROM ${ref("base__source__customers")}
WHERE customer__status IN ${dataform.projectConfig.vars.active_statuses}

Modèles incrémentaux

dbt :

{{ config(materialized='incremental', unique_key='order_id') }}
SELECT
order_id,
customer_id,
order__total_usd,
order__created_at,
order__updated_at
FROM {{ ref('base__source__orders') }}
{% if is_incremental() %}
WHERE order__updated_at > (SELECT MAX(order__updated_at) FROM {{ this }})
{% endif %}

Dataform :

config {
type: "incremental",
uniqueKey: ["order_id"]
}
SELECT
order_id,
customer_id,
order__total_usd,
order__created_at,
order__updated_at
FROM ${ref("base__source__orders")}
${when(incremental(), `WHERE order__updated_at > (SELECT MAX(order__updated_at) FROM ${self()})`)}

Tests de schéma vs assertions

dbt (schema.yml) :

models:
- name: mrt__marketing__customers
columns:
- name: customer_id
tests: [unique, not_null]
- name: customer__email
tests:
- not_null
- unique

Dataform (inline) :

config {
type: "table",
assertions: {
uniqueKey: ["customer_id"],
nonNull: ["customer_id", "customer__email"]
}
}

Référence de complexité de conversion

Fonctionnalité dbtÉquivalent DataformComplexité
{{ ref('model') }}${ref('table')}Faible (automatisé)
{{ source('schema','table') }}Fichiers de déclarationMoyenne
Macros JinjaIncludes JavaScriptÉlevée (manuelle)
Tests YAML de schémaAssertions inlineMoyenne
is_incremental()when(incremental(), ...)Faible
Seeds (fichiers CSV)Tables BigQuery + déclarationsMoyenne
Snapshots (SCD2)Implémentation manuelleÉlevée
Packages dbtPas d’équivalentLacune critique

Les fonctionnalités qui ne se transposent pas

Certaines capacités de dbt nécessitent un travail significatif de réécriture ou n’ont pas d’équivalent dans Dataform.

Snapshots

Les snapshots dbt implémentent automatiquement les dimensions à évolution lente de type 2 (SCD Type 2). Dataform n’a pas de fonctionnalité de snapshot intégrée. Vous devrez implémenter manuellement les dimensions à évolution lente en utilisant des tables incrémentales avec une logique de merge personnalisée.

Packages dbt

Si votre projet utilise dbt_utils, dbt_expectations ou dbt_date, attendez-vous à une conversion manuelle. Les fonctions courantes de [[fr/guide-macros-dbt|dbt_utils]] comme surrogate_key ou star nécessitent des équivalents JavaScript ou du SQL inline. L’outil de migration gère certaines fonctions dbt_utils, mais la couverture est incomplète.

Stratégie incrémentale microbatch

Le traitement microbatch de dbt, introduit en 2024, n’a pas d’équivalent dans Dataform. Si vous traitez des données par lots temporels pour des raisons de performance ou de maîtrise des coûts, vous devrez restructurer votre approche.

Slim CI

Le Slim CI de dbt Cloud ne build que les modèles modifiés plus leurs dépendants, en créant automatiquement des schémas PR. Reproduire cela dans Dataform nécessite d’appeler l’API REST Dataform depuis des outils CI externes. C’est possible, mais demande nettement plus de configuration.

Processus de migration étape par étape

1. Auditer votre projet

Avant de toucher au code, inventoriez ce que vous avez :

Terminal window
# Compter les modèles par type
find models -name "*.sql" | wc -l
# Lister les fichiers de macros
find macros -name "*.sql"
# Vérifier packages.yml pour les dépendances
cat packages.yml

Documentez :

  • Le nombre total de modèles
  • Le nombre de macros personnalisées
  • Les packages externes utilisés
  • Les stratégies de modèles incrémentaux
  • Les tables snapshot
  • La complexité CI/CD

2. Exécuter l’outil de migration automatisé

L’outil ra_dbt_to_dataform gère la conversion de base :

Terminal window
# Cloner l'outil de migration
git clone https://github.com/rittmananalytics/ra_dbt_to_dataform.git
cd ra_dbt_to_dataform
# Installer les dépendances
pip install -r requirements.txt
# Lancer la migration (utilise GPT-4 pour la conversion de macros complexes)
python migrate.py --dbt-project /path/to/dbt --output /path/to/dataform

L’outil convertit :

  • Les références de modèles (syntaxe ref())
  • Les déclarations de sources
  • Les fonctions courantes de dbt_utils
  • La logique incrémentale de base

Il ne gère pas :

  • Les seeds
  • Les snapshots
  • Les macros personnalisées complexes
  • Les définitions de la couche sémantique

3. Convertir les macros en includes JavaScript

Dataform utilise des fichiers JavaScript pour la logique réutilisable. Créez un répertoire includes/ pour les fonctions partagées.

Macro dbt (generate_surrogate_key.sql) :

{% macro generate_surrogate_key(field_list) %}
TO_HEX(MD5(CONCAT({% for field in field_list %}COALESCE(CAST({{ field }} AS STRING), ''){% if not loop.last %}, '|', {% endif %}{% endfor %})))
{% endmacro %}

JavaScript Dataform (includes/utils.js) :

function generateSurrogateKey(fields) {
const fieldExpressions = fields
.map(f => `COALESCE(CAST(${f} AS STRING), '')`)
.join(", '|', ");
return `TO_HEX(MD5(CONCAT(${fieldExpressions})))`;
}
module.exports = { generateSurrogateKey };

Utilisation en SQLX :

config { type: "table" }
js {
const { generateSurrogateKey } = require("includes/utils");
}
SELECT
${generateSurrogateKey(["customer_id", "order__created_at"])} AS surrogate_key,
customer_id,
order__created_at,
order__total_usd
FROM ${ref("base__source__orders")}

4. Recréer les tests sous forme d’assertions

Les assertions intégrées de Dataform couvrent les cas de base :

config {
type: "table",
assertions: {
uniqueKey: ["order_id"],
nonNull: ["order_id", "customer_id", "order__created_at"],
rowConditions: [
"order__total_usd >= 0",
"order__created_at <= CURRENT_DATE()"
]
}
}

Pour les validations complexes, créez des fichiers d’assertion séparés :

-- definitions/assertions/assert_valid_customer_emails.sqlx
config { type: "assertion" }
SELECT
customer_id,
customer__email
FROM ${ref("mrt__marketing__customers")}
WHERE customer__email NOT LIKE '%@%.%'
OR customer__email IS NULL

Les tests de dbt_expectations comme les vérifications de distribution, les patterns regex ou les comparaisons inter-tables nécessitent des fichiers d’assertion personnalisés.

5. Mettre en place l’orchestration

Les workflows Dataform ne se déclenchent pas nativement à partir d’événements git. Choisissez une approche d’orchestration :

Cloud Composer (Airflow managé) :

from airflow.providers.google.cloud.operators.dataform import (
DataformCreateCompilationResultOperator,
DataformCreateWorkflowInvocationOperator,
)
compile_task = DataformCreateCompilationResultOperator(
task_id="compile",
project_id="my-project",
region="us-central1",
repository_id="my-repo",
)
run_task = DataformCreateWorkflowInvocationOperator(
task_id="run",
project_id="my-project",
region="us-central1",
repository_id="my-repo",
compilation_result="{{ task_instance.xcom_pull('compile') }}",
)

Cloud Scheduler + Workflows :

main:
steps:
- compile:
call: http.post
args:
url: https://dataform.googleapis.com/v1beta1/projects/PROJECT/locations/REGION/repositories/REPO/compilationResults
auth:
type: OAuth2
- run:
call: http.post
args:
url: https://dataform.googleapis.com/v1beta1/projects/PROJECT/locations/REGION/repositories/REPO/workflowInvocations

6. Exécution parallèle et validation

Ne basculez pas immédiatement. Exécutez les deux pipelines simultanément :

  1. Déployez le projet Dataform dans un dataset séparé (par ex. analytics_dataform)
  2. Planifiez dbt et Dataform pour s’exécuter à la même cadence
  3. Comparez les résultats avec des comptages de lignes et des checksums
  4. Validez les dashboards en aval par rapport aux deux sources
  5. Surveillez pendant 2 à 4 semaines avant de décommissionner dbt
-- Requête de validation
SELECT
'dbt' AS source,
COUNT(*) AS row_count,
FARM_FINGERPRINT(TO_JSON_STRING(ARRAY_AGG(t ORDER BY order_id))) AS checksum
FROM `analytics.mrt__sales__orders` t
UNION ALL
SELECT
'dataform' AS source,
COUNT(*) AS row_count,
FARM_FINGERPRINT(TO_JSON_STRING(ARRAY_AGG(t ORDER BY order_id))) AS checksum
FROM `analytics_dataform.mrt__sales__orders` t

Quand la migration n’a pas de sens

La migration n’est pas toujours le bon choix. Reconsidérez sérieusement si :

Vous dépendez fortement des packages dbt. Convertir dbt_utils, dbt_expectations ou des packages spécialisés comme dbt-ga4 demande un effort considérable. Si les packages apportent une valeur significative, cette valeur disparaît après la migration.

Le multi-warehouse est dans la feuille de route. Dataform ne supporte que BigQuery. S’il y a la moindre chance que vous ajoutiez Snowflake, Databricks ou un autre warehouse dans les 2 à 3 prochaines années, l’écosystème d’adaptateurs dbt prend de la valeur.

Le développement de carrière de l’équipe compte. Les compétences dbt sont quasi universelles dans les offres d’emploi analytics engineer. L’expertise Dataform est de niche. Si votre équipe valorise la portabilité de carrière, l’expérience dbt la sert mieux.

Vous utilisez des stratégies incrémentales complexes. Le traitement microbatch, la logique de merge sophistiquée ou les patterns de données en retard sont plus faciles à gérer dans dbt. Le support incrémental de Dataform est basique.

Le coût de migration dépasse les économies de licence. Une migration de 2-3 mois pour un ingénieur senior coûte entre 30 000 et 50 000 $ en salaire seul. Ajoutez le débogage, la validation et les corrections en aval. Comparez aux 12 000 $/an de licence. Si la migration met plus de 2 ans à être rentabilisée, reconsidérez.

Prendre la décision

Utilisez ce cadre pour évaluer votre situation :

Migrer vers Dataform quand :

  • Engagement 100 % BigQuery sans plans multi-cloud
  • Moins de 50 modèles avec un minimum de macros personnalisées
  • Pas de dépendance aux packages dbt au-delà des basiques de dbt_utils
  • Pression sur les coûts aiguë (startup, budget contraint)
  • L’équipe préfère JavaScript à Jinja
  • L’intégration GCP compte plus que la largeur de l’écosystème

Rester avec dbt quand :

  • Utilisation active des fonctionnalités dbt Cloud (couche sémantique, Mesh, CI avancé)
  • Usage intensif de macros rendant la conversion douloureuse
  • Les pipelines ML dépendent d’un comportement de templating spécifique
  • L’équipe grandit et a besoin d’un levier de recrutement
  • Une stratégie multi-warehouse est envisageable
  • Le retour sur investissement de la migration dépasse 2 ans

La question honnête

Posez-vous une seule question : votre organisation est-elle BigQuery pour toujours ?

Si oui (sincèrement, stratégiquement, pour l’avenir prévisible), Dataform offre un chemin crédible pour éliminer les coûts de licence. L’outil est mature, l’investissement de Google est constant, et l’intégration BigQuery est excellente.

S’il y a de l’incertitude, la flexibilité de dbt a une valeur d’option. Le coût de licence vaut peut-être la peine d’être payé pour la capacité de changer de cap ultérieurement.

Aucune des deux réponses n’est mauvaise. L’erreur est de migrer uniquement pour les économies de coûts sans tenir compte des compromis d’écosystème et de l’effort de conversion. Faites les calculs avec des délais réalistes, pas optimistes.