Skip to content
Back to Blog

Expressiveness is no longer a budget

Expressiveness is no longer a budget

There is a rule every frontend developer treats as physics: the more expressive an app is, the heavier it runs. More derived state, more memory. Richer models, slower creation. More capability per component, more weight per component. Nobody voted for this. It is just the water.

And because it is the water, everyone rations. Look at what passes for discipline in a mature codebase. Most of it is expressiveness being withheld:

  • The condition stays inline in the template — v-if="items.length && !loading && mode === 'edit'" — because naming it would cost a computed().
  • The model stays flat, because every derived field is another 300 bytes per instance.
  • The editor lives in a separate component from the viewer, because carrying edit capability everywhere would make the common case heavy.
  • The entity is a plain object with helper functions scattered around it, because a real model class "doesn't scale."

Each of these is taught as a best practice. Each one is really a purchase decision: a thing not expressed because expressing it had a price. So the rule was never physics. It was a price list.

The price list

In the conventional shape, every unit of expressiveness bills per instance, at creation, whether used or not:

you want to expressit costs
a derived value~300 bytes of computed(), per instance, up front
a named conditionsame — so it stays inline instead
a piece of statea ref allocated the moment setup runs, touched or not
a capability (the edit half)its whole state and derivation graph, paid by every viewer
another instancethe full re-run of the factory: every ref, every computed, again

Under that price list, the rationing is rational. If naming a condition costs allocation, you inline it. If dormant capability costs weight, you split components by capability. If instances cost creation, you avoid modeling things as instances. The architecture optimizes expression away, because expression is what gets billed.

The repealed prices

ivue is a 1.1 kB class layer over Vue's reactivity. You write plain classes. State lives behind getters and appears on first use. Derived values are ordinary getters that cost nothing to keep. That one design change rewrites the price list — and every line below carries a measurement, not a promise:

you want to expressit costsreceipt
a derived value0 bytes — a plain getter on the prototype, shared by every instanceDerivations are free
a named condition0 bytes — same getter. Templates read as prose because naming is freethe standard
a piece of statenothing until first touch — the ref appears on first readTotal memory control
a dormant capabilitynothing at all — an unread getter is a function nobody calledDerivations are free
another instancea plain object — creation runs 55–253× faster than the alternativesPerformance
the engine itself1.1 kB gzippedOne kilobyte of feature
even the leaka husk — cells released on demand, GC or no GCRelease what the GC can't

In plain words: you used to pay for everything your app could say, the moment it could say it. Now you pay only for what it actually says, at the moment it says it. The vocabulary is free. Only the speaking costs.

Weight no longer scales with expression. It scales with use.

What a repealed budget looks like

We measured a production component that is both the player and the editor of a post. One class. One model. About ninety derived getters: scale ratios, font sizes, named conditions, dialog field templates, has-changes checks.

Under the old price list this component is malpractice. Ninety computeds would cost ~27 kilobytes per instance, and many instances sit on screen at once.

In ivue it is just a well-spoken model. The ninety derivations weigh zero. A full template pass over forty of them recomputes from scratch in two to three microseconds. While the post is only playing, every editor derivation sits untouched: no allocation, no subscription, no bookkeeping. And the component can multiply. A thousand instances is ninety thousand live derivations backed by nothing, still under the floor.

That component is not an outlier. It is what any component becomes when its author stops rationing. Richer names, more refinements, the whole capability in one honest model — because there is no longer a reason not to.

The disciplines dissolve

Watch what happens to the "best practices" when the price list that created them is repealed:

  • Keep logic out of templates stops being a sacrifice. Every condition gets a name, because names cost zero.
  • Split by capability to stay light loses its premise. Dormant capability weighs nothing, so components split by meaning, not by weight.
  • Avoid modeling entities as instances inverts. Instances are plain objects with lazy state, so the object graph comes back.
  • Memoize by default was always a cache misapplied. Now it is a deliberate purchase with a measured crossover: a cache pays for itself at about two reads per change, and only when the work is real.

None of those disciplines were wrong under the old prices. They were correct responses to a bill that no longer exists. Keeping them after the repeal is not rigor. It is paying a tax that was abolished.

The rule, broken

When expressiveness has a price, architecture optimizes expression away. When it is free, architecture optimizes for meaning. The link between expressive and heavy was never physics. It was pricing. Change the prices and it breaks.

Super complex and super lean were opposite directions for as long as every name, every derivation, and every dormant capability sent an invoice. They are not opposite directions anymore. Build the app that says everything it means. The weight follows use, and use was measured.

Last updated:

Evgeny Kalashnikov
AuthorEvgeny KalashnikovLead Software Engineer@Blackline, Adhoc Studio
Share

Comments

More from the blog

Released under the MIT License.