Why we model around decision boundaries, not source cleanup
We shape analytical models around the business decision or entity they need to represent, not around the temporary cleanup steps needed to tame source data on the way in.
Start an analytical model by asking, “Which decision should this output make easier?” rather than, “How do we clean this source?”
Source cleanup is necessary. It’s also a poor organizing principle for the semantic layer. A table named after the seventh normalization step tells consumers how difficult ingestion was, not what one row means or when they should use it.
Start from the decision and actor
Name who uses the model and what they decide.
A sales manager may decide which accounts need intervention. Operations may decide which orders risk missing a service window. Finance may decide which revenue belongs in a reporting period. Each decision has a grain, timing requirement, history model, and tolerance for uncertainty.
Work backward from that contract:
- What entity or event does one row represent?
- Which measures and dimensions support the decision?
- Which history must remain visible?
- How fresh must the result be?
- Which source conflicts need resolution?
- Who owns changes to the definition?
This produces a model boundary the business can challenge. “Cleaned CRM data” can’t answer the same questions because cleanup has no consumer contract.
Keep source preparation honest
Raw and preparation layers should expose source behavior rather than conceal it.
Normalize types, timestamps, encodings, duplicate delivery, nested structures, and source-specific identifiers. Preserve source keys, extraction metadata, deletion markers, and rejected records needed for replay and debugging.
Name these models after the source or preparation responsibility. Their job is to make source facts usable and traceable, not to become a universal business entity by gradual accumulation.
A preparation table can remain awkward where the source is awkward. That honesty is useful. The semantic layer then makes the deliberate business choices.
Resolve meaning once at the semantic boundary
The semantic model chooses grain, identity, source precedence, business time, classification, and inclusion rules.
Suppose three systems describe customers. The preparation layer preserves all three identities and their source timestamps. A customer model decides which records belong to the same business entity, which source owns each attribute, how inactive or merged customers behave, and whether history is current-state or slowly changing.
Downstream marts should consume that decision instead of each recreating a slightly different customer join. Repeated cleanup is usually evidence that the semantic boundary never formed.
Separate entities from decision-specific marts
A stable business entity can support several decisions without becoming one enormous table.
Build reusable semantic entities such as customers, orders, products, and ownership where those concepts are genuinely shared. Then build decision-specific marts at the grain users need: account risk by owner, daily fulfilled revenue, open-order exceptions, or customer-product opportunity.
Don’t force every use case into one canonical table with hundreds of nullable columns. Shared meaning and universal shape are different ambitions. The latter tends to become a source cleanup layer wearing a business name.
Make history follow the decision
A current account-routing decision may need the latest owner. A commission calculation may need the owner at transaction time. A customer-risk alert may need both current state and recent trend.
Choose snapshot, event, or slowly changing history according to that requirement. Don’t inherit history semantics from whichever source table was easiest to copy.
This decision also drives incremental correction. Once the model states what historical row can change, stale-row prevention can follow the business boundary rather than a generic ingestion window.
Keep orchestration out of meaning
A scheduler may sequence extraction, preparation, semantic models, and publication. It shouldn’t decide which source wins or which rows qualify for the mart.
Those rules belong in named models where reviewers can see them. A runtime switch that chooses “old customer logic” for one run and “new customer logic” for another creates historical meaning through workflow state and damages trust.
Test the contract users depend on
Assertions should reflect the decision boundary: one row per declared key, accepted status values, resolved ownership where required, reconciliation to source totals, and freshness appropriate to the action.
Source-level cleanliness checks are still useful. They don’t replace semantic tests. A perfectly typed, duplicate-free source can still produce the wrong business entity or period.
Know when the boundary should move
A model should change when the business decision, entity definition, or source authority changes. Record that as a semantic migration. Backfill history only when the new definition is intended to apply historically. Otherwise, version the boundary or preserve effective dates.
Don’t reshape a mart merely because one new source column became available. New data earns inclusion when it changes the decision or removes a known limitation.
We model around decision boundaries because they give data a durable responsibility. Cleanup prepares evidence, semantic models decide what it means, and marts shape it for action. Keeping those jobs separate lets the warehouse absorb ugly sources without making every consumer inherit their ugliness.
More in this domain: Data
Browse allBigQuery freshness checks that detect partial loads
A recent row doesn’t prove a load is complete. Track expected source scopes, validated source coverage, and successful publication separately so missing data can’t look healthy.
Dataform assertions: make failures block downstream models
A failed assertion won’t automatically stop a consumer of the tested table. Add the assertion dependency, include it in the run, and publish only from a stable, validated candidate.
BigQuery physical vs. logical storage: compare the full bill
Physical billing rewards compression, but retained versions can consume the savings. Compare both models using the dataset’s current data, rewrite history, and recovery requirements.
BigQuery primary keys are NOT ENFORCED: what to validate
BigQuery can use primary and foreign keys to optimize queries without checking whether the data satisfies them. Validate the full relationship before declaring it, then protect it during publication.
Dataform vs. transformation scripts: keeping warehouse changes reviewable
Use Dataform when shared BigQuery transformations need explicit dependencies, materialization, and checks. Keep scripts where they remain simpler, and judge both by how safely someone else can change them.
Related patterns
Why declarative data models scale better than script-driven pipelines
Declarative modeling scales better because it keeps business shape, dependencies, and reviewable intent visible as the platform and team both grow.
Partitioning defaults for event tables that don't lie
Partitioning is not just a performance tweak. It is one of the cheapest ways to control scan blast radius, but only if the partition contract matches how the table is actually queried.
BigQuery cost spikes usually come from table shape, not queries
When BigQuery spend jumps, the cause is usually in model shape, weak incremental design, or unnecessary reprocessing long before it's a single bad query.
How we prevent stale rows in incremental fact models
Incremental fact models stay trustworthy only when record identity, reprocessing rules, and cleanup boundaries are designed on purpose instead of patched after drift shows up.