The Choice: Grow a Seam or Accept the Loss
The shared panel persists its own geometry — mode, width, height — in browser local storage under a key prefix the app supplies. One converting app kept those values in its own settings store instead, and had exposed them: a mode picker and two size sliders, live in its settings popover.
Two options. Grow a persistence seam in the shared component so an app can supply its own storage backend, or accept the shared persistence and delete the controls.
The decision was to accept the loss. Three settings controls were deleted, along with the state fields and types behind them. The reasoning: the other apps live without those sliders, and edge-drag resize already does the job — so the controls were solving a problem the shared component solves differently rather than a problem it fails to solve.
Why Refusing the Seam Was the Cheaper Answer
A persistence seam sounds harmless. In practice it is a permanent widening of the contract: every consumer now has a storage strategy to choose or default, the shared component has two code paths to maintain and test, and the next feature touching persistence has to work in both. That cost is paid forever, by everyone, in exchange for one app keeping three controls.
And here is the part that made the decision look right in hindsight: the same question came up in all three conversions that followed, and cost nothing in any of them. Each of those apps also kept geometry in its own state — but none had exposed it to a user. Accepting shared persistence was invisible to them.
Had the seam been added for the first app, it would have been carried by four consumers to serve one, and the three later conversions would have quietly proved it unnecessary — after it was already load-bearing.
Ask What Has Shipped, Not What Is Stored
The distinction that made the cost real in exactly one place is worth extracting. All four apps stored panel geometry. Only one had built an interface for it. The internal state was identical; the user-visible commitment was not.
When pricing a conversion, the question is not what an app holds but what it has promised. Internal state can be moved silently. A control somebody has used cannot, and that is where the actual cost lives — which means the pricing question is "what has this app shipped that a user could notice disappearing", not "what does this app store".