Let's talk
engineering

An endpoint nobody can reach is not a feature, and a search box that does not search is worse

The product is a field-force platform. Sales reps visit customers, and the phone in their hand is the point of the whole thing. The scope document said orders could be created offline from the mobile app.

Some way into the build, a review of what actually existed found that the mobile app had no order-creation flow at all. Not a partial one. None. A rep could look at orders and could not take one.

The order module was complete on the API. It had a service, a router, permissions, a numbering series, price resolution, stock decrement on confirmation and forty tests. It had been marked done and nothing about that was untrue on the layer it referred to.

That is the failure this article is about. A module marked done on one layer is not a feature, and the gap does not announce itself, because every layer reports honestly about itself.

Why the rule has to be written down

The standards for this project define done as: API, plus web portal, plus mobile, plus permissions, plus tests, plus translations. Six things, and a module is not done until all six exist.

That looks like process for its own sake until you count the things that shipped otherwise. The CRM pipeline shipped as a complete API with zero clients — the web interface arrived in a later round and the mobile one in another. Administration screens for roles and settings had working routes and no link in the navigation, which made them not merely undiscoverable but untestable, because nobody could reach them by clicking.

Each of those is the same shape. Work was done, it was real, and it was invisible to the person the product is for.

The reason the rule needs to be explicit is that the natural unit of work is a layer. One person spends a day on API endpoints. That is a coherent day. Marking it done is accurate about the day and wrong about the feature, and only a definition that spans layers catches the difference.

Completion is a property of a user-visible capability, not of a layer. If the sentence describing what is finished does not have a person in it, it is not a completion report.

The tracker will not tell you

None of these were found by reading a status document. The order-creation gap was found by driving the actual application. So was a separate finding in the same review: the seeded tenant had no price list, so every attempt to create an order was rejected — invisible to every automated check, because the checks created their own data.

At one point overall completion had to be re-derived from the code, because the tracker had drifted far enough that it could not be used to plan against. The re-derived figure was roughly half the scored points, against a tracker that had been suggesting something more comfortable.

The practice that came out of it: completion is measured by using the product, and the measurement is redone rather than accumulated. A tracker records the last thing somebody said. Driving the application records what is true.

The other half: never ship the appearance of a capability

Once you accept that features cross layers, a second problem appears immediately. The client work often runs ahead of the server, and there is constant pressure to produce something that looks complete.

Several list screens in the web portal had search boxes wired to nothing. The API had no search on those resources, so the box accepted text and did nothing with it. That is worse than no search box. An absent control tells the user the capability does not exist. A control that does nothing tells them it exists and is broken, and they will use it, and they will trust results that were never filtered.

Those boxes were removed rather than left in place, and the missing server capability was written up instead. In the same sweep, roughly fifteen server-side gaps were catalogued as required API work — missing date-range parameters, missing status filters, a detail route that did not exist. Catalogued and reported, not worked around in the client.

The most disciplined instance came when building record detail screens. An order’s status timeline needs a history endpoint; the table and the schema existed and the endpoint did not, so the timeline was not built and the missing endpoint was reported. A panel showing payments against an order could not be built either, because receipts allocate to invoices and there was no filter to reach them from an order — so the panel that was built is labelled honestly as customer-level rather than pretending to be order-level.

Faking a capability in the client is not a shortcut, it is a debt with the interest hidden. The user learns to distrust the screen, and the missing server work stops being visible to anybody planning, because the screen looks finished.

What the sweep looked like when it was done properly

The one sweep that closed this properly across every list screen is worth describing, because the scale is the point.

Every list got column sorting wired to the sort columns each endpoint actually permits — never a column the server would reject. Foreign-key filters became pickers rather than raw identifier boxes. Row actions were added and permission-gated, with domain-correct omissions: an order is a workflow and does not get an edit action, an invoice can be edited only while it is a draft and voided once issued, a movement ledger is read-only because it is a ledger.

And nineteen native browser confirmation dialogs across fifteen files were replaced with a proper component. That sounds cosmetic and it is not: a native dialog cannot be styled, cannot be translated through the application’s own translation layer, and cannot show a loading state, so every destructive action in the product was untranslated and gave no feedback while it ran.

The reason to do all of that in one sweep rather than incrementally is consistency. A table contract applied to thirteen feature areas at once is a contract. Applied to three of them is a preference, and the next person will follow whichever example they happen to open.

Rules

Define done across layers and refuse to mark anything done that is not reachable by a user. API, clients, permissions, tests, translations.

Measure completion by using the product. Re-derive it. A tracker records what was claimed.

Never ship a control that does not work. An absent capability is honest. A dead one destroys trust in the screens around it.

When the server cannot support a screen, report the gap and label the screen honestly. A panel that says what it actually shows is worth more than one that implies what it cannot.

Apply an interface contract to every screen in one pass. Partial adoption is not a smaller version of the standard, it is the absence of one.

Working on something like this?

We build this kind of software, and we staff the teams that do.

Get in touch