Vol. 01 · No. 06
— Field Notes —
July 2026
A Field Note on Data Pipelines

Replace Your SQL Pipelines with Polars

Your transformation stack is four tools, three languages, and a warehouse you cannot leave. A Polars, Dagster, and pytest stack is one language, any data source in and out, and data contracts enforceable in CI before a single row reaches production.

Every data team I know runs a transformation stack that nobody designed on purpose. Fivetran for ingestion. dbt for SQL models inside the warehouse. Python scripts for the transforms SQL cannot express. pytest duct-taped to dbt for testing. The result is a pipeline that touches five tools before a dashboard loads, and when something breaks at 2 AM, the on-call engineer traces a bug through a YAML config, a Jinja template, a SQL CTE, and a Python script that has not been opened in six months. There is a simpler way, and it has been shipping in production since late 2025.

One Python function fetches from any source, transforms at Rust speed, and writes to any destination. Dagster orchestrates it. pytest enforces the contract.

— working rule
01 · The fragmentation tax

SQL is not a software engineering language. That stops being acceptable at scale.

SQL has no functions you can import, no type system you can rely on, and no standard way to write a test. Every warehouse has its own dialect. Every UDF is incompatible with every other warehouse. Moving from Snowflake to BigQuery means rewriting your transformation layer. That is vendor lock-in dressed as a query language.

Capability SQL + dbt stack Polars + Dagster + pytest stack
Languages 3–4 SQL, Jinja, YAML, Python (for edges) 1 Python
Testing dbt tests run against warehouse compute, after deploy pytest runs locally in seconds, enforceable in CI before merge
Data source portability Locked to warehouse dialect; migration rewrites transforms Same API for S3, GCS, Azure, Delta, Iceberg, Parquet, databases
Modularity Jinja macros, no real functions or classes Python functions, classes, packages, importable across projects
AI code generation LLMs generate dialect-specific SQL with frequent errors LLMs generate typed Polars expressions verified against live API
Local development Requires warehouse connection or CI run Runs on a laptop, no external dependencies
Figure 1 The capability gap at a glance. The SQL stack adds tools to compensate for SQL's limits. The Polars stack removes the need for those tools.
02 · One function, any source, any destination

The same API works across S3, GCS, Azure, Delta, Iceberg, and databases.

This is the advantage that is hardest to explain to someone who has only worked inside a warehouse. SQL is locked to the engine you are connected to. Polars reads from any object store, any lakehouse format, any database — all through the same `pl.read_*()` and `pl.scan_*()` API. Switching from CSV to Parquet is one word. Moving from local dev to S3 is a path prefix. The transformation logic stays identical.

The universal reader

One API, five storage backends

Same syntax whether your data is in the cloud, on disk, or in a lakehouse table. The pipeline does not care where the bytes live.

Cloud object stores

S3, GCS, Azure Blob

`pl.scan_parquet("s3://bucket/orders/")` or `gs://` or `az://` — same call, different prefix.

Lakehouse formats

Delta Lake, Apache Iceberg

`pl.scan_delta("az://container/table/")` — lazy scan, query optimizer, no Spark cluster needed.

Local files

CSV, Parquet, JSON, Arrow, IPC

`pl.read_csv("data.csv")` → change to `pl.read_parquet("data.parquet")` — one word, zero logic changes.

Databases

Postgres, MySQL, SQLite, any connector

`pl.read_database(query, connection)` — pull exactly what you need, transform in Rust, write anywhere.

The real win

Portability that SQL cannot match

Migrating warehouses? Change connection strings, not transformation code. Your pipeline logic is warehouse-agnostic.

Figure 2 The universal I/O surface. Polars decouples the transformation logic from the storage backend. SQL cannot do this — every warehouse dialect is a form of lock-in.
03 · The performance boundary

Polars with streaming beats warehouse SQL on complex transforms at a fraction of the cost.

The January 2026 benchmark by Reliable Data Engineering ran Polars 0.20.x with streaming engine against Snowflake X-Small on identical data, identical cloud, identical transforms. Ten runs each, median reported. The results are not subtle.

Test Polars result Snowflake SQL Read it carefully
Simple aggregation (100K–100M rows) Won every size Slower 8 vCPUs at $0.34/hr vs 4 cores at $2/hr.
String manipulation (50M rows) 7.6 seconds 23.1 seconds Vectorized Rust regex vs SQL string functions. 3× gap.
Window functions (up to 10M rows) Won small–medium Caught up at 10M+ Both have mature window implementations. Performance converges.
Complex joins (4 tables, filtered) Competitive More mature join algorithms Polars' predicate pushdown through joins is a structural advantage.
Customer LTV (RFM + decay) Cleaner code, 40% fewer lines Nested CTEs, UDFs Complex business logic is where Polars earns its place.
Figure 3 Polars vs Snowflake SQL, January 2026. The same data, cloud setup, and methodology across five test categories. Polars wins on compute-heavy transforms; SQL converges at scale on simple relational operations.
04 · Data contracts, enforced

pytest gives you data contracts. dbt tests give you warehouse bills.

The testing gap is the single largest structural advantage of the Polars stack. dbt tests run against the warehouse — slow, expensive, and after the code has already been deployed. pytest runs in seconds, locally, for free, and you can enforce data contracts in CI before a single row reaches production.

The testing gap

Two testing models, one structural difference

dbt tests verify data at rest. pytest verifies logic before it runs. The gap is not tooling quality. It is where the verification happens.

dbt tests

After the fact

Run against warehouse compute. `not_null`, `unique`, `accepted_values`. Slow. Expensive. Post-deploy. Good for data quality monitoring, bad for preventing bugs.

pytest

Before the merge

Synthetic input → Polars transform → assert on output. 3 seconds locally. Runs in CI. Catches logic errors before they touch data. No warehouse needed.

What pytest can enforce that dbt cannot

Business rules as code

`assert result["segment"].is_in(["VIP","Standard"]).all()` — every row gets a valid segment. Test it once, enforce it forever.

The structural truth

Verification moves left

dbt tests catch problems in production. pytest catches them in development. The cost of a bug caught in CI is near zero. The cost of one caught in the warehouse is real.

Figure 4 The testing surface. pytest lets you verify transformation logic before it sees real data. dbt tests verify data quality after the fact. Both have value. Only one prevents bugs.
05 · The AI wrinkle

AI coding agents generate Polars expressions more reliably than SQL.

When an LLM writes SQL, it has to guess which dialect you are using, whether your warehouse supports `QUALIFY`, whether `STRING_AGG` or `LISTAGG` is the right function, and where the optimizer will place the join. When it writes a Polars expression, the API is typed, composable, and verified against a live MCP server that introspects the installed version at runtime. That is not speculation. It is already shipping.

The code-generation surface

Why agents produce better Polars than SQL

The difference is not model quality. It is API surface area. Polars has a smaller, typed, versioned API. SQL has decades of dialect drift.

SQL generation

Dialect guessing game

The LLM must infer Snowflake vs BigQuery vs Postgres vs Redshift. Wrong guess = runtime error. No API to validate against at generation time.

Polars generation

Live API verification

The Polars MCP server lets agents query method signatures at runtime. `pl.col("x").sum().over("y")` either type-checks or fails with a clear error.

Production evidence

200 turns, $10–15, zero manual code

One practitioner ported an entire BigQuery + dbt layer to Polars using Claude Code. Row-level verification passed on the first try. Try that with SQL-to-SQL migration.

The 2027 bet

Agents default to Polars

Polars has an official agent skill and MCP server. dbt does not. The agents your team will rely on next year are already better at generating Polars than SQL.

Figure 5 The AI code-generation surface. SQL generation is a dialect-guessing game. Polars generation is API-verified. Both models are improving. Only one has a verification layer.
06 · The honest limits

Where SQL still wins, and why you should keep it there.

No technology replaces everything. The Polars stack is not a total answer. It is a better answer for the parts of the pipeline where SQL is the wrong tool being used for lack of an alternative. Here is where SQL still wins, and why the winning teams use both.

Scenario Default to Reason
Data already in Snowflake, BigQuery, or Redshift; transform is SELECT, GROUP BY, basic joins SQL (dbt) The optimizer is next to the data. Extracting to Polars pays egress and loses leverage.
Team is entirely analysts, no Python experience, governance and lineage are hard requirements today SQL (dbt) The best tool is the one your team can maintain. dbt's docs and lineage are genuinely useful.
Multi-petabyte, governed, SLAs with auditors SQL (warehouse) Auto-scaling, query history, role-based access. Polars governance tooling is maturing but not turnkey yet.
Complex business logic, string processing, ML feature engineering, multi-source ETL Polars + Dagster Method chains beat CTEs. pytest beats dbt tests. Python modules beat Jinja macros.
Local dev, CI, agentic AI pipelines Polars + Dagster No warehouse needed. Instant feedback. AI agents generate better Polars than SQL.
Figure 6 The routing table. SQL wins on warehouse-resident, team-compatible, simple-set-based work. Polars wins on complex logic, multi-source I/O, testability, and AI-assisted development.
Closing observation

The teams that switch first build faster, spend less, and hire better.

The SQL-versus-Polars argument will settle the same way every language-versus-language argument settles: not by consensus, but by builders voting with their time. The builders who have tried both are already writing Polars for the hard parts and keeping SQL for the simple parts. The unresolved question is how many years of unnecessary fragmentation the rest of the field will pay for before catching up.

The decision is not “Polars or SQL.” It is “one unified platform or five tools that barely hold together.” A Polars, Dagster, and pytest stack gives you one language that fetches from any source, transforms at Rust speed, writes to any destination, and enforces data contracts in CI before a single row reaches production. The SQL stack gives you three languages, warehouse lock-in, tests that run against production compute, and an architecture where your most complex logic lives in stored procedures nobody can debug.

The cost of staying with SQL is rising faster than the cost of switching. The window for building a competitive advantage on pipeline architecture is still open, but it will not stay open forever. Every month a team spends debugging Jinja templates and warehouse UDFs is a month a competitor spends writing pytest data contracts and shipping with confidence.

— end —

Replace Your SQL Pipelines with Polars · A Field Note Set in Shadows Into Light Two & Cause

← Back to all posts