AWRA OpsHub Search

The Twelve Places a New Module Appears

Building a module is the visible half of adding one. The other half is declaring it in a dozen registries that decide whether anybody can find it, search it, report on it, recover from it or be told about it.

Implementation & Rollout AWRA OpsHub Team 12 min read

The screens work, the permissions are right, and nobody can find it. This is the most common way a finished feature fails.

A module in a system this size is not one thing. It is a set of screens plus a series of appearances in shared surfaces that were built long before it existed, and each of those appearances is a declaration somewhere rather than something that happens automatically.

Where a module has to be declared

Registry What forgetting it costs
Quick actions The module has no shortcut in the topbar and no action bar on its own hub.
The command palette It cannot be reached by keyboard, and the palette stops being a complete index.
The settings navigation Its configuration exists and is unreachable from the settings index.
Settings search The configuration is reachable and unfindable, which is worse.
The help centre categories Documentation about it lands in the wrong section, or first, by accident.
The report catalogue The module produces nothing anybody can run a cross-module report against.
The recovery registry Deleted records keep their rows and nobody can restore one.
The custom field registry An organisation cannot add its own fields to the module.
The notification catalogue Events happen and nobody is told, or the preference to be told does not exist.
Attachable modules Files cannot be attached to its records.
The public interface catalogue The data exists and no integration can read it.
List search and status presentation Its lists cannot be searched consistently and its statuses render as raw strings.

None of those failures produce an error. Every one of them produces a module that works and is incomplete in a way that is only discovered by somebody trying to do the thing it does not do.

This is why they are registries rather than templates

Each of these surfaces could have been a template with a list of links in it. The reason they are not is that a template is edited by whoever remembers it exists. Declaring a module once, in a file whose whole purpose is that declaration, means the topbar, the hub bar and the palette all render from the same statement — and the failure mode becomes "we forgot to declare it", which is one omission rather than several inconsistent ones.

Every entry carries its own permission

The shared rule across these registries is that an entry declares the permission its own route already enforces, and the two must agree. That is a maintenance burden and it is the correct one, because the alternatives are both bad in visible ways.

  1. A shortcut looser than its route

    Hands the user a refusal. They see a button, press it, and are told they may not — which reads as a broken product rather than as a permission boundary.

  2. A shortcut tighter than its route

    Hides a page they can actually open. Less annoying and more insidious: the capability exists, they were told about it in training, and it is not there.

  3. No permission at all

    What the command palette was before it became a registry — a flat block of links where somebody without accounting still saw journal entries and bounced off them.

A link to a page you may not open is a worse experience than no link.

Two decisions that make these safe to extend

The first is that a missing route drops an entry rather than raising an error. A half-deployed module — declared in a registry, with its routes not yet present — quietly loses its shortcut instead of taking down the topbar of every page in the product.

That matters during a deployment more than at any other time, and it is the difference between a partial release being invisible and a partial release being an outage. It is a small piece of defensiveness in exactly the place defensiveness pays.

The second is the separation between doing and going. Actions live in one registry and destinations in another, and the palette draws from both. That is not tidiness for its own sake: an action needs a permission matching a write route and often has no address of its own, while a destination is a page. Keeping them apart means each registry has one shape rather than a shape with an exception in it.

How to use this if you are extending the product

You have just built a module

Walk the list before calling it done

Each registry is a small declaration and the whole set is an afternoon. Discovering them one complaint at a time takes months.

Users say they cannot find something

Check the palette and settings search first

Reachable-but-unfindable is the most common version of this failure, and it looks like a training problem until somebody checks.

A permission was changed on a route

Change it in the registry too

The two are declared separately and must agree. A route tightened without its shortcut produces buttons that refuse.

Something appears for the wrong people

Compare the entry against the route, not against intent

The registry entry is a claim about what the route enforces. When they disagree, the route is the truth and the entry is the bug.

Five questions to ask about extending a system

Where is a shortcut declared?

A good answer sounds like

One place.

What ours actually is

One registry, rendering the topbar menu, the hub bars and the keyboard palette from a single list.

How do permissions on shortcuts stay correct?

A good answer sounds like

A declaration, kept in step.

What ours actually is

Each entry declares the permission its route enforces, and the two must agree — a looser entry produces a refusal, a tighter one hides a working page.

What happens during a partial deployment?

A good answer sounds like

Graceful degradation.

What ours actually is

An entry whose route is not present is dropped rather than raising, so the topbar cannot be broken by a half-deployed module.

How many places does a new module need declaring?

A good answer sounds like

A known list.

What ours actually is

About a dozen, covering discovery, search, settings, reporting, recovery, custom fields, notifications, attachments and the public interface.

Can a module be found by typing as well as by clicking?

A good answer sounds like

Both, from one declaration.

What ours actually is

Settings search and the keyboard palette read the registries directly, so a module declared once is reachable either way without a second list to maintain.

What is declared, and what would make the walk verifiable

The registry stack, layer by layer

Shortcut actions

One declaration point rendering the topbar Quick menu, the module hub bars and the keyboard palette from a single list.

Built in

Destinations

A separate registry mirroring the sidebar, so the palette reads as an index of the product rather than a pile of links.

Built in

A permission on every entry

Each entry declares the gate its own route enforces, with support for requiring all of a set or any of a set.

Built in

Partial-deployment safety

An entry whose route is absent is dropped rather than raising, so a half-shipped module cannot break the topbar on every page.

Built in

Actions without an address

Entries that launch behaviour rather than navigate, carrying their own attributes and no URL of their own.

Built in

The other ten registries

Settings navigation and search, help centre sections, the report catalogue, the recovery registry, custom fields, notifications, attachable modules and the public interface catalogue.

Built in

Six layers, each a small declaration, and together they are what makes a module findable, searchable, reportable and recoverable rather than merely present.

Three positions held on purpose

  • Permissions stay declared on registry entries rather than inferred from the route at render time. Reading middleware to decide what to draw couples the interface to route internals, and it is slower on every page render than a declaration that has to be kept in step.
  • An entry pointing at a route that has yet to ship is dropped rather than raising. During a partial deployment the alternative is a global failure of the topbar, and a quietly dropped shortcut is a far better outcome than an unusable product.
  • How your organisation wants a module to appear — which actions belong on a hub bar, which belong in the topbar — is configuration of the product rather than a decision the module makes for itself, which is why each entry states it explicitly rather than deriving it.

Our take

The interesting thing about a mature product is how much of adding a feature is declaration rather than construction. A dozen registries decide whether a module can be found, searched, reported on, recovered from, extended with custom fields, attached to, notified about and read by an integration — and none of them fail loudly when a module is absent from them. That is the right architecture, because the alternative is the same list of links copied into a dozen templates and drifting. And because every one of those registries is already data rather than markup, verifying that the walk was completed is a query rather than a design problem.

Ask what a module has to be declared in

It is a good question to put to anybody proposing to extend a system you depend on, because the answer reveals whether the extension will be a real part of the product or a screen somebody has to be told about.

Talk through extending the product

Frequently asked questions

Why is a shortcut declared separately from the route?

Because the alternative — reading a route's middleware at render time to decide whether to draw a button — couples the interface to route internals and does that work on every page load. A declaration is faster and clearer, and the cost is that it has to be kept in step with the route it describes. When the two disagree, the route is the truth and the entry is the bug.

What actually breaks if a module is left out of a registry?

Nothing breaks, which is the problem. The module works, its screens are correct, and it simply cannot be found in the palette, or searched in settings, or reported on, or recovered from. Every one of those is discovered by somebody trying to do the thing that is absent, usually weeks later.

Why are actions and destinations in different registries?

Because they have different shapes. An action often has no address of its own — it opens a dialogue — and its permission matches a write route. A destination is a page. Keeping them apart means each registry has one consistent form rather than one form with an exception in it, and the palette draws from both.

What happens if a registry names a route that does not exist?

The entry is dropped rather than raising an error. That is deliberate and it matters most during a deployment: a module declared but not yet routed loses its shortcut quietly, where the alternative would be a failure in the topbar of every page in the product.

Is there a way to check a new module was declared everywhere?

Not today, and it is the most useful thing that could be added. The registries are already data, so a report saying which of them have never heard of a given module is a query rather than a design problem — and it turns a checklist somebody has to remember into something that answers for itself.

Users say a screen is missing. Where do I look first?

The palette and settings search. Reachable-but-unfindable is by far the most common version of this, and it presents as a training problem right up until somebody checks whether the module was ever declared in the search registry. The second place to look is the permission on the entry, which may be tighter than the route it points at.

Help Center

Need a quick answer while you read?

Run inventory, procurement, assets, sales, and field work with approved AWRA guidance for setup, migration, integrations, security, pricing, and support.

Search all approved AWRA public help articles.

Open Help Center