Skip to content

Removing a Status Guard, Keeping One Hard Rule

Adityo Guni Waluyo

Dropping a forward-only guard on a booking state machine, and why one hard rule beats a total ban for real accountability.

TL;DR

The author replaced a rigid forward-only state machine guard with one hard rule: rejecting a ticket requires a written note and notifies the applicant. The guard blocked legitimate fixes and pushed admins into duplicate-ticket workarounds. Removing it shifted safety from blind prevention to auditable accountability.

An admin of a facility-booking system at KotaPortal was staring at a screen, trying to move a booking ticket backwards. A data-entry mistake on the form needed fixing, and the ticket status had to return to an earlier stage first. The state machine refused. It only allowed forward moves. An error message appeared, blocking an action that should have been simple.

My initial guess was that this guard existed to protect data integrity, so it had to stay. Forward-only logic felt like the last line of defense against tampering. I treated the rigidity as a fair price for safety: as long as movement was restricted, I believed I had closed the door on privilege abuse.

The Illusion of Safety in a Rigid State Machine

That assumption turned out to be wrong. A strict state machine only models the ideal process. Real operations are full of corrections: backdated decisions, documents that arrive late and get re-filed, tickets that need rework because of a typo. The guard I built blocked legitimate work. Instead of protecting anything, it pushed admins toward ugly workarounds, like creating a duplicate ticket just to "cancel" the old one, and the database filled up with junk entries.

The fail-safe defaults principle says the default condition is lack of access, and protection identifies the conditions under which access is allowed [1]. On the same page, exception paths still exist: a documented break-glass procedure that raises an alert, generates an audit record ("ideally a louder one"), and gets reviewed after the fact [1][4]. Break-glass procedures in clinical systems work exactly that way; administrators document every emergency use, and the standard controls are designed to minimize how often that path is needed [2].

When I removed the status guard from the service layer, the system did not descend into chaos. The six-status flow stayed as the recommended default path, but I stopped enforcing it with hard blocks. The order from the client's SOP became a signpost, not a fence. Admins can set any status at any time through the same interface.

The technical detail matters. The admin contract did not change at all: same endpoints, same payload, same status column. Only the location of the decision moved, from a guard that blocked up front to validation and side effects at the end. Three note columns had existed in the schema for a long time, one for the final decision and two for processing notes, so there was no database migration. The work came down to three things: remove the guard, add required-note validation, make sure the email goes out, then update the transition tests that used to demand forward-only order.

One Hard Rule Replaces a Total Ban

Of all possible transitions, exactly one hard rule survived without compromise: moving a ticket to the "rejected" status. That transition requires a written decision_note and automatically sends a notification email to the applicant. This shape matches the HL7 EHR-S functional standard: the system must capture the metadata of extraordinary actions, who, what, when, where, why, and it explicitly SHALL capture the rationale behind them [3]. The written reason is what replaces the ban.

There is a side effect I came to treat as a feature: because the SOP flow remains the default path in the interface, an admin with no special reason keeps moving forward as usual. The fenced path did not disappear; it just stopped being a guard and became a guide. Exactly the message from [2]: standard controls exist so the escape hatch is rarely used, not so the escape hatch does not exist.

A guard that keeps fighting the operators' real workflow eventually gets routed around. An override that requires a written rationale is far more honest than a fence everyone climbs. Safety did not vanish; it shifted from blind prevention to measurable accountability.

The final decision: every rejection carries a written reason, and the applicant always gets notified. One rule carries the whole safety load. Every deviation from the normal flow leaves a trail that can be audited, without sacrificing the smoothness of daily operations at KotaPortal.

Sources

Related articles