Recipe: Handle Schema and Type Drift

Freeze inferred schemas, count type mismatches, and decide when to coerce or fail.

If you do not provide a schema, BlazeRules infers rule-referenced field types from the first evaluated batch. After that, the engine treats the schema as fixed.

Example:

{"account_age_days": 42}

If a later record sends:

{"account_age_days": "forty-two"}

the value is a type mismatch. The configured type_mismatch_mode decides what happens:

ModeBehavior
NULL_ON_TYPE_ERRORTreat the field as null for that row; normal comparisons return false.
COERCETry lightweight coercion such as "42" to an integer.
HARD_FAIL_TYPEThrow a structured type error for the batch.

Production default:

cfg = blazerules.EngineConfig()
cfg.ingest_error_mode = blazerules.IngestErrorMode.SKIP_AND_COUNT
cfg.type_mismatch_mode = blazerules.TypeMismatchMode.NULL_ON_TYPE_ERROR
CLI equivalent: run the same policy in a shell script
cat events.ndjson | blazerules eval --rules rules.yaml --input ndjson --stdin --output summary

The defaults (SKIP_AND_COUNT / NULL_ON_TYPE_ERROR) apply unless you pass --type-mismatch-mode / --ingest-error-mode for non-default policies.

Watch:

result = engine.evaluate_ndjson(payload)
print(result.error_counts)
print(result.error_samples)
CLI equivalent: print drift diagnostics from stdin
cat events.ndjson | blazerules eval --rules rules.yaml --input ndjson --stdin --output summary

The summary reports processed/skipped/error counters.

For important fields, pin the type in fields: so inference cannot guess differently across environments:

fields:
  account_age_days: {type: int32, nullable: false}
  amount: {type: float32, nullable: false}
  event_ts_ms: {type: timestamp_ms, nullable: false}

Did this page help you?