Skip to content

Post-Init Validation and Invariants

Page Maps

graph LR
  family["Python Programming"]
  program["Python Object-Oriented Programming"]
  section["State Validation Typestate"]
  page["Post-Init Validation and Invariants"]
  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

Validation becomes much easier to reason about when objects do not begin life in an invalid state.

If construction permits broken objects, every later method must become suspicious:

  • "did anyone create this correctly?"
  • "should I re-check this field here?"
  • "can I trust this combination of values?"

That uncertainty spreads everywhere. Construction-time invariant checking prevents that drift.

What an invariant is

An invariant is a condition that should always hold for a valid instance.

Examples:

  • a threshold stays within an allowed range
  • a start time does not come after an end time
  • an object marked active must already have required fields populated

If an invariant can be broken silently, the rest of the design becomes harder to trust.

Why post-init validation matters

Post-init validation is the point where you take raw constructor inputs and confirm that the instance is legitimate as a whole.

It is useful because some rules depend on combinations of fields, not only one field at a time.

Single-field checks are not enough when meaning depends on the full object shape.

The design principle

Prefer this rule:

If an object exists, it should already satisfy its core invariants.

That does not mean every future state is guaranteed forever. It means the object should not start life broken and hope later code will fix it.

What this changes in the rest of the code

When construction enforces invariants:

  • downstream methods can be simpler
  • tests can assume a valid starting point
  • invalid combinations fail early and locally
  • readers do not need to mentally track "maybe broken" objects everywhere

This is a teaching win as well as a design win.

Common mistakes

  • validating only at a far-away service boundary and never in the object itself
  • checking fields one by one but missing invalid combinations
  • allowing placeholder instances that are supposed to become valid later
  • catching invariant errors too late, after the object has already spread

The worst version is the half-formed object that floats through several layers before someone notices it should never have existed.

When not everything belongs in post-init

Not every rule belongs in construction.

Construction-time validation is best for:

  • identity and field-shape assumptions
  • required relationships between fields
  • "this object is nonsense if this is false" rules

Lifecycle-specific rules may belong on transition methods instead.

The point is not "all validation in one place." The point is "each rule at the earliest honest point."

Review checklist

Question Good sign
does the object reject broken field combinations at construction? yes
can downstream code mostly assume a valid starting instance? yes
are lifecycle rules separated from initial invariants when needed? yes
are half-formed placeholder instances avoided? yes

Capstone connection

In the capstone, this lesson matters anywhere an object can be created from several related inputs:

  • configs
  • policies
  • scheduled actions
  • domain commands that become durable objects

If the capstone still creates objects first and asks whether they are valid later, this lesson should be applied next.

Exit check

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

  • explain one invariant that should be enforced at construction time
  • explain why broken instances make downstream code noisier
  • distinguish between initial invariants and later lifecycle transition rules