The app was organised like filing cabinets and the people using it were running a process
The complaint was that the app felt scattered. Not slow, not buggy — scattered. That is the kind of feedback that usually gets answered with a visual refresh, and a visual refresh would have done nothing at all, because the cause was structural and it could be written down precisely.
The navigation had nine modules and every one of them was named after a data type. Client management. Site management. Financial management. Document management. Each opened onto a landing page of cards and figures, and from that page you went back down into a list to do work.
Now take the single most common job the business does: book a hoarding for a client and start billing for it. The hoarding lives in site management. The booking and the invoice live in client management. The receivable lives in financial management. One job, three modules, and the sidebar listed clients before sites even though you cannot book a hoarding you have not added yet.
Nouns do not tell you what comes next
A module called “client management” tells you what is inside it. It tells you nothing about where you are in a process or what happens after. So every transition between steps is a trip up to a menu and a hunt for the drawer the next thing is filed in.
The business is a chain and the data model says so unambiguously: sites come from owners, clients generate bookings, bookings commit sites, invoices come from bookings, payments settle invoices, rent and utilities go out against the sites, and the field work hangs off all of it. Every one of those arrows is a foreign key. The dependency order was sitting in the schema and the navigation contradicted it.
The realignment renamed the groups after stages rather than tables — inventory, sales, billing, payouts, operations — and ordered them the way the money moves. That is it. Same screens, same components, same endpoints. Reordered and renamed.
Hubs are destinations, not conductors
The second structural problem was the landing pages. Every module had one: a page of summary cards and counters that you passed through on the way to a list.
A hub feels like navigation and it is actually a stop. You arrive, read numbers you did not ask for, and then click again to reach the screen you wanted. Worse, it makes the sidebar a two-level thing where one level does no work. The fix was to delete the hubs entirely and make every sidebar item land directly on a real screen, with the group auto-expanding from the current path so the user can see where they are.
The two dashboards collapsed into one at the same time, role-aware rather than duplicated, because two dashboards with overlapping figures is the same problem in another costume — the user has to decide which one to trust before they can read either.
The same thing in two places is the tax
The finding I would carry into any other product is this one. Invoices and payments were physically filed under clients and also surfaced under the financial module. Receivables and invoices were two views of the same client debt. Task types sat in settings while tasks sat in operations.
When something appears in two places, people stop trusting where anything belongs. That is not an aesthetic problem; it is the scattered feeling itself. Every duplicate location adds a small decision to every navigation, and those decisions accumulate into a sense that the software has no shape.
Money was split in half by the same mechanism. Revenue lived under clients, costs lived under the finance module, so there was no screen anywhere that showed both sides. The person asking “how did we do on this site” had to hold half the answer in their head while they went to find the other half.
Doing it without breaking bookmarks
The mechanical part was larger than the design part. Around three hundred and forty-six internal links across eighty-six files pointed at the old paths, and about twenty prefix redirects were added so every old path still resolves. Those redirects stay permanently. They cost nothing and they keep every bookmark, every link pasted into a message, and every route that the sweep missed working.
The sweep itself is where I made the one real mistake. The first pass was a broad find-and-replace on path strings, and it hit import statements as well as links, because a module path and a route path look identical to a regular expression. That was caught by the build, reverted, and redone with the replacement anchored on quote characters and the URL context. A rename that touches both routes and imports is not a text operation. Run it, then read the diff, and if the diff is too large to read then the operation was too broad.
What it cost and what it bought
Reordering navigation is genuinely cheap. No data model changed, no endpoint changed, and the build was green at every phase. The expensive half was the follow-on: once records sit in a workflow order, the absence of forward links between them becomes glaring, and that is a real body of work.
Group your navigation by the order in which the work happens, not by which table the row lives in. The table grouping is obvious to whoever wrote the schema and invisible to everybody else, and it puts the seams of your software exactly where the user’s job crosses them.
The limit: this fixes where things live. It does not, on its own, make a record hand you to the next step. That is a separate and larger problem, and the diagnosis document said so at the time.