Changing where a new state is handled, instead of teaching eight read paths about it
A new platform-wide asset state was specified as a rule that eight different read paths had to learn. I proposed applying it once at the write boundary instead, removing the asset from route scope and restoring it on reactivation, which made most of the original scope unnecessary and left the counters, reports, app sync and adherence untouched.
impact~8 read-path rules replaced by 1 write-side rule + 2 UI guards + 1 messageWhole use case fits one slice per flow, not a multi-surface epicZero changes to reports, print, app sync or adherence calculationNo new table, no new route version, no history rewriteRefined design adopted as the epic's source of truth over the roadmap item
01
Context
This is the case I'd use to show that I can change *scope* by changing *design*, without it being a negotiation. I didn't argue the feature was too big or ask for less. I moved where one rule is applied, and most of the original scope stopped being necessary as a consequence, which is a much better conversation to have with product than "can we cut this".
It's also the clearest instance of an earlier decision of mine paying off. A month before this, I had consolidated a cross-service metric onto a single computation path fed by a recalculation queue and a worker. That decision is exactly why "the counters come for free" is a true statement here rather than a hopeful one. Architecture compounds, and this is the first time I watched my own compound.
02
Problem
A customer contract introduced a new operational status on assets in the shared asset tree: *hibernated*, meaning equipment intentionally out of service for a while, which should stop producing operational noise across the whole platform. Every product had to react: alerts, notification emails, the spot viewer, hour meters, analysis plans, spot comparison, and inspection routes.
The inspection use case, as first written, kept the hibernated asset inside its routes and made every consumer compensate:
—the *Items in Route* column in route management subtracts hibernated assets and their child items, meaning subsets, components, checklists and measurement points;
—the asset tree flags hibernated assets;
—selection of a hibernated asset is blocked;
—on the edit screen, a hibernated asset that was already linked stays linked, the planner cannot remove it, and it will not appear in the app;
—the route report shows a total with hibernated assets subtracted;
—the report's adherence indicators exclude hibernated assets from the calculation;
—the printed report does the same.
03Constraints
+
—The specification wasn't wrong, it was placed wrong. Every rule in it described real, desirable behaviour. There was nothing to reject, which is precisely why "this is too much scope" would have gone nowhere.
—The rule would have leaked across a service and team boundary. Route counters, adherence and the app payload are produced by different services from the one that owns the asset tree and its status. Read-time exclusion means the hibernation state has to be available, and correctly joined, at every one of those points.
—The number of read paths only grows. Any future surface that counts route items, a new indicator, a new export, a new screen, inherits the obligation to remember the rule. The cost isn't the eight; it's that eight is not the final number.
—Removal sounds lossy. "Just take it out of the route" is only safe if the planner doesn't lose information, the asset comes back to the right place, and history doesn't move. If any of those fails, read-time exclusion is the correct design after all.
—The state is not owned by this domain. Inspection consumes hibernation from the asset tree and must never write it, so whatever the design was, it had to be driven by an event rather than a local flag.
04
Decision
I asked what the state actually means to this domain. Hibernated doesn't mean "count this differently". For inspection routes it means *this asset is not part of the work right now*. Route membership already has a representation for that. So the question wasn't "where do we subtract?" but "why is it still in the route?"
I moved the rule to the write boundary. When the asset-tree event says an asset is hibernated, the backend removes that item and its descendants from route scope. When the asset is reactivated, it goes back to its original route, with the children that were actually in it, at its previous position where that still exists. Hibernation is applied once, where the state change arrives.
Then I checked what that makes unnecessary. All of it, on the read side. Counters and indicators are computed over the route's live items, so they are naturally correct with *no special calculation to disregard hibernated assets*. The app receives the route's items, so it never sees a hibernated one. Reports and print total what's there. The adherence calculation needs no hibernation concept. That's the whole point: not "cheaper to build", but *nothing to build*.
I kept the two things that genuinely belong on the read side, the visual flag in the asset tree and the block on selecting a hibernated asset, because those are about the planner's choice at authoring time, not about arithmetic.
And I paid the one real cost the design creates. Silent removal would confuse the planner: assets disappear from a route with no explanation. So the design adds one message on the edit screen that names each asset removed by hibernation and states that it will return automatically when reactivated. One informational surface replaces eight arithmetic ones, and the planner ends up better informed than under the original design, where the asset was present but inert.
The refined use case became the scope. Both slices now state, in writing, that the use-case pages are the source of truth and that the roadmap item "records the initial scope and does not reflect the changes agreed during refinement." The whole basic flow plus both alternative flows fit into a single slice: four stories for dynamic scope management, three for automatic restoration.
A later spike confirmed the load-bearing assumption in code, which is the part I'd most want a reviewer to check rather than take on trust. It found that the soft-delete-of-item-and-descendants mechanism already runs in production, since it is what happens when an asset is deleted from the tree, and does not create a new route version; that route counters and the active cycle's adherence are already recomputed from live items by the recalculation queue and worker, so they "come for free"; that closed cycles are immune, because recalculation touches only the active cycle and aggregate adherence reads a consolidated per-cycle value; that no new table is needed, since the route-item table already carries soft-delete, ordering and parent columns; and that checklist answers are matched by checklist and cycle rather than by route item, so they survive the round trip.
05Trade-offs
+
—Removing from scope over excluding at read time. One rule at one boundary instead of a rule at every consumer, and every future consumer correct by default. The cost accepted: the planner sees assets vanish from a route, which had to be answered with an explicit message rather than left implicit.
—Restoring automatically over asking the planner to re-add. Reactivation is not a planning decision, so making a human redo it would be busywork and would drift. Cost: the system now needs to remember position and original membership, and to decide what to do when the route has since shrunk or been reordered.
—One informational message over keeping the asset visible but inert. The original design's "linked, unremovable, invisible in the app" item would have kept the asset on screen, but it introduces a state nothing else in the system has, and it silently lies to every counter until each one is taught otherwise.
—Driving it from the asset-tree event over a local hibernation flag in the inspection domain. Inspection must not own this state. Cost: the behaviour is eventually consistent, so the design commits only to reflecting the change within an acceptable synchronization interval, not instantly.
—This shape is right for a *scope*, and I would not generalize it. Sibling squads handling the same platform state for hour meters, analysis plans and spot comparison went the read-time route, and for at least one of them that is correct: comparing a spot's data before and during hibernation *is* the product, so the hibernated asset cannot be removed from the thing being read. Removal is available to me because a route is a scope of work, reconstructible and meaningless once the work isn't happening. A history is not. The lesson isn't "always remove"; it's "check whether the thing you're filtering is a scope or a record."
06
Impact
Stated honestly: this is a design and scoping contribution, and it is not shipped. Development was scheduled to start 2026-08-31; both slices were in the backlog when this was written, with a spike in review. There is no realized before/after metric, and no hours or story-point figure exists to quote.
What *is* verifiable:
—The refined design replaced the roadmap item as the epic's source of truth. Both slices say so explicitly, and the use-case pages carry the removal-based flow, including the line that counters reflect operational assets "with no special calculation to disregard hibernated ones".
—Roughly eight read-path rules collapsed to one write-side rule, two UI guards and one message. Reports, printed reports, the app payload and the adherence calculation are untouched by the feature.
—No new route version, no new table, no history rewrite, confirmed in code by the spike, reusing a mechanism already in production.
—The whole use case fits one slice per flow, four stories for dynamic scope management and three for automatic restoration, rather than a change spread across every surface that counts route items.
—The design surfaced two real problems rather than hiding them, both now tracked ahead of implementation. First, there is currently no discriminator between "removed because hibernated", "removed by the planner", "asset deleted from the tree" and "left over from an older route version", all the same state, so automatic restoration could resurrect an item removed for a different reason. Second, a pre-existing silent data-loss path gets amplified, where an answer collected for an item that has left scope is accepted and confirmed to the inspector, then discarded asynchronously into an error log. That second one already happens today whenever an item leaves scope; hibernation multiplies its frequency, and the naive symmetric fix would convert silent loss into a failure in the inspector's hands, so both ends have to be decided together.
07Lessons Learned
+
—Ask where a new state should be applied before asking how each consumer should handle it. "Every read path subtracts X" is almost always a sign that X belongs at the write boundary. One application point beats N compensations, and it makes future consumers correct without them knowing the rule exists.
—The best way to cut scope is to make it unnecessary, not to negotiate it away. Nothing in the original specification was wrong, so arguing about size would have failed. Changing where one rule lives deleted most of it as a side effect, and that is a design conversation, not a prioritization fight.
—Check whether the thing you're filtering is a scope or a record. A scope of work can be reduced and rebuilt; a history cannot. Removal is only available on the first, which is why the same platform state deserved a different shape in a sibling product.
—Silent correctness needs an explicit explanation. Making the data right is not the whole job. If a user's route quietly changes shape, "correct" reads as "broken". One message that names what happened and promises the reversal is the price of the simpler design, and it's cheap.
—Architecture compounds, and you can feel it. "Counters and adherence come for free" is only true because of a single-computation-path decision made a month earlier. The return on that decision showed up as scope I didn't have to write.
—Design your own proposal's failure modes into the plan. The discriminator gap and the amplified answer-loss path are both consequences of my design. Naming them in the refinement, before implementation, is what separates a proposal from a pitch.
08Evidence
+
—Verified: the refined scope superseded the original. Both slices of the epic (opened 2026-07-16 and 2026-08-20) state that the use-case and design-acceptance pages are the source of truth and that the roadmap item, opened 2026-07-06 by product, "records the initial scope and does not reflect the changes agreed during refinement."
—Verified: the published use case is the removal-based design. Its basic flow opens with hibernated assets already excluded from routes by the backend and counters that reflect operational assets "with no special calculation to disregard hibernated ones"; its alternative flow is the informational message plus automatic return on reactivation. The stories decomposed from it match one-to-one.
—Verified in code by the team's spike (2026-08-21): the soft-delete-with-descendants path already runs in production without versioning the route; counters and active-cycle adherence already recompute from live items via the recalculation queue and worker; closed cycles are unaffected; no new table is required; checklist answers are keyed by checklist and cycle, not by route item. The spike also produced the two open problems listed under Impact.
—Verified as a genuinely cross-product initiative, which is what makes the placement question interesting: the same platform state has parallel reaction epics in the condition-monitoring, asset-tree and management-indicators squads, and the sibling roadmap item for hour meters, analysis plans and spot comparison specifies read-time exclusion, including "exclude hibernated machines from the adherence and completion calculation", for products where removal is not available.
—Not shipped. Development scheduled for 2026-08-31; slices in backlog, spike in review, as of 2026-08-28.
—Attribution note (VERIFY): the refinement is mine, but the shared artifacts are team-owned. The epics and stories were written by the squad's product manager and tech lead, and the use-case page's last editor in the wiki is a product colleague. If this case study is used in a hiring conversation, anchor the contribution to a concrete trail (a refinement meeting note, a comment, or a message thread) rather than to page authorship.
—Source (private): Jira roadmap item, the two use-case slices and their stories, the implementation spike, and the internal product-documentation wiki pages for the use case and its design acceptance criteria.