An admin with one event hit the bracket manager and got a picker with exactly one option in it. An admin with no events got an empty bracket manager and a banner telling them so. Both of those pages ran the full bracket-structure query first, pulled competitions and folders and brackets out of the database, and then threw all of it away because there was nothing selected to show. The page was doing expensive work to render a dead end.

“Open the bracket manager” sounds like one screen. It isn’t. Depending on how many events the org owns and whether they’ve already picked one, hitting that page should be a redirect, an auto-select, or a build-it-empty prompt. All three can be decided before a single database query for bracket data runs.

I added a feature to a multi-tenant SaaS I run that lets an org admin drill into a child event and edit its brackets. The org is the parent account; each tournament event hangs off it as a child. The bracket manager operates on whichever event you’ve selected, carried in a ?div=<event> query param. The moment I shipped that, “go to the bracket manager” stopped having an obvious meaning. The page now has a prerequisite: it needs a selected event before it can do anything useful. And there are three different ways “which event” can resolve.

The mistake I see (and made in earlier versions) is to load the page first and sort out the selection later. Render the chrome, kick off the expensive structure load, then discover there’s no event to show and bolt a picker on top. That gets you a page that pulls bracket data it’s about to throw away, and an empty state that’s an afterthought instead of a designed path. The better shape is to resolve the selection on the way in, branch explicitly on the outcome, and only then decide whether expensive work is even warranted.

The decision tree

Here’s what an org admin gets when they hit the bracket manager with no ?div= in the URL. The page walks the org’s child nodes, skips the shared team-pool node and anything that isn’t a real event, and builds a list. Then it branches on the count:

  • Zero events. There is nothing to manage. Redirect to the event-creation screen. Don’t render an empty bracket manager with a “you have no events” banner; send them to the page that actually moves them forward.
  • One event. There’s no real choice to make, so don’t make them make it. Auto-select it by redirecting with &div=<the one event> appended. The admin lands directly in the thing they came for.
  • Two or more, none chosen. Now a choice exists and the user has to make it. Enter “select event mode”: render a picker and skip the expensive bracket-structure load. There’s no selected event, so there’s nothing to fetch.
  • An event already selected (the URL has ?div=, or they got here via auto-select). Normal load. Do the real work.

None, one, many, plus the already-resolved case. Each gets its own branch, and the many case is the only one that genuinely needs the user, so it’s the only one that renders a picker. Conflating any two of these gets you something worse: a picker with a single option is a speed bump, not a choice, and a picker over zero events is a picker over nothing.

Resolve before you fetch

The performance payoff lives in the “many, none chosen” branch. The normal page load calls an init routine that hits the database to pull the bracket structure: competitions, folders, brackets, the works. In select-event mode there’s no selected event, so that query would return nothing useful. Instead of calling the init routine and getting an empty result, the code hand-builds a minimal empty structure literal in JavaScript: empty maps for competitions, event folders, and brackets, an empty root list, zero teams. The rest of the page renders against that placeholder, the picker shows, and the database is never touched for bracket data.

That’s the part I’d underline. The cheap win isn’t caching the query or making it faster. It’s noticing that when no event is selected, the query has no reason to run at all, and structuring the page so the expensive call sits behind the selection rather than in front of it. “No event selected” and “nothing to fetch” are the same fact; the code should treat them as the same fact.

This is a general property of any page with a selection prerequisite. The expensive work is almost always downstream of the selection. If you load the page first and resolve the selection second, you’ve inverted the dependency and you’ll either fetch data you discard or special-case your way back out of it. Resolve first. Then the expensive branch is the only branch that fetches, and it only runs when there’s actually something to fetch.

How the selection actually resolves

The thing the page is keying off is whether the account it’s operating on differs from the account in the URL. The logged-in org is one global; the event whose data you’re editing is another. When you select a child event, a helper swaps the operating account to point at the child while the URL account stays the parent. So “is an event selected?” reduces to “does the operating account differ from the logged-in account?” If they’re equal, nothing’s selected and you’re in one of the none/one/many branches. If they differ, an event is selected and you do the normal load.

This equality check is downstream of a design choice I wouldn’t repeat: a mutable global that a hundred call sites read, swapped mid-request based on a query param. That swap is what made parent != child possible, and it caused a string of save bugs where writes targeted the parent account instead of the selected event, because so much code assumed the two were always the same object. That’s a separate post. But it’s relevant here because the selection-resolution logic is the one place that’s supposed to be deliberate about the difference. Everywhere else in the app that distinction is a landmine; on the way into this page it’s the explicit thing the routing branches on. If you’re going to have an ambient “current context,” at least make the page entrypoint the spot where you nail down what it is.

The empty-state edge nobody hits until they do

One more branch worth stealing, because it only shows up in production. The event switcher normally renders inside a filter bar. But the filter bar only renders if the selected event has folders. So if an admin selects an event that has no folders yet, the filter bar doesn’t render, which means the switcher that lives inside it doesn’t render either, which means they’re stuck with no way to switch back out. Brand-new event, no folders, no escape hatch.

The fix is a defensive check: when the page goes to show the bracket manager, it looks for the filter-bar row, and if that row is missing it prepends the switcher on its own. The switcher is no longer dependent on the filter bar existing. This is the unglamorous half of selection routing: the happy paths are easy to reason about, and then there’s the empty-folder, freshly-created, first-time-through state where a component you assumed was always present isn’t. Build the entrypoint so the navigation control survives the empty case. Otherwise your users will find the one path where they can get in but not out.