Reactive systems become expensive when every mutation forces the runtime to inspect the whole graph. The [.me](https://www.npmjs.com/package/this.me) kernel avoids that pattern by treating derivations as indexed dependencies rather than as global recomputation jobs.
In [.me](https://www.npmjs.com/package/this.me), a derived value is not just a stored result. It is registered as a MEDerivationRecord with an expression, an evaluation scope, and a list of resolved reference paths. The important structure is the reverse index: refSubscribers. Instead of asking “what depends on the whole graph?”, the kernel asks “what depends on this exact path?”
For example, a derived assignment such as:
me.order.price(100);
me.order.quantity(5);
me.order["="]("total", "price * quantity");
causes the derivation engine to extract price and quantity, resolve them relative to order, and register order.total as a subscriber of those reference paths. This happens in registerDerivation(): each resolved ref gets a Set of target derivations.
On write, the runtime calls invalidateFromPath(). The changed path gets a new ref version, then the kernel looks up only refSubscribers[changedPath]. In eager mode, those targets are recomputed immediately and pushed into the queue, allowing chained derivations to propagate one edge at a time. In lazy mode, the write only bumps versions; readPath() and explain() call ensureTargetFresh() when a stale derived target is read.
The practical complexity is therefore O(k), where k is the affected dependency frontier, not N, the total number of paths in the graph. This distinction matters. In the City_Scale demo, N = 10,000 districts exist, but mutating districts[5001].currentLoad recomputes only:
districts.5001.loadPercent
districts.5001.overCapacity
districts.5001.needsRedirection
A fresh run of that demo reported explain().k = 3, with a mutation time of 0.295ms; the later predicate filter over the collection took 60.88ms. That is the core design boundary: mutation propagation stays local, while broad queries still pay for broad selection.
This architecture is not a claim that every operation is constant time. If many derivations subscribe to the same root, then k grows. The Root_Fanout_100k demo exists as the counterexample: one shared source with 100,000 dependents makes the dependency frontier itself large. The optimization is not magic; it is locality.
The same model also works with secret scopes. Secret branches are stored through encryptedBranches, outside the public semantic index. secretEpoch invalidates secret-related caches when the security topology changes, and explain() masks stealth inputs as origin: "stealth" instead of exposing values. In other words, derivation tracking can still operate internally while the public structure remains silent.
A useful implementation pattern emerges:
- Store derivations separately from plain values.
- Resolve expression inputs into canonical paths at registration time.
- Maintain a reverse index from input path to dependent targets.
- On mutation, walk the reverse index instead of scanning the graph.
- Support eager recomputation for write-time freshness and lazy recomputation for read-time freshness.
- Expose recompute waves through debugging tools, but treat observability as a real cost.
That is the optimization implemented in .me: the runtime does not make large graphs cheap by ignoring consistency. It keeps consistency local by making dependencies addressable in reverse.

Top comments (0)