At 9:52 on April 2, we opened a ticket for a staff-only event signup that should not surface to regular users. The ticket was short. The obvious read was a new entry point, gated by staff role, sitting alongside the existing public signup flow.
We almost built that. Then we opened the controller, found the admin link flow already sitting there, and realized the door we needed was already in the building.
What “hidden” actually meant
The request was for a signup path that staff could hand to a customer for a specific event without surfacing it in the marketing site or a normal account view. That is a discoverability requirement. Any access and validation requirements still belong in the underlying flow.
A private route still has to exist and be built. A hidden path that piggybacks on an existing private mechanism needs nothing new. The existing thing just does one more job.
The admin section already had a “link to existing site” flow. Staff could pick an event, pick an existing customer site, and connect them. The flow lived in the admin controller, had its own JavaScript module, and already passed the relevant context to the downstream signup handling. Its placement made it a sensible internal starting point, and the downstream flow still owned any customer-facing validation.
So the signup work became teaching the signup handler to accept the arrival from that flow and behave appropriately on the other side. That is a much smaller piece of work.
Three commits
The first taught the signup handler to recognize the staff-arrival shape and skip the parts of the public signup flow that don’t apply. Thirty-one lines of changes, plus a CSS rule and a JS adjustment. The diff was small because the signup machinery already handled most of the variations.
The second extended the admin controller and its JS twin with the actual linking UI: pick an event, pick how it’s getting connected, fire the redirect into the signup flow with the right context. This was the largest of the three, around 500 lines added, because it included the dialog, the field handling, and the API change to support the lookup.
The third filled in the “add a brand-new site from this flow” branch, in case staff need to create the customer site at the same moment they’re attaching an event. That meant a new admin nav module, template CSS adjustments, and a small change in the page layout layer so the new path renders inside the admin chrome instead of the customer chrome.
The work fit into an afternoon. A separate route would have introduced another entry point, another integration surface, and an ongoing synchronization decision.
Our first instinct, before we opened the controller, was to draft a new signup URL. Something like /signup/event-staff?token=X. Authenticated against a staff session, validated against a one-time token, dropped into a stripped-down signup component. We had the rough sketch in our heads already.
That sketch was wrong, and it doesn’t announce itself until you go look at what’s already there. The “new URL” plan would have created a parallel signup flow to maintain alongside the public one forever. Every future change to public signup would need to be re-applied to the staff flow or explicitly noted as not applying. The staff flow would drift from the public flow over time, because that is what parallel implementations do.
The admin link flow has the opposite property. It already exists, it already has staff there, and it already integrates with signup on the back end. The marginal cost of one more capability inside it is small, and it gets smaller every time we touch that area for another reason.
We had the wrong frame. “Build a hidden route” became “extend the route that’s already hidden by location.” Different work, smaller surface area, and no new discovery problem to solve, because the admin section’s existence had already solved it.
Looking first, with an agent in the loop
The default mode for an engineering ticket is to build the thing the ticket describes. On an older codebase, that can miss an adjacent mechanism that already does much of the job. Looking first is a small discovery step that can keep a second implementation from becoming permanent maintenance work.
We have noticed we do this better with an agent in the loop than without one. Typing “where does staff currently hand a customer a signup link” into a Claude Code session forces us to articulate the question before we write any code. The agent reads the area and surfaces the relevant files, and we see the existing flow before our fingers start drafting the new one.
The agent didn’t tell us to reuse the existing flow. We told it to look, and looking is what changed our minds. Its value is that it lowers the cost of looking enough that we do it on every ticket, including the ones where we have no suspicion there’s something to find.
Check before you reuse
Before approving a new entry point or feature, ask whether an existing mechanism already does most of the job, since a parallel flow has to be maintained alongside the original. Reuse is not automatic, though. Confirm that the existing flow has the right authorization, data boundaries, and user experience. The first step is still to look at the building before you draw.