Data Engineering’s Shift from Imperative to Declarative — and Why It Matters More in the Age of AI
Author: Brad Coles | Source: bradcoles.dev/blog | Published: 2026
한 줄 요약
경쟁 플랫폼들이 서로 말하지 않고 같은 것을 만들었다면 그것은 보통 근본 아이디어가 옳다는 신호이고, 선언적 프레임워크는 실행 전에 서버가 선언을 검증한다는 구조적 안전망을 AI 생성 코드에 제공해 그 논거를 더 강하게 만든다.
핵심 주장/내용
- 무엇이 바뀌었나 — SQL 언어가 아니라 SQL 주변의 프레임워크: SQL은 언제나 선언적이었고(
SELECT * FROM orders WHERE status = 'pending'은 어떻게가 아니라 무엇을 기술한다) DataStage·Informatica의 드래그앤드롭도 정신적으로는 선언적이었다. 바뀐 것은 모델 의존성이 어떻게 관리되고, 테스팅이 어떻게 강제되고, 문서화가 어떻게 최신을 유지하고, 비즈니스 로직이 특정 실행 엔진에 묶이는지 다- SCD Type 2 예시 — 명령형으로는 비즈니스 키로 새 데이터를 기존 테이블에 조인하고, 추적 컬럼이 바뀌었는지 확인하고, 옛 row를 end-date하고, 새 버전을 삽입한다. PySpark로 약 30줄의 Delta merge 로직이고 모든 엣지 케이스를 직접 소유한다. 선언적으로는
unique_key,strategy,updated_at몇 줄이면 프레임워크가 비교·end-dating·삽입을 처리
- SCD Type 2 예시 — 명령형으로는 비즈니스 키로 새 데이터를 기존 테이블에 조인하고, 추적 컬럼이 바뀌었는지 확인하고, 옛 row를 end-date하고, 새 버전을 삽입한다. PySpark로 약 30줄의 Delta merge 로직이고 모든 엣지 케이스를 직접 소유한다. 선언적으로는
- 인간 엔지니어에게의 매력: 명령형에서는 append-only, upsert, merge, SCD Type 2 같은 모든 로딩 패턴마다 로직을 직접 쓰고 유지해야 한다. 여러 도메인 팀이 각자 파이프라인을 소유하는 federated 모델에서는 이것이 빠르게 복리로 쌓인다 — 재무 팀의 SCD Type 2 로직이 중앙 데이터 팀 것과 일치하는지 어떻게 보장하나? 선언적 프레임워크는 “SCD Type 2를 원한다”고 말하면 모든 팀에게 같은 테스트되고 버전 관리된 구현을 준다
- 모든 주요 플랫폼이 같은 답에 도달했다 — 이 전환이 실재한다는 가장 명확한 증거는 어느 하나의 도구가 아니라 경쟁 플랫폼들이 독립적으로 같은 결론에 도달한 것처럼 보인다는 점:
- Snowflake는 Dynamic Tables를, Databricks는 Delta Live Tables(현 Lakeflow)를, Microsoft는 Materialized Lake Views를 만들었다
- 2022년 2월 Snowflake와 Databricks가 둘 다 dbt Labs에 투자했고, 그 뒤 몇 달 안에 각자의 경쟁 네이티브 도구를 출시했다 (6주 간격이었다 — 우연이 아니다). 독립 프레임워크를 후원해 시장을 검증하고, 그다음 같은 아이디어의 플랫폼 네이티브 버전을 만든 것
- 2025년 10월 Fivetran이 dbt Labs와 약 $600M ARR에 합병. Extract와 Load를 소유한 회사가 Transform을 소유한 회사와 합쳤다
- 2025년 6월 Databricks가 Spark Declarative Pipelines(SDP)를 Apache Spark에 기여 — DLT를 그대로 오픈소스화한 게 아니라 DLT를 수년간 프로덕션에서 돌린 경험으로 정보를 얻은 새 프레임워크. 2025년 12월 Spark 4.1.0에 헤드라인 기능으로 출시
- 절차형 노트북 vs 선언적 프레임워크 비교 (작은 규모에서는 노트북이 완벽히 관리 가능하지만, 코드베이스가 커지고 더 많은 사람이 손대면 왼쪽 칼럼의 격차가 복리로 쌓인다):
| 절차형 노트북 | 선언적 프레임워크 | |
|---|---|---|
| 의존성 | 셀 위치로 실행 순서가 암시됨. 업스트림 변경 시 다운스트림 영향은 직접 추적 | 모델 간 관계를 선언하면 프레임워크가 실행 순서를 자동 결정 |
| 에러 탐지 | 런타임에 실패 — 깨진 컬럼 참조, 스키마 변경, 조인 키 오타가 잡이 실제 실행될 때만 표면화 | 컴파일 타임에 표면화, row 하나 처리되기 전에 |
| 테스팅 | 누군가 기억해서 쓴 것뿐. 표준 프레임워크도 일관된 강제도 없음 | 데이터 품질 제약을 모델 정의 옆에 선언, 자동 실행 |
| 문서화 | 위키·주석이 코드가 진화하며 stale해지고 동기화 메커니즘 없음 | 로직과 같은 소스에서 생성되어 항상 동기 |
| 버전 관리 | 노트북이 JSON으로 저장되어 diff가 noisy하고 리뷰하기 어렵다(셀 메타데이터·출력 상태가 로직과 섞임). 다중 개발자 팀의 merge 충돌이 고통스럽다 | 평문 SQL 파일, 깨끗한 diff, 의미 있는 코드 리뷰 |
| 이식성 | 비즈니스 로직이 실행 엔진에 결합 — PySpark DataFrame은 이동하지 않는다 | 이식 가능한 SQL. 어댑터나 플랫폼을 바꿔도 로직은 그대로 |
- AI가 전환을 증폭시키는 이유 (2016년 dbt 출시 때는 없던 레이어): LLM은 당신의 커스텀 메타데이터 프레임워크를 모른다. 사내 state store나 맞춤 파이프라인 config를 모른다. 스키마를 기술하고 모델이 나머지를 추론하길 바랄 뿐이다
- 선언적 프레임워크는 그 위에 구조적 안전망을 준다 — 서버가 실행이 시작되기 전에 선언을 검증한다. 대부분의 주요 선언적 도구가 어떤 형태의 dry-run 검증을 제공한다 (dbt의
dbt-dry-run, SDP의--dry-run, Kubernetes/Helm의--dry-run) - LLM이 선언적 config를 생성하고 뭔가 틀리면 프레임워크가 컴파일 타임에, row 하나 움직이기 전에 잡는다. 명령형 노트북 코드로는 같은 실수가 런타임에만 표면화된다
- 에이전트 기반 워크플로 실험에서 이 구조적 속성이 처음 예상보다 더 중요했다. “유일한 커스텀 코드는 비즈니스 로직이어야 한다”는 제약이 단순한 설계 선호가 아니라 뭔가 잘못됐을 때 전체를 디버깅 가능하게 만드는 것
- 선언적 프레임워크는 그 위에 구조적 안전망을 준다 — 서버가 실행이 시작되기 전에 선언을 검증한다. 대부분의 주요 선언적 도구가 어떤 형태의 dry-run 검증을 제공한다 (dbt의
- 선언성의 스펙트럼 — 모든 선언적 도구가 같은 방식으로 선언적이지는 않다:
- 인터페이스 레벨 (dbt류) — 원하는 최종 상태를 기술하면 프레임워크가 의존성 해소·materialization·테스팅을 처리. 투명하게 말하면 실행 레벨에서 dbt는 mutable DB에 대해 명령형 SQL mutation 시퀀스를 돌린다. 쓰는 것에서 선언적이지 실행 방식에서 반드시 그렇지는 않다 — Makefile처럼 생각하라. 노트북에서 벗어나는 대부분의 팀에게 올바른 출발점
- 실행 레벨 (SQLMesh) — MODEL 블록이 속성·의존성·업데이트 방식·스케줄을 코드에 직접 선언하도록 설계되었고, plan/apply 워크플로가 무엇이 바뀔지 실행 전에 정확히 보여주며, 가상 환경이 파이프라인 변경을 격리해 테스트하게 한다
- 플랫폼 네이티브 (Dynamic Tables, Lakeflow, MLV, Spark SDP) — 설치할 프레임워크도 설정할 어댑터도 없다. 트레이드오프는 이식성
- 모든 것에 걸친 주의 — leaky abstraction: 선언적 도구는 실행을 추상화하고 그게 대체로 요점이지만 추상은 새고, 샐 때 느껴진다. 특정 컴퓨트 설정, spot 인스턴스 선택, 세밀한 성능 튜닝 같은 granular 통제를 포기하게 된다. 대부분의 변환 워크로드에는 상관없지만 복잡한 성능·비용 요구가 있는 팀은 미리 알아둘 가치가 있다 — 편의를 위해 통제를 거래하는 것이고 가끔 추상이 커버하는 것의 가장자리에 부딪힌다
- 범위 — 변환 레이어가 가장 성숙하다: 인제스션도 따라오고 있고(Fivetran, Airbyte, Fabric Copy Job, Lakeflow Connect가 모두 같은 원칙 — 무엇을 어디로 옮길지 선언하면 플랫폼이 실행·스키마 변경·증분 상태를 처리), Python·PySpark는 진짜로 필요한 곳(복잡한 API 인제스션, 스트리밍, ML 추론, 반복 알고리즘, 깊게 중첩된 데이터)에서 역할이 남고 선언적 도구가 거기서 대체하려 하지 않는다
- 브릭 비유: 집을 짓는다면 벽돌공에게 각 벽돌을 어떻게 어디에 놓고 모르타르를 어떻게 섞을지 말할 것인가, 아니면 그냥 “brick veneer를 원한다”고 할 것인가? 그리고 brick veneer를 원한다고 해서 벽돌공이 집에 가는 것은 아니다 — 여전히 현장에서 벽돌이 필요한 곳에 벽돌을 놓는다
관련 위키
- Declarative Data Engineering
- Analytics Engineering
- Open Source Data Stack
- Data Pipeline Fundamentals
- Workflow Orchestration
- Incremental Materialization
- Medallion Architecture
Source: 원문 보기