一键导入
data-pipeline-design
Data pipeline architecture and design patterns for production-grade ETL/ELT, streaming, and orchestration systems.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Data pipeline architecture and design patterns for production-grade ETL/ELT, streaming, and orchestration systems.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Expert reference for application security — OWASP Top 10 mitigations, auth/authz, secrets management, cryptography, input validation, dependency hygiene, and secure-by-default code patterns
Expert reference for token counting, prompt compression, cost estimation, and quality preservation when optimizing prompts for Claude models
Experimentation design and A/B testing standards for product teams
Expert reference for digital accessibility — WCAG conformance, ARIA, and inclusive design patterns
Authoritative reference for agent architecture selection, multi-agent orchestration, tool design, memory systems, and failure mode prevention
Expert reference for evaluating LLM systems, RAG pipelines, and AI features in production
| name | data-pipeline-design |
| description | Data pipeline architecture and design patterns for production-grade ETL/ELT, streaming, and orchestration systems. |
| version | 1 |
The Idempotency Contract Every pipeline step is a pure function: same input always produces same output, with no observable side effects on re-execution. This means: writes are upserts or truncate-insert, never append-only appends. File ingestions use content hashing or watermark tracking to prevent double-processing. Achieving idempotency is not optional — it is the mechanism by which backfills, retries, and recovery are possible without engineering heroics.
The Data Contract A data contract is an explicit agreement between a producer and consumer: the schema, semantics, SLA, and ownership of a dataset. Contracts are versioned. Changes to a contract follow a deprecation lifecycle. Without contracts, pipelines are tightly coupled to undocumented assumptions that break silently when the producer changes. Schema registries enforce the syntax of a contract. Ownership metadata and SLA documentation enforce the semantics.
Push Down or Pull Up Every transformation has a home: the source system, the pipeline, or the warehouse. ELT's principle is to push transformation down into the warehouse where compute is elastic and transformations are replayable. Extract raw, load raw, transform in SQL. Only push transformation into the pipeline layer when the warehouse cannot perform it (e.g., NLP, ML feature extraction, binary decoding) — and even then, emit both the raw and transformed values.
The Dead Letter Queue as a Reliability Buffer A pipeline without a DLQ has two failure modes: it crashes on bad data, or it silently drops bad data. Both are wrong. A DLQ decouples the failure-handling concern from the processing concern. It makes failure visible, measurable, and recoverable. Every streaming pipeline produces a DLQ topic/queue. DLQ depth is a monitored metric. DLQ records are reviewed on a schedule and replayed after fixes.
| Term | Precise Meaning |
|---|---|
| Idempotency | Property of a pipeline step where re-execution with identical input produces identical output without additional side effects. Required for safe retries and backfills. |
| ELT | Extract, Load, Transform — raw data loaded to warehouse first, transformed in-place using tools like dbt. Preferred over ETL when warehouse compute is elastic. |
| ETL | Extract, Transform, Load — data transformed before loading. Appropriate when the warehouse cannot perform the transformation or data must be masked before landing. |
| Data Contract | Explicit schema, semantic, SLA, and ownership agreement between a data producer and consumer. Versioned and enforced via schema registry or documentation. |
| Schema Registry | Central catalog of event schemas (Avro, Protobuf, JSON Schema) with version history. Consumers validate messages against registered schema on read. |
| Dead Letter Queue (DLQ) | Queue/topic where records that fail processing are routed with attached failure metadata. Enables visibility and replay without data loss. |
| Type 2 SCD | Slowly Changing Dimension type 2 — history preserved by adding rows with effective_date, expiry_date, and is_current. Required when historical accuracy matters. |
| Watermark | A high-water mark tracking the latest successfully processed record (e.g., max event_timestamp). Used to implement incremental extraction and prevent reprocessing. |
| Data Freshness SLA | The maximum acceptable age of data in a table or dataset at any given time. Must be defined, measured, and alerted on. |
| Partitioning | Physically dividing table storage by a key (date, category) to reduce query scan cost and enable partition pruning. Never partition on high-cardinality keys. |
| DAG (Directed Acyclic Graph) | The dependency graph of pipeline tasks where edges represent execution dependencies. The core abstraction in Airflow, Prefect, and Dagster. |
| Backfill | Reprocessing historical data for a date range, typically to apply a new transformation or recover from data loss. Requires idempotent pipeline steps to be safe. |
Mistake 1: Non-idempotent pipeline steps using append-only inserts
INSERT INTO orders_cleaned SELECT * FROM orders_raw WHERE date = '{{ ds }}' — re-running this DAG duplicates all rows.MERGE/upsert semantics on a natural key, or DELETE WHERE date = '{{ ds }}' THEN INSERT. In dbt, use unique_key with incremental materialization.Mistake 2: No data quality checks before downstream access
orders_fact. BI dashboards query orders_fact immediately. Nobody validates that 15% of rows have NULL customer_id due to a join bug.Mistake 3: Streaming pipeline with no dead letter queue
<topic>-dlq topic with the original payload plus error message and stack trace. Monitor DLQ depth as a production metric.Mistake 4: Airflow for complex asset-based dependency management
ExternalTaskSensor. Lineage is undocumented. Adding a new consumer means editing 3 DAGs. On failure, tracing impact requires reading DAG code.Mistake 5: No schema registry, schema changes discovered in production
user_id to userId in event payload. Kafka consumer parsing user_id starts producing NULLs. 6 hours of data is corrupted before anyone notices.BACKWARD or FULL compatibility mode to reject breaking changes automatically.Pipeline Architecture: No Observability vs. Instrumented
Bad:
def run_pipeline():
raw = extract_from_postgres()
transformed = transform(raw)
load_to_warehouse(transformed)
# No counts, no validation, no alerts
Good:
def run_pipeline(execution_date):
raw = extract_from_postgres(watermark=get_watermark(execution_date))
log_metric("rows_extracted", len(raw))
validated = run_quality_checks(raw) # raises if null rate > 5%
log_metric("rows_validated", len(validated))
transformed = transform(validated)
log_metric("rows_transformed", len(transformed))
load_to_warehouse(transformed, mode="upsert", key="order_id")
log_metric("rows_loaded", len(transformed))
update_watermark(execution_date)
assert_freshness_sla(table="orders_fact", max_age_minutes=15)
dbt Model: Full Refresh vs. Incremental with Quality Gates
Bad:
-- orders_fact.sql — full rebuild every run, no tests
SELECT order_id, customer_id, amount, created_at
FROM raw.orders
Good:
-- orders_fact.sql
{{ config(
materialized='incremental',
unique_key='order_id',
incremental_strategy='merge',
on_schema_change='fail'
) }}
SELECT order_id, customer_id, amount, created_at
FROM {{ source('raw', 'orders') }}
{% if is_incremental() %}
WHERE created_at > (SELECT MAX(created_at) FROM {{ this }})
{% endif %}
# schema.yml — quality gates
models:
- name: orders_fact
columns:
- name: order_id
tests: [unique, not_null]
- name: customer_id
tests: [not_null, relationships: {to: ref('customers'), field: id}]
- name: amount
tests: [{dbt_utils.accepted_range: {min_value: 0}}]
Schema Change: Breaking vs. Non-Breaking
Bad (breaking, shipped without warning):
// v1: {"user_id": 123, "event": "click"}
// v2: {"userId": 123, "eventType": "click"} ← renamed fields, consumers silently produce NULLs
Good (backward-compatible evolution):
// v1: {"user_id": 123, "event": "click"}
// v2: {"user_id": 123, "userId": 123, "event": "click", "eventType": "click"}
// Both old and new field names present during deprecation window (2 release cycles)
// v3: {"userId": 123, "eventType": "click"} ← only after all consumers migrated