Skip to main content
chaptertechnical-review

TypeScript, runtime contracts and deterministic state

Prerequisites and learning outcome​

Know discriminated unions, Promises and React reducers. Learn to distinguish compile-time convenience from runtime authority by rejecting malformed events and replaying valid ones deterministically.

Why types alone do not protect the boundary​

An HTTP response, browser storage entry and model-produced object are all unknown until decoded. as Event only changes what the compiler assumes. Decode at ingress, then keep the internal model narrower. Prefer a discriminated union for operations over an object with many optional fields. An approval command needs an exact proposal hash; a cancellation command does not.

The fixture uses strict Zod schemas for identities, submissions, proposals and versioned UI events. Strict decoding rejects unexpected fields instead of silently letting a new producer smuggle capabilities into an old consumer. Compatibility therefore requires an explicit protocol rollout; strictness is not a substitute for version negotiation.

Implementation: deterministic projections​

The projection is a pure function of state and event. IDs and timestamps arrive in persisted events; neither Date.now() nor random IDs belong in the reducer. Sequence numbers are monotonic within a run, not globally. Duplicates and old events are ignored. A sequence gap requests reconstruction instead of pretending later state is complete. A terminal projection rejects subsequent transitions.

An edited draft belongs to the user. Incoming draft text must not overwrite it. The user explicitly submits a revision to the server, which creates a new proposal hash. This separates optimistic editing from authoritative execution.

// Conceptual boundary: the executable implementation is linked below.
const event = UIEventSchema.parse(untrusted);
const next = project(previous, event);

Failure and lifetime reasoning​

Closures capture a render's values. A delayed callback may still reference the previous account after the user switches. Abort obsolete requests and also verify the response belongs to the current scope before publishing it. Cancellation is advisory: a backend may already have completed work.

JavaScript tasks run to completion. A loop that reduces 100,000 messages without yielding can delay input even when all its functions are “async.” Awaiting an already-resolved Promise repeatedly can keep work in the microtask queue. Bound batches and measure long tasks; put expensive independent computation in a worker when serialization costs are justified.

Do not store both a normalized entity and several editable copies without an explicit reconciliation policy. Derive labels and totals; persist user intent and authoritative versions. Branded IDs improve accidental mixing in TypeScript, but the server still validates ownership.

Exercise and expected evidence​

Mutate an event's run ID, duplicate sequence 2, remove sequence 3, and replay after completion. Assert the precise result for each condition. Replay the same log twice and deep-compare projections, including timestamps. The reference tests prove deterministic output, gap detection, terminal protection and draft ownership. Add a schema version to an event and demonstrate deliberate rejection before planning a compatible migration. Roll back a producer only if the older consumer can still read the persisted history.

Executable reference and sources​

Implementation · Behavioral evidence · Primary source.

Run the projects and assessment. Human review is pending.