skip to content

What does deep-freezing every object reachable from a published price list cost as that graph grows?

level: seniorimportance: should knowfreq 38%

answer

  1. proportional to reachable objects
  2. paid on every publication
  3. the edit's size is irrelevant
  4. some objects refuse to become unwritable
  5. establish it once at the entry point

basics

~20 s

It costs a full walk of the reachable graph on every publication: work proportional to the number of reachable objects, not to the size of the edit. Parts nothing touched are re-walked, and some objects cannot be made unwritable from outside at all.

solid answer

~40 s

Deep-freezing at publication time is a traversal, so its cost is **proportional to n, the number of objects reachable from the value** — the container, every tier row, every object a row reaches — and is entirely independent of how large the change was. Publish a price list where one amount moved and you still pay for all n. Two further costs hide behind that. Some objects simply offer no way to become unwritable, so freezing them degrades into producing a substitute you do control. And a freeze applied at publication is too late for anyone who already obtained a reference on the way in. The alternative is to move the boundary: admit data in an unwritable form at the entry point, so the interior never needs a later pass.

code

pseudocode · 7 lines
pseudocode
// entry point: convert once, on the way in
function admit(incomingRows)
    return fixedSequence(map(incomingRows, r -> fixedRow(r.threshold, r.amount)))

// publication: no traversal, nothing left to prove
function publish(fixedRows)
    return { tiers: fixedRows }

go deeper

for a junior

Grasp that making a whole graph unwritable means visiting every object in it. The work grows with how much the value contains, not with how much of it you changed.

for a middle

Quantify it: work proportional to the number of reachable objects, per publication, independent of edit size. Add that a per-node wrapper also costs allocation now and indirection on every later read.

for a senior

Bring the failure modes a running system shows: parts that cannot be frozen because you did not define them, and references that escaped before the freeze ran. Then price the entry-point alternative honestly.

for a principal

Decide where the system's immutability boundary sits and hold teams to it, since a rule of freeze it before you publish quietly becomes a per-publication traversal over data nobody owns.

Deep immutability is a property of a value's reachable closure, and there are two moments at which you can establish it: when data enters, or when the value is published. This question is about what the second one costs. ## The shape of the cost Freezing the closure is a graph traversal. Let **n** be the number of objects reachable from the published price list — the root, the container of tier rows, each row, and anything a row reaches in turn. The walk visits each of them, so the work is proportional to n. The important part is what n is *not* proportional to. It is not the number of fields that changed. It is not the number of rows edited. A publication in which a single amount moved by one unit costs the same walk as a publication that rebuilt every tier, because a traversal cannot know which parts changed without visiting them. If the value is republished on a schedule, that cost recurs at the schedule's rate, mostly over parts that were identical last time. ## The costs behind the walk - **Repeated work over unchanged structure.** Most of what the walk touches is the same as at the last publication, and it is re-walked anyway. - **Allocation, where freezing means wrapping.** If unwritability is achieved by producing a substitute object per node rather than by flipping something on the node itself, the walk allocates in proportion to n as well as visiting in proportion to n. - **Indirection at read time.** A per-node wrapper adds a hop on every subsequent read of that node, which is paid by readers forever rather than once at publication. - **A window before the freeze.** The value exists in a writable state between construction and the freeze, and anyone who obtained a reference during that window is unaffected by the later freeze. ## Where the owner cannot freeze at all Unwritability is a property an object has to be able to support. If a part of the graph arrived from somewhere else and offers no way to become unwritable, the owner cannot impose it from outside; the only route left is to build a substitute out of parts the owner controls and use that. At that point the deep freeze has quietly become a rebuild, and pricing it as a cheap sweep at the end is wrong. This is the practical reason whole-graph freezing tends not to survive contact with real data: the graph is rarely made entirely of objects you defined. ## Moving the boundary instead The alternative is to fix **where** the guarantee is established. Admit incoming data at a single entry point, converting it there into structures that cannot be written, and build everything inside out of those. The interior is then unwritable by construction and never needs a later pass. ``` // entry point: pay once, on the way in function admit(incomingRows) return fixedSequence(map(incomingRows, r -> fixedRow(r.threshold, r.amount))) // publication: no traversal, nothing to prove function publish(fixedRows) return { tiers: fixedRows } ``` The conversion at the entry point costs work proportional to k, the size of the data arriving. That is not free, and it is not nothing — but it is paid once per arrival instead of once per publication, and it closes the window, because there is never an interval during which the interior is writable. ## Choosing between them | | Freeze the graph at publication | Unwritable at the entry point | |---|---|---| | Cost paid when | On every publication | Once, when data arrives | | Cost proportional to | n reachable objects | k incoming items | | Unchanged parts | Re-walked every time | Never revisited | | Objects you do not control | May be unfreezable | Converted on arrival | | Writable window | Exists until the freeze runs | None | Neither column is universally right. A small graph published rarely does not justify reshaping how data enters the system, and a freeze at the end is a reasonable local move there. A large graph, published often, built from parts that arrived from several places, is where the per-publication walk becomes the wrong place to pay — and where it is also least likely to succeed, because that is exactly the graph containing objects the owner cannot make unwritable. ## What a strong answer sounds like State the cost as a quantity and say what n is. Note that it is independent of the edit's size. Then add the two things that make it worse than the traversal alone — the parts that cannot be frozen from outside, and the window before the freeze runs — and name moving the boundary as the alternative, with its own honest cost per arrival rather than as a free win.

  • If only one tier row changed, why does the walk still cost the whole graph?
    Because a traversal has no record of what changed. Distinguishing touched from untouched nodes needs something extra — a version marker, a dirty flag, a structure that produces a new value rather than editing one — and once you have that, you are no longer freezing at the end. Without it, finding the one changed row means visiting all n.
  • What does the window between construction and the freeze actually expose?
    Any reference obtained during it. Objects handed to the builder, passed to a callback, or registered in an index before the freeze runs are held by holders whose references keep working; making the objects unwritable afterwards affects what those holders may do next, but nothing they already did. Establishing the property on arrival removes the window entirely.

saying these in an interview costs you the question

  • Assuming the freeze cost tracks the size of the edit
  • Believing any object can be made unwritable from outside
  • Treating a whole-graph freeze at publication time as free
  • Dismissing conversion at the entry point as more expensive than repeated walks
  • Freezing after publication and assuming earlier holders are covered