Skip to main content
CFDLAcademy
All chapters

Part 3 · Modeling judgment · Chapter 18 of 28

Choosing the construct

Part II gave you eight ways to say things. Every working session now begins with the same silent decision: which one is this claim? Most modeling reviews that go badly go badly here — not on wrong numbers but on wrong constructs, economics that are technically computed and structurally illegible. This chapter makes the decision explicit: one sorting question, calibration through pairs, and the smells that tell you a claim has been poured into the wrong shape.

One question: what kind of claim is it?

Ask what the sentence you would say aloud is about, and the construct follows:

  • "Cash moves on this pattern." → a stream. The atom; when nothing below applies, this is the answer.
  • "This quantity persists and evolves." → a field — a fact or rule on the entity that owns it. Not cash: memory.
  • "Cash accumulates here until the rules move it." → an account. Not a quantity about the deal but a location in it; a field remembers, an account holds.
  • "At some point, the regime changes — once." → an event. A turning point, and it happens once because the world happens once: a singular schedule, or a topology with no way back.
  • "The regime changes and can change back." → a lifecycle. Declared states and guarded edges — the breach that cures is topology walked twice, and edge availability is the memory.
  • "Someone holds a right they may use." → an option. The right's terms, apart from the prediction of its use.
  • "A pot is divided by priority." → a waterfall. Contested cash, document order.
  • "This is a standard deal in a domain." → a contract (next chapter): a pack's named economics, stated by its terms.

The question is about the claim, never the computation — every construct here can be imitated with streams and arithmetic, which is precisely why the framework matters. The constructs exist to make the model say what kind of thing each claim is; choosing by computational convenience throws that information away.

One tier sits apart from all of these, because its claims are not about cash at all but about reading it:

  • "The deal solved for this figure." → a metric — one number, evaluated at the horizon over the finished projection, published beside the engine's own.
  • "Look at just this part." → a slice — a named partial selection, deliberately partial and shown as partial.
  • "Present the results this way." → a statement — the rows a reader meets, generated from a hierarchy or authored line by line.

None of the three changes a value, and none changes what the model is in a specific, checkable sense: a slice and a statement are views, excluded from the model's hash, so a colleague who adds one still shares your model identity. A metric does move the hash — a figure the model claims belongs to the model — but it still produces no cash. The sorting instinct carries over unchanged: if the sentence is about what the deal does, it lives in the tiers above; if it is about what the reader sees, it lives here, and nothing here can quietly alter a number.

Calibration pairs

Frameworks calibrate on hard cases. Three pairs, each the same economics twice.

A declining balance: field, or stream arithmetic? A loan balance could be an amount expression — 250000 - 4166.67 * time.t — and for a strictly linear paydown the numbers even agree. But the expression version states "cash follows this formula," when the claim is "a quantity persists and moves by rule." The moment reality complicates — a sweep, a rate change, an event writing the balance — you must re-derive the formula version, while the field's next absorbs the change in place.

The tell that you chose wrong is an amount expression re-deriving history. Anything shaped like initial - payment * time.t is a recurrence flattened into a formula, and it breaks silently the first time any single period deviates.

A regime switch: event, or if-ladder? Chapter 10's refinance could be written as if(time.t < 12, 9500, 6200) in one stream — same cash. The ladder fails three ways. It buries the deal's most consequential moment inside arithmetic, where no reviewer will find it. It states no reason: the 12 is a naked number, not a condition. And it cannot coordinate — when the switch also affects fees, covenants, and the exit, each expression carries its own copy of time.t < 12, and they drift. The event states the turning point once, with its trigger; guards subscribe. The tell: the same condition appearing in two expressions — a regime wearing an if-costume.

An entitlement: waterfall, or field-plus-paydown? A deferred fee that accrues when cash is short and pays when cash allows can be built by hand: a field for the balance, a stream that pays min(balance, available), careful ordering. Chapter 8 called this the smell it is.

The waterfall's owed.x - paid.x states the entitlement inside the priority structure that governs it. Who ranks where is visible, the pot cannot be double-spent, and the shortfall arithmetic is the construct's own guarantee rather than your reimplementation of it.

The tell: a field paired with a stream that tries to empty it, next to other claims on the same cash. One claimant with no competition can stay a field. The moment cash is contested, priority is the claim, and priority is spelled waterfall.

The pairs share a moral worth stating once. The wrong construct is rarely wrong today. It is wrong at the first amendment, when you must re-derive the imitation while the right construct absorbs the change.

The smells, collected

For review checklists — yours and your team's:

  1. A naked number where a reason belongs. time.t < 12 gating economics: a date claim that should be a phase, or a turning point that should be an event.
  2. The same condition twice. A regime spread across expressions instead of declared once.
  3. History re-derived in an amount. base - rate * time.t, pow on something that rounds or resets: a recurrence flattened into a formula.
  4. A field with a bailiff. Balance field + paydown stream + implicit ordering against other claims: a waterfall built by hand.
  5. Mirror streams. Two streams that must always negate each other — one transfer written as two claims; usually one stream (or one waterfall step) attached to the right entities.
  6. A cumulative window doing an account's job. series_sum("x.*", 0, time.t) as a waterfall's pot, or a field accumulating cash a step then "pays down" — the accumulated-cash claim has its own construct, with a journaled balance the hand-rolled sum never gets.
  7. A pair of events fighting over one field. A set-and-unset pair, or a condition and its negation on the same status — that is a regime that returns, written without the construct that constrains it. It is one lifecycle with two edges, where the edges that do not exist are the deal terms you are relying on.
  8. An expression that stops reading aloud. The chapter-5 limit, which is really this chapter's question arriving early: three claims in a trench coat want three constructs.

None of these is a compile error, and that is the point of Part III: the compiler guarantees the model computes what it says; only judgment guarantees it says what the deal means.

What can go wrong

Construct maximalism. The framework cuts both ways. A one-off consulting invoice does not need an event and a lifecycle field. It is a stream with a date, and dressing it up is smell 6 inverted. The sorting question includes a null answer; most claims are streams.

Refactoring without an invariant. Moving a claim between constructs must not move cash. The method from the exercises you have already done: run before, refactor, run after, compare to the penny. A construct refactor with a changed total is two changes pretending to be one.

Reviewing the shape and skipping the numbers. A well-formed model can still claim the wrong rent. Construct choice is half of review; chapter 3's descent is the other half, and neither substitutes.

Exercises

Exercise

Where the entitlement lives

The management fee starves as collections shrink — 60, 50, 40, 30 against 30 of senior debt and 15 of fee.

  1. Work the quarters by hand first. Find the quarter where the fee first falls short. Then decide whether the deferred step catches anything that quarter — the deferral only receives what survives the steps above it.
  2. Add the missing deferred-fee step with owed.mgmt_fee - paid.mgmt_fee, ranked just after the fee itself.
  3. Run. Read the owed-versus-paid columns against your arithmetic.

The construct lesson: a balance field and a paydown stream could build the same thing — smell four from the chapter. Inside the waterfall, the entitlement sits in the priority structure that governs it, and the pot arithmetic is the engine's guarantee instead of yours.

Loading exercise…

Then, on your own:

  1. Return to the chapter-10 exercise and write its if-ladder twin: one stream, if(time.t < 12, 9500, 6200), same totals. Now add a second consequence of the refinance — a 1% exit fee on the bridge, one time, at the switch. Implement it in both versions and notice how many places each required you to restate the trigger.
  2. Audit any Part II model of your own against the eight smells. The most instructive finding is usually smell 3 — a formula you wrote fluently that is quietly a recurrence.