¡Hola a todo el mundo! Hoy les quiero contar acerca de una tecnología con la que estuve trabajando los últimos meses y que se volvió fundamental en muchos procesos de datos para muchos de nuestros clientes y directamente un estándar de la industria: DBT (Data Build Tool).
Se trata de una herramienta de código abierto que, apoyada en motores de bases de datos como Postgres DB, Databricks, Snowflake o Redshift, permite transformar datos de manera flexible y eficiente. En esencia, DBT facilita escribir transformaciones basadas en SQL, pero con capas añadidas de funcionalidad que hacen que sean más mantenibles, testables y confiables.
Pero antes, unas palabras a nivel personal
Mi formación en el mundo data se inició por el lado de ciencia de datos y desarrollo de modelos de machine learning. Como no vengo de un trasfondo tradicional de ciencias de la computación o matemática, en mis comienzos debí enfocarme en un único aspecto para tratar de mejorar mis habilidades en búsqueda de una oportunidad laboral.
Afortunadamente esa oportunidad se dio y con el transcurso de los años fui creciendo enormemente como profesional hasta llegar a deployr. Pero hay una realidad ineludible: a lo largo de estos años, los roles técnicos orientados a ML/IA y programación cambiaron significativamente por una serie de factores:
- El advenimiento de ChatGPT y otras herramientas de IA que agilizan el desarrollo de código,
- El surgimiento de frameworks que abstraen gran parte del trabajo que antes se tenía que hacer a mano,
- La popularización de la formación digital a distancia,
y otros tantos factores hicieron que la barrera de entrada a esta industria baje significativamente, y en simultáneo, nos volvieron más productivos, con más tiempo disponible para distintas tareas.
Esto quiere decir que en muchos casos el rol del data scientist tradicional fue cayendo en desuso y otras figuras lo fueron reemplazando, no necesariamente en funciones sino en alcance y diversidad de responsabilidades. En deployr decidimos llamar a este rol “Data Developer” y es con el que más identificado me siento hoy en día: ya no me enfoco 100% en desarrollar un modelo de machine learning, sino que participo de todo el proceso de datos de punta a punta.
Sin embargo, para poder hacer esto tuve que formarme en Data Engineering y adaptarme a los tiempos que corren. Con el cliente con el que estoy trabajando, luego de enfocarnos en el modelado y ML, fue necesario concentrar esfuerzos en la eficiencia del proceso de datos y en modernizar la infraestructura. Y la tecnología elegida para esto fue una herramienta que cambió mi concepción de un proceso integral de datos y que tiene nombre de secuela a Dragon Ball Z: DBT.
¿Qué es DBT?
Como ya expliqué más arriba, DBT es una herramienta que permite escribir transformaciones basadas en SQL, con muchas funcionalidades más. Pero a diferencia de las herramientas tradicionales de ETL (Extract, Transform, Load), DBT se centra exclusivamente en la T.
Esto significa que DBT asume que los datos ya están almacenados en un data warehouse y nos provee de las herramientas para modificar y procesar esos datos dentro del propio warehouse en información lista para ser consumida por otros procesos, como tableros de BI o modelos de machine learning.
El concepto fundamental de DBT es tratar las transformaciones de datos como una forma de desarrollo de software. Esto lleva las mejores prácticas de ingeniería de software al trabajo con datos, incluyendo control de versiones, modularidad, tests, documentación y gestión de deployment.
¿Por qué elegir DBT?
Desde el punto de vista de los desarrolladores, la facilidad de implementación y lo estructurado de los proyectos hace que DBT sea un framework de trabajo muy ágil y provechoso.
Pero desde una perspectiva de negocio, al momento de seleccionar el stack con el que se va a empezar a trabajar –o evaluar alternativas de reemplazo/mejora de lo existente–, DBT tiene algunos puntos muy fuertes a su favor:
- Eficiencia de costos: DBT ayuda a aprovechar eficientemente los recursos de cómputo y sus costos asociados gracias a la optimización de queries, implementación de modelos incrementales, transformaciones planificadas y reusabilidad de código, entre otras.
- Mayor rapidez de desarrollo: DBT facilita la aceleración de los ciclos de desarrollo de procesos de datos y su posterior análisis, lo que se traduce en insights más veloces en los que apalancar las decisiones de negocio.
- Reducción de deuda técnica: el enfoque estructurado de transformación de datos evita el código desordenado y procesos sin documentar, y esto reduce costos a largo plazo al evitar la duplicación de trabajo o reescritura de scripts.
- Mejoras en el gobierno de datos: la documentación, trackeo de linaje y testeo que incluye el framework apoyan el cumplimiento de los requerimientos de compliance y gobernanza.
- Escalabilidad: la capacidad de crecer con los equipos de datos y escalar en simultáneo con las necesidades de la empresa son un gran punto a favor de DBT.
- Democratización del acceso a los datos: el enfoque basado en transparencia y documentación ayuda a que las decisiones de negocio sean data-driven.
Además, como se puede integrar con la mayoría de los motores de bases de datos y proveedores de data warehousing, la barrera de entrada de implementación de DBT es comparativamente muy baja. En nuestra experiencia, cualquier empresa que esté pensando en modernizar su infraestructura de datos tiene que al menos considerarlo como una opción.
Conceptos centrales
DBT se apoya en una serie de pilares fundamentales para aprovechar todas las capacidades técnicas del framework. En las próximas notas de esta serie iremos profundizando en cada uno de estos componentes y veremos ejemplos concretos de aplicación. De momento, vayamos a lo básico.
Modelos
Los modelos son los bloques fundacionales de cualquier proceso de transformación en DBT. Son scripts de SQL que contienen un SELECT statement y que definen las transformaciones de la data, lo que resulta en un dataset, y normalmente corresponden a una tabla o una vista dentro del data warehouse.
Veamos un ejemplo de cómo se ve un modelo súper sencillo en DBT.
Analicemos brevemente sus componentes:
- {{ config(materialized=’table’) }}: Esta es sintaxis propia de DBT que indica la utilización de un macro cuya función es materializar (persistir) esta transformación como una tabla y almacenar los resultados de nuestra query dentro de la base de datos. No se asusten, ya veremos qué son los macro y sus sintaxis.
- SELECT statement: Constituye el núcleo de la lógica de transformación de un modelo. Define cómo se extraen, transforman y estructuran los datos de las tablas de origen o de otros modelos DBT.
- FROM {{ ref(‘stg_customers’) }}: Este es otro macro que referencia a un modelo distinto dentro de nuestro proceso de DBT, en este caso una versión compilada de una tabla llamada ‘stg_customers’. Esto garantiza la gestión de las dependencias para que DBT ejecute ‘stg_customers’ antes de ejecutar el modelo actual.
En las siguientes notas veremos distintos tipos de transformaciones que podemos realizar con nuestros modelos.
Sources
Los sources son archivos YAML que definen la data upstream sobre la que se construyen los modelos, típicamente los datos crudos cargados dentro del data warehouse. Es decir, sirven para registrar y parametrizar las fuentes de datos que luego alimentan a los distintos procesos de DBT.
Veamos un ejemplo de archivo source:
Materializaciones
DBT soporta diferentes tipos de persistencia de la data que transformamos en nuestro proceso.
- Tabla: una tabla completa que se reconstruye cada vez que se corre el proceso.
- Vista: una vista de SQL que realiza queries a tablas subyacentes.
- Incremental: sólo procesa los registros nuevos o que tuvieron modificaciones.
- Efímeras: usadas como subqueries en otros modelos sin persistirlas.
- Snapshots: materializaciones especiales que registran los cambios históricos de la data a través del tiempo.
En las próximas notas de la serie usaremos varias de estas materializaciones para analizar sus distintos efectos en el proceso.
Macros
Los macros son secciones de código SQL reutilizables, similares a las funciones en otros lenguajes de programación, que sirven para evitar la redundancia. Pueden aceptar parámetros, condicionales y loops, y son capaces de devolver código SQL para insertar en los modelos. DBT proporciona macros incorporados para operaciones comunes, y podemos importar otros adicionales de paquetes DBT para ampliar su funcionalidad o incluso desarrollar macros propios –parecido a hacer una función en Python–.
Seeds
Los seeds son archivos CSV que pueden cargarse directamente en el data warehouse, lo que los hace útiles para datos de referencia estáticos que no cambian frecuentemente. Por ejemplo, si tenemos una tabla auxiliar que contiene información que usamos en nuestros procesos de transformación y que normalmente no sufrirá modificaciones, normalmente la definiremos como un seed.
Snapshots
Los snapshots son un tipo especial de modelo que registra los cambios en los datos a lo largo del tiempo. Conservan el historial de modificaciones de los datos mediante la adición de columnas de metadatos que permiten realizar un seguimiento. Los snapshots son especialmente útiles para entidades que cambian lentamente pero en las que el seguimiento histórico es importante, como la información de clientes o los detalles de productos.
Archivos de configuración
Este archivo .yml es indispensable para todo proyecto y sirve como centro de control para el proyecto, definiendo desde las convenciones de nomenclatura hasta las estrategias de materialización. Cada archivo de proyecto debe incluir ciertos campos obligatorios, como vemos en el ejemplo siguiente:
Existen configuraciones avanzadas adicionales, que van desde el comportamiento de los snapshots, la configuración de los tests y las definiciones de las variables. Iremos viendo algunas de ellas en las próximas notas cuando hagamos uso de los distintos componentes de DBT, pero de momento pueden consultar la documentación oficial de DBT para el yml de configuración si quieren más información al respecto.
dbt Core vs dbt Cloud
dbt Core es una herramienta de línea de comandos de código abierto de uso gratuito, pero requiere hosting propio y gestión manual de la infraestructura. Carece de un scheduler integrado y depende de herramientas externas como Airflow para automatizarlo. Además, como no tiene una interfaz gráfica y tiene capacidades de logging limitadas, el control de versiones y la configuración de CI/CD deben gestionarse manualmente, algo un poco engorroso y que requiere conocimientos técnicos.
En cambio, dbt Cloud es una plataforma comercial SaaS con costos asociados (aunque tiene un tier gratuito), totalmente gestionada por dbt Labs, la empresa creadora de DBT. Proporciona un IDE web para desarrollar los procesos, un scheduler de trabajos integrado, logging y monitoreo avanzados, y flujos de trabajo CI/CD automatizados. Además, provee alojamiento integrado de documentación, herramientas de colaboración en equipo y control de acceso basado en roles. Por último, al ser un producto pago, tiene opciones de soporte dedicadas.
En esta serie de notas, utilizaremos DBT Core para poder desarrollar nuestras transformaciones de manera local, pero dependiendo de las necesidades de un negocio, el presupuesto disponible y las particularidades del proyecto, DBT Cloud puede ser una excelente opción. ¡Incluso es posible usar DBT Core para luego conectar con DBT Cloud, lo que permite desarrollar de manera local pero productivizar en la versión online!
¡Demo time!
Para cerrar tanto esta nota como las que se vienen en las próximas semanas, me gustaría dejarles código de práctica en el que veamos una implementación de un proyecto muy simple en dbt. Esta semana crearemos el entorno virtual, instalaremos DBT Core y armaremos el directorio del proyecto mediante la ejecución de un simple comando. En las próximas entregas veremos cómo crear intermedios y source files, y cómo usar macros, seeds y generar documentación, entre otras cosas.
Antes que nada, abrimos una nueva ventana en VSCode. Previamente creé una carpeta llamada dbt-demo para que alojemos allí nuestro proyecto de prueba. Luego, como es práctica común y recomendable, armamos nuestro entorno virtual y lo activamos. En mi caso, como estoy trabajando en Linux, luce así:

Luego, instalamos DBT Core, y una vez que termine, armamos el setup inicial.
pip install dbt-core
dbt init my_project

Notarán que el proceso termina con un runtime error. Esto es porque no instalamos ningún adaptador de motor de base de datos. Algunos ejemplos de los que se pueden utilizar son:
- Databricks: pip install dbt-databricks
- PostgreSQL: pip install dbt-postgres
- BigQuery: pip install dbt-bigquery
- Snowflake: pip install dbt-snowflake
- Redshift: pip install dbt-redshift
Instalaremos el de Postgres para este tutorial. Por ahora, veamos en detalle qué componentes creó DBT en nuestro directorio:

Allí podemos ver nuestro entorno virtual, una carpeta con logs y una carpeta principal llamada my_project (que fue como llamamos a este proyecto de prueba cuando corrimos el comando dbt init my_project). De momento las carpetas analyses, macros, seeds, snapshots y tests están vacías salvo por los archivos .gitkeep. En las próximas notas las usaremos.
Ahora nos enfocaremos en sólo dos componentes: models y dbt_project.yml.
En dbt_project.yml, DBT ya nos creó una versión inicial del archivo de proyecto para que nosotros podamos customizar de acuerdo a nuestras necesidades:
Ahora vamos a crear profiles.yml en la carpeta de proyecto y pegar el texto que indicado a continuación:

Ese archivo no se commitea nunca, por eso es que normalmente no se pone directamente en el proyecto sino en un directorio seguro. Pero dado que es un tutorial de prueba, de momento vamos a dejarlo aquí y agregar ese archivo el .gitignore como buena práctica para garantizar que nunca pushearemos información sensible a nuestro repositorio de GitHub.
Allí veremos que DBT ya preconfiguró algunos archivos que tampoco deben ser incluídos en el repo, así que agregamos profiles.yml a esa lista.
Paso siguiente, vamos a instalar el adaptador de Postgres para DBT, con el comando
pip install dbt-postgres
Esto instalará y actualizará los componentes necesarios para correr una base de datos Postgres, que en tutoriales futuros utilizaremos más en profundidad pero que en esta primera instancia nos servirá para ejecutar nuestros modelos predeterminados.
Instalado el adaptador de Postgres, vamos a correr Docker con el siguiente comando (únicamente por conveniencia, se podría usar cualquier instancia de Postgres que se quiera), que responde a la configuración que seteamos en nuestro archivo de profiles.yml:
docker run --name dbt_postgres -e POSTGRES_PASSWORD=mysecretpass -p 5432:5432 -d postgres
Luego iremos al directorio de proyecto, donde en breve ejecutaremos nuestras transformaciones: tipeamos cd my_project en la consola para pararnos en la carpeta correcta. Ahora ya está todo listo para correr el proyecto.
Pero antes, el otro componente que vamos a observar hoy es la carpeta models, que contiene otra de examples, donde alojaremos todas las transformaciones que llevará a cabo nuestro proyecto. Dentro de la carpeta de examples, inspeccionemos los dos modelos de ejemplo que creó DBT.
Son dos procesos extremadamente simples y que no revisten ninguna complejidad, cuyo único objetivo es mostrar cómo se escriben las transformaciones y cómo se encadenan los modelos.
Ahora sí, lo que todos esperaban: vamos a ejecutar las transformaciones que estos archivos de modelo estipulan. Tipeen el siguiente comando en la consola:
dbt run --select my_first_dbt_model
Esta instrucción tiene dos componentes:
- dbt run: Sirve para ejecutar los modelos de DBT en nuestro proyecto corriendo las transformaciones definidas en nuestros archivos .sql.
- –select my_first_dbt_model: Esto especifica que el único modelo que queremos correr es el que se llama my_first_dbt_model en lugar de correr todos los modelos del proyecto.
Esto debería tener este output en la consola, que nos indica que el modelo fue ejecutado con éxito:

Una pequeña variante: podemos hacer lo mismo agregando un + al final de la primera instrucción que dimos.
dbt run --select my_first_dbt_model+
Esto ejecuta my_first_dbt_model y todos los modelos downstream, es decir, los que dependen de este. Podemos hacer lo mismo pero al revés con el otro:
dbt run --select +my_second_dbt_model
Con esto, corremos my_second_dbt_model upstream, es decir, con los modelos de los que este depende.

Por último, vamos a realizar una query simple. Vamos a usar psql para conectarnos con el Postgres que levantamos previamente con Docker. Revisen el user y pass de acuerdo a lo que configuramos en profiles.yml y luego hacemos la query:

Pueden probar de jugar con los modelos de ejemplo que provee DBT para agregar más columnas, realizar transformaciones adicionales y ejecutar queries que visualicen los resultados. ¡La mejor manera de aprender es meter las manos en el barro!
No es un adiós, es un hasta pronto
¡Jornada intensa de aprendizaje! Con este tutorial ya hicimos las siguientes cosas: inicializamos un proyecto de DBT de cero, creamos los archivos de configuración básicos, levantamos una base de datos Postgres y corrimos nuestros primeros modelos para realizar transformaciones. Además, en la parte teórica nos familiarizamos con los conceptos básicos de este framework tan interesante. Nada mal para una tarde de estudio.
En las próximas notas profundizaremos el concepto de modelos y sus distintas variantes, popularemos una base de datos dummy para probar transformaciones, crearemos un seed como tabla auxiliar, usaremos macros para abstraer código redundante y generaremos documentación automática, entre otras cosas. ¡No se lo pierdan!
Como siempre dije, una de las cosas que más me gusta de trabajar en Deployr es que compartimos lo que sabemos. Cada proyecto, cada cliente y cada desafío es un aprendizaje y un proceso de crecimiento para nosotros, y democratizar el conocimiento y las experiencias para nosotros es fundamental desde el punto de vista de pertenencia a una comunidad como es la de los que trabajamos en datos. ¡Nos vemos en la próxima edición para seguir profundizando en esta herramienta!