Skip to content

Runtime Contracts, Assertions, and Defensive Checks

Page Maps

graph LR
  family["Python Programming"]
  program["Python Object-Oriented Programming"]
  section["Testing Contracts Verification Depth"]
  page["Runtime Contracts, Assertions, and Defensive Checks"]
  capstone["Capstone evidence"]

  family --> program --> section --> page
  page -.applies in.-> capstone
flowchart LR
  orient["Orient on the page map"] --> read["Read the main claim and examples"]
  read --> inspect["Inspect the related code, proof, or capstone surface"]
  inspect --> verify["Run or review the verification path"]
  verify --> apply["Apply the idea back to the module and capstone"]

Read the first diagram as a placement map: this page is one concept inside its parent module, not a detached essay, and the capstone is the pressure test for whether the idea holds. Read the second diagram as the working rhythm for the page: name the problem, study the example, identify the boundary, then carry one review question forward.

Why this lesson matters

Tests are not the only place a system can defend its design claims.

At runtime, some boundaries still need explicit checks so the system can fail:

  • early
  • clearly
  • near the source of the violation

But runtime checks are easy to misuse. Teams often blur together:

  • caller-facing validation
  • internal assertions
  • defensive parsing
  • panic-style last resorts

When these are mixed carelessly, the code stops explaining what kind of failure happened and who is supposed to respond to it.

A runtime check should answer one clear failure question

Before adding a check, ask:

  • is this guarding against a caller mistake?
  • is this guarding against malformed or hostile boundary data?
  • is this guarding an internal invariant that should be impossible if surrounding code is correct?

Those are different situations, and they deserve different responses.

Domain errors and assertions are not interchangeable

Use a domain or application-facing error when the caller can legitimately do something invalid or unsupported.

Examples:

  • acknowledging an already closed incident
  • registering an unsupported plugin capability
  • submitting malformed input through a public command surface

Use an assertion when the condition represents a programmer error, corrupted control flow, or a supposedly impossible internal state.

Examples:

  • an aggregate claims to be active but lacks the data required for active use
  • a transition table reaches a branch the surrounding code should have ruled out
  • an internal coordinator observes a state combination that violates a preserved invariant

This distinction matters because the error message communicates who owns the next move.

Defensive checks belong at data-entry boundaries

Many important checks are not deep in the domain. They sit at the edges where the system accepts data or commands from the outside.

Examples:

  • codec parsing
  • repository hydration
  • adapter responses
  • plugin loading
  • file or network payload decoding

The purpose of these checks is not to prove the entire system correct. It is to reject bad or unsupported input before it contaminates deeper layers.

Assertions should clarify the invariant, not narrate confusion

An assertion such as:

  • assert x

usually teaches very little.

A stronger assertion names the preserved truth:

  • the incident version must advance monotonically after persistence
  • an acknowledged incident must record the acknowledging actor
  • a scheduler callback must not run after the coordinator is closed

The message should help a future reader understand what the system thought was impossible and why the failure is serious.

Checks still need proof

One common failure mode is decorative runtime safety:

  • a vague assertion nobody tests
  • a defensive branch nobody exercises
  • a malformed-input path nobody reviews

These checks look reassuring but become stale quickly.

If a runtime guard matters to correctness, recovery, or diagnosis, it should have:

  • a direct test
  • or a documented and intentionally reviewed failure route

Otherwise the check is easy to break without anyone noticing.

A worked example

Suppose a repository loads incident snapshots from durable storage.

Useful runtime checks might include:

  • required fields are present before hydration
  • version numbers are non-negative
  • persisted state names map to supported lifecycle states

If a snapshot is malformed, that is not a domain disagreement between healthy callers. It is a boundary integrity problem. The repository should reject it explicitly.

Now suppose the aggregate's close() method internally reaches a branch where the incident is supposedly already closed yet still emits a new closure event. That is more likely an internal invariant break than a caller error. An assertion or equivalent invariant failure route is the better fit there.

Checks should strengthen, not duplicate, the model

Do not add runtime checks everywhere just because a boundary feels risky.

A good check should either:

  • stop bad data before it enters the model
  • expose an impossible internal condition as soon as it appears

If a check simply repeats clear domain validation without clarifying responsibility, it may be noise rather than protection.

Common mistakes

  • using assertions for caller mistakes that deserve explicit errors
  • using vague errors for invariant breaks that should be treated as internal faults
  • adding defensive branches with no test or review path
  • placing boundary checks too deep, after corrupted data already spread
  • writing assertion messages that do not explain the preserved truth

Review checklist

Question Good sign
is it clear whether the failure is a caller problem, boundary problem, or invariant break? yes
do important entry boundaries reject malformed input early? yes
do assertion messages name the invariant rather than just the symptom? yes
do the important checks have direct verification? yes

Capstone connection

Use the capstone boundaries to ask:

  • where should malformed repository or adapter data be rejected before hydration or dispatch?
  • which current assertions are too vague to help diagnose an invariant failure?
  • which caller-facing errors are incorrectly being treated as internal crashes, or vice versa?

That review makes runtime checks part of the proof architecture instead of a pile of scattered guard clauses.

Exit check

Leave this lesson only when you can do all of these:

  • explain the difference between caller-facing validation and internal invariant assertions
  • name one capstone boundary that deserves a defensive check
  • rewrite one weak assertion message so it states the preserved truth directly