Module 2: Declarative Data Quality Tests With Pandera
Choosing a vehicle: Pandera vs. Great Expectations vs. Soda
Description
Lesson 2 precisely defined what a declarative quality test is. This lesson answers the next question, mandatory before writing a single line of real code: which market tool do you use to interpret those declarations? It isn't a rhetorical question or a marketing comparison — it's a real technical decision, with license, maturity, and vendor-risk evidence, exactly the same way this guide and its sisters already decided DuckDB over Snowflake, or dbt Core over Fivetran. This lesson investigates three real tools — Pandera, Great Expectations, Soda — and picks one, with concrete reasons, not out of habit or because it's the best known.
Connection to the module. This lesson closes the criteria that opens the door to installing something for real: lesson 4 installs exactly the tool this lesson picks, and lessons 5 through 8 use it without ever questioning it again. Everything that follows in this guide — contracts in module 4, anomalies in module 5, freshness in module 6 — gets built on the same tool that wins here.
The running case: the same old question, now about a tool
Review the list of decisions this ecosystem's previous guides already made: DuckDB over a cloud warehouse (data-modeling-for-analytics-guide), dbt Core over managed Fivetran (dbt-analytics-engineering-guide), Apache Iceberg over Delta Lake (lakehouse-and-iceberg-guide). None of those decisions got made by default or by popularity — each guide investigated license, maturity, and the risk of the vendor changing the rules of the game after you already depended on them. This lesson does exactly that same exercise, on the question Kiosko needs answered now: which tool is going to read the quality declarations this module builds over orders_2026-08-14.csv?
An analogy: three kitchen scales, for the same exact-measurement recipe
Lesson 2 compared an imprecise rule ("a bit of salt") against a recipe with exact measurements ("6 grams of salt per 500 grams of dough"). This lesson continues that same analogy, one step further: you already decided you need exact measurements, now you have to choose which scale you're going to weigh them with.
The first scale is a professional laboratory scale, the kind a certified factory would use: it weighs with milligram precision, comes with a huge catalog of accessories — taring, calibrating against a standard, logging every weighing with date and time —, but to weigh salt you first need to set up the whole apparatus, calibrate it, and learn a hundred-page operating manual. That's Great Expectations: the richest catalog in the market, with ceremony proportional to that richness.
The second scale is given away for free by a kitchen-supply company, as long as you use it at home — but the same company made it very clear, in the fine print, that if you ever open your own place and start weighing ingredients to sell to other restaurants using that scale as a service, you have to pay. Plus, the manufacturer already announced that new upgrades to the scale — the more precise sensor, the app that tells you when to calibrate — only come in the paid model. That's Soda: free for personal use, with a license that explicitly restricts offering it as a managed service, and product investment concentrated in the commercial layer.
The third scale is simple, precise, and comes with the complete manufacturing blueprint included: anyone can open it up, understand exactly how it weighs, modify it, give it away, sell it, with no need to ask anyone's permission. It has no automatic calibration sensor or app connection — it weighs exactly what you ask it to weigh, and nothing more. That's Pandera: a license with no conditions attached to anything, and a minimal API that does exactly what this module needs.
Worked example: the same rule, in all three tools' syntax
This is the same rule — unique order_id, non-null and non-negative unit_price, positive quantity — expressed in each of the three tools' real syntax. The Great Expectations and Soda versions are documented syntax reference, not code executed in this guide — following the same rule dbt-analytics-engineering-guide already applied with SQLMesh and lakehouse-and-iceberg-guide with Delta Lake: the alternative gets named, with real evidence, without building a second parallel implementation. Pandera's version does actually run, for real, at the end of this lesson.
Great Expectations (GX Core 1.20.0) — reference, not executed
# gx_reference.py -- REFERENCE ONLY, documented GX Core syntax, not run in this guide
import great_expectations as gx
context = gx.get_context()
data_source = context.data_sources.add_pandas("orders_source")
data_asset = data_source.add_dataframe_asset(name="orders_asset")
batch_definition = data_asset.add_batch_definition_whole_dataframe("orders_batch")
batch = batch_definition.get_batch(batch_parameters={"dataframe": orders_df})
suite = context.suites.add(gx.core.expectation_suite.ExpectationSuite(name="orders_suite"))
suite.add_expectation(gx.expectations.ExpectColumnValuesToBeUnique(column="order_id"))
suite.add_expectation(gx.expectations.ExpectColumnValuesToNotBeNull(column="unit_price"))
suite.add_expectation(gx.expectations.ExpectColumnValuesToBeBetween(column="quantity", min_value=1))
validation_definition = context.validation_definitions.add(
gx.core.validation_definition.ValidationDefinition(name="orders_validation", data=batch_definition, suite=suite)
)
checkpoint = context.checkpoints.add(
gx.checkpoint.checkpoint.Checkpoint(name="orders_checkpoint", validation_definitions=[validation_definition])
)
result = checkpoint.run()
print(result.describe())
Count the steps: DataContext → DataSource → DataAsset → BatchDefinition → Batch → ExpectationSuite (with three Expectations inside) → ValidationDefinition → Checkpoint → run(). Eight distinct objects, chained together, before the first rule — "order_id is unique" — even gets compared once against real data.
Soda (SodaCL, Soda Core) — reference, not executed
# checks.yml -- REFERENCE ONLY, documented SodaCL syntax, not run in this guide
checks for orders_s04:
- duplicate_count(order_id) = 0
- missing_count(unit_price) = 0
- invalid_count(quantity) = 0:
valid min: 1
Four lines of YAML, much lighter than GX — SodaCL is, in itself, a real declarative language, with its own engine that interprets it (soda scan, run from the terminal against this file). Soda's problem isn't its ceremony — it's its license and where the product's investment is going, as shown in the next section.
Pandera 0.32.1 — actually executed
# pandera_vehicle_check.py
import pandera
import pandera.polars as pa
import polars as pl
print(f"pandera version: {pandera.__version__}")
class OrdersSchemaPreview(pa.DataFrameModel):
order_id: str = pa.Field(unique=True)
unit_price: float = pa.Field(nullable=False, ge=0)
quantity: int = pa.Field(gt=0)
clean_rows = pl.DataFrame({
"order_id": ["ORD-V1", "ORD-V2"],
"unit_price": [0.55, 1.20],
"quantity": [2, 1],
})
validated = OrdersSchemaPreview.validate(clean_rows)
print(f"Rows validated with no problem: {validated.height}")
What to expect. Running python3 pandera_vehicle_check.py (with pandera[polars] installed — lesson 4 shows how), the output is exactly this:
pandera version: 0.32.1
Rows validated with no problem: 2
Three lines of declaration — class OrdersSchemaPreview(pa.DataFrameModel): ... —, no DataContext, no Checkpoint, no separate YAML file to interpret with an external engine. OrdersSchemaPreview.validate() runs directly against a Polars DataFrame, in the same Python process, with no prior configuration step.
Comparison table: license, version, and the verdict
| Pandera | Great Expectations (GX Core) | Soda (Soda Core / Library) | |
|---|---|---|---|
| License | MIT | Apache-2.0 | Elastic License 2.0 (not OSI: prohibits offering it as a managed service) |
| Current version | 0.32.1 (jun-29-2026) | 1.20.0 (aug-7-2026) | Soda Core, maintained under the same license; active commercial layer: Soda Library + Soda Cloud |
| Requires Python | >=3.10 | 3.10-3.13 | — |
| Parallel commercial layer | None | None known | Soda Cloud, Team plan: USD 750/month — features like "collaborative data contracts" and "AI-powered data quality" stay on the paid side |
| Minimum ceremony for 3 rules | One class, three Fields | DataContext→DataSource→Batch→Suite→ValidationDefinition→Checkpoint (8 objects) | A 4-line YAML file + the soda scan runner |
| Deterministic output | SchemaErrors.failure_cases, no embedded timestamps | Checkpoint.run()'s result objects include, by default, run metadata (a run identifier with a timestamp) — you have to suppress it on purpose to reproduce output byte-for-byte | Depends on the scan's output format |
| DataFrame engine in this guide | Polars (the only bridge Pandera supports without going through pandas) | Pandas/Spark/SQL (no native Polars) | Declarative SQL, not DataFrame |
Pandera wins for the four concrete reasons this table summarizes: (1) MIT license, with no parallel commercial layer pushing toward a cloud; (2) the minimal API — one class, Field, validate() — is consistent with this entire ecosystem's anti-ceremony bias, the same one that already chose DuckDB over a cloud warehouse and a local SQL catalog over Glue; (3) the output is pure and deterministic, with no extra work needed to keep the byte-for-byte reproducibility every "What to expect" block in this guide requires; (4) it continues exactly the pattern foundations M5's validate_orders() already taught — separating valid rows from invalid ones —, now declared as a schema instead of imperative code.
Diagram: the same rule, three paths to the result
flowchart TD
R["Rule: order_id unique,\nunit_price not null, quantity > 0"]
R --> GX["Great Expectations:\nDataContext -> DataSource -> Batch\n-> Suite -> ValidationDefinition\n-> Checkpoint -> run()"]
R --> SODA["Soda:\nchecks.yml (SodaCL)\n-> soda scan (external engine)"]
R --> PA["Pandera:\nclass OrdersSchema(DataFrameModel)\n-> .validate(df, lazy=True)"]
GX --> GXR["8 chained objects\nApache-2.0, no commercial layer"]
SODA --> SODAR["4 lines of YAML\nElastic License 2.0,\nSoda Cloud Team: USD 750/month"]
PA --> PAR["1 class, MIT,\ndeterministic output"]
PAR --> WIN["Chosen for this guide:\nmodules 2 through 8"]
Going deeper: the same vendor-risk pattern, third time in this ecosystem
This isn't the first time this ecosystem has run into a tool with a free core and a parallel commercial layer. dbt-analytics-engineering-guide already named the same pattern with Fivetran versus dbt Labs: a managed, convenient tool, and a bill that grows with data volume without the user controlling the pace. lakehouse-and-iceberg-guide named it again with Delta Lake: a technically solid table format, but with a company (Databricks) that decides the roadmap and competes directly against whoever adopts it without paying for their cloud. Soda is this pattern's third appearance, in this guide: Soda Core remains free and usable, the Elastic License 2.0 allows self-hosting it at no cost — but that same license explicitly prohibits offering it as a managed service to third parties, and Soda Cloud's public pricing catalog confirms where the product investment lives: a Team plan of USD 750 a month that includes "collaborative data contracts" and AI-powered data quality features, none of which are available in the free core.
Naming this pattern three times isn't a coincidence or an obsession with legal fine print — it's the same discipline this entire guide teaches, applied inward: just as Kiosko needs a versioned data contract to avoid depending on one person's memory, whoever chooses tools for a production pipeline needs to understand, with evidence and not marketing brochures, which part of the tool is really free and which part is a hook toward a subscription. Great Expectations, by contrast, doesn't show that pattern: full Apache-2.0, with no known parallel commercial layer competing against its own free core — its disadvantage against Pandera in this guide is ceremony and fit with the DataFrame engine (Polars), not license.
Common mistakes
Choosing Pandera "because it's the simplest" without checking the other two's license. What happens: someone reads this lesson's comparison table, sees Pandera needs less code, and concludes it won because it's easier to write. Why it happens: GX's ceremony is the most visible thing at a glance — eight chained objects against one class — so it's tempting to stop there. How to spot it: if your justification for choosing Pandera is "it has fewer lines," you're missing half the evidence — Soda is lightweight too (four lines of YAML) and yet this guide doesn't pick it as the main vehicle. How to fix it: the real decision combines three factors — license, project maturity, and fit with the DataFrame engine this guide already chose (Polars) —, not just how many lines it takes to write three rules.
Concluding Great Expectations is "worse" than Pandera in an absolute sense. What happens: someone, after seeing the eight-chained-objects table, decides GX is a badly designed tool. Why it happens: comparing against Pandera's minimal example makes GX's ceremony look, at first glance, like over-engineering. How to spot it: if your conclusion is "GX is badly built," you're missing context — GX solves a broader problem than this guide needs: DataContext and Checkpoint exist because GX is designed to orchestrate validations over data living in different systems (databases, Spark, the cloud), with run history and built-in alerting. How to fix it: this guide's choice is specific to this context — a Polars DataFrame, running locally, with no need for managed run history —; in a different context (a large organization, with dozens of data sources and a need for an expectations catalog shared across teams), GX could be the right choice. This lesson compares tools for a concrete case, it doesn't dictate a universal ranking.
Thinking "Elastic License 2.0" means "can't be used." What happens: someone reads that Soda Core isn't OSI and concludes it's forbidden to install or use at all. Why it happens: "not open source in the OSI sense" sounds, on first read, like "closed" or "paid." How to spot it: if your summary of Soda is "can't be used for free," re-read this lesson's table — Soda Core is free to self-host and use within your own organization; Elastic License 2.0's specific restriction is not being able to resell it as a managed service to third parties, something that doesn't apply to Kiosko's case anyway. How to fix it: the reason for not choosing Soda in this guide isn't that it's forbidden to use — it's that the vendor's product investment is concentrated in the commercial layer (Soda Cloud, USD 750/month), the same vendor-risk pattern this guide avoids repeating, consistent with the decisions already made in dbt-analytics-engineering-guide and lakehouse-and-iceberg-guide.
Exercises
Exercise 1 — Count the objects needed for one more rule in each tool. Using this lesson's GX example, if Kiosko needed to add a fourth rule — store_id can't be null —, how many new lines are needed in the suite.add_expectation(...) block? And in Pandera, using this lesson's OrdersSchemaPreview, how many new lines are needed in the class?
See solution
In GX, one new line is enough: suite.add_expectation(gx.expectations.ExpectColumnValuesToNotBeNull(column="store_id")) — the DataContext/Batch/Checkpoint ceremony is already built, so adding one more rule to an existing suite is, in fact, lightweight. In Pandera, one line is also enough: store_id: str = pa.Field(nullable=False), added inside the OrdersSchemaPreview class. The difference between the two tools isn't how much it costs to add a new rule to an already-existing schema — in both it's one line —, it's how much it costs to get the whole apparatus running the first time, before writing the first rule.
Exercise 2 — Explain, in your own words, why deterministic output matters specifically for this guide. This lesson's comparison table mentions that GX's results include, by default, run metadata (a run identifier with a timestamp). In 2-3 sentences, explain why this is a problem specific to the "What to expect" format each lesson in this guide uses, and not necessarily a problem for any other use of GX.
See solution
Every "What to expect" block in this guide — and in the ecosystem's previous eight guides — promises byte-for-byte reproducible output: anyone running the same code, with the same fixed data, should see exactly the same printed text. A result that includes, by default, a run identifier with a real timestamp automatically breaks that promise — two runs of the same code, at different moments, would produce a result with a different field —, and you'd have to write extra code just to suppress that field before every demonstration. For a real production use of GX, that run identifier is valuable information (knowing when each validation happened); the problem is specific to this guide's pedagogical context, not a design flaw in the tool.
Exercise 3 — Argue whether Soda would be a reasonable choice if Kiosko already had a twenty-person team. Based on this lesson's table and Going deeper section, in 3-4 sentences, argue whether this guide's decision — not choosing Soda — would still be correct if Kiosko were a much larger company, with a real budget for data tools.
See solution
With a real budget and a twenty-person team, Soda Cloud (the Team plan, USD 750/month) could be a perfectly reasonable choice: at that scale, features like collaborative data contracts and a catalog shared across several analysts can justify the cost, the exact same argument dbt-analytics-engineering-guide already applied to Fivetran for teams that prioritize speed over full control. This guide's decision is specific to Kiosko's context within this educational ecosystem: a guide that teaches the whole mechanism from scratch, with $0 resources, and that already built DuckDB, Polars, and local tools across the previous eight guides. This lesson's argument isn't "Soda is bad" — it's "Pandera is the right choice for what this guide needs to teach, with the license and vendor-risk evidence laid on the table so the decision, in a real context, gets made by whoever knows their own budget."
Summary and next step
In this lesson you compared three real tools from the data quality market — Pandera, Great Expectations, Soda — with license, current version, minimum ceremony, and vendor-risk evidence, not out of habit. You saw the same business rule written in all three tools' real syntax, actually ran Pandera's version for real, and reached a justified decision: Pandera, for its MIT license with no parallel commercial layer, its minimal API consistent with the rest of this ecosystem, and its pure, deterministic output.
Before moving on you should be able to: name each of the three tools' exact license and what it means in practice; explain why GX's ceremony doesn't make it "worse," just different in purpose; and recognize the vendor-risk pattern this guide already saw twice before (Fivetran, Delta Lake) repeated a third time with Soda.
You have your chosen tool. Lesson 4 actually installs it — pip install "pandera[polars]" — and builds the technical bridge connecting kiosko.duckdb to Polars, the only DataFrame format Pandera needs in this guide.
Resources
- Pandera — PyPI (current version 0.32.1, MIT license, requires Python
>=3.10). pypi.org/project/pandera. In English. - Great Expectations (GX Core) — PyPI (current version 1.20.0, published August 7, 2026; Apache-2.0 license). pypi.org/project/great-expectations. In English.
- Great Expectations — GX Core quickstart guide (the
DataContext→DataSource→Batch→Suite→ValidationDefinition→Checkpointflow quoted in this lesson). docs.greatexpectations.io/docs/core/introduction/try_gx. In English. - Soda Core — official repository and its
LICENSE(Elastic License 2.0, confirmed in the repository's license file). github.com/sodadata/soda-core. In English. - Soda — official pricing page (Soda Cloud's Team plan, USD 750/month, confirming the commercial layer quoted in this lesson). soda.io/pricing. In English.
dbt-analytics-engineering-guideDESIGN — the source of the vendor-risk warning pattern (Fivetran/dbt Labs) this lesson mirrors for Soda.src/guides/dbt-analytics-engineering-guide/DISENO.md. In Spanish.lakehouse-and-iceberg-guideDESIGN — the source of the same pattern applied to Delta Lake, its second appearance before this one.src/guides/lakehouse-and-iceberg-guide/DISENO.md. In Spanish.- This guide's DESIGN — the complete vehicle decision, with the four reasons for Pandera quoted in this lesson.
src/guides/data-reliability-and-governance-guide/DISENO.md. In Spanish.