We built the same pharmacy counter twice on mobile, and the second one is smaller because of what the first taught us
Two attempts at putting the dispensing counter on a phone. The first mirrored the desktop console and was too deep to use standing up; the second kept five screens and pushed everything else behind a drawer.
Who, what, and how long
- Industry
- Healthcare & Dental Practices
- Duration
- 14 weeks across two builds
- Cooperation model
- Time and materials
Client name withheld under NDA. Engagement details are shown to the extent our agreement permits.
The hard problem
The first build reproduced the desktop information architecture: a drawer, tabs, and management sections nested two and three levels deep. On a phone, held one-handed, next to a customer, the useful screens were four taps away and staff went back to walking to the terminal.
The first build reproduced the desktop information architecture on a phone: a drawer, tabs, and management sections nested two and three levels deep. Held one-handed next to a customer, the screens people actually needed were four taps away. Nobody reported it as a fault, because a slightly awkward app isn't a bug. They simply stopped opening it.
The second build starts from what someone standing up actually does: look something up, take a payment, check stock. Those are tabs. Everything that's administration (categories, companies, generics, suppliers, expense types) went behind a drawer nobody needs during a transaction.
How the pieces fit
- 01
Kept the second build's tab bar to the five things done while standing
The tab bar holds the five things done standing up, which was decided by watching people work, not by asking what they wanted on the phone.
- 02
Pushed all management screens behind a drawer, out of the tab hierarchy
Setup lists (categories, companies, generics, suppliers, expense types) live in a drawer, away from the sale.
- 03
Made search its own route with the query in the URL, so a lookup is resumable
Search is its own route with the query held in it, so an interrupted lookup resumes on back with nothing retyped.
- 04
Split purchase returns and sales returns into two screens, retiring the single screen with a direction toggle
Splitting them cost one drawer entry and removed a class of mistake that moves both stock and money in the wrong direction.
- 05
Reused the desktop API unchanged, so the rebuild was navigation and nothing else
The desktop API was reused unchanged, which is what made the rebuild a navigation exercise instead of a second project.
The system we were asked to build
After the counter system shipped on desktop, they wanted it on a phone for stock checks on the shop floor. We built it, learned what didn't survive contact with a standing user, and built it again.
After the counter system shipped on desktop, the chain wanted it on a phone for stock checks on the shop floor. The first build was delivered, used briefly, and abandoned: staff went back to walking to the terminal. The second build is the one that shipped, and the interesting part of this engagement is the two days that separated them.
Mobile Engineering
Five tabs, chosen deliberately
The tab bar holds only what someone does standing up; everything else is one drawer away.
The tab bar holds five things, chosen from what someone standing at a counter actually does in a transaction: look up a medicine, take a payment, check stock, handle a return, see the day. Everything else is one drawer away. The first build had put administration in the tab bar, which meant the frequent actions were competing with screens used twice a month.
- Five tabs, chosen from what happens standing up
- Everything else one drawer away, off the bar
- Frequent actions no longer share space with monthly ones
Stock filtered to low and expiring, with a fridge line marked, and along the bottom the five-tab bar that is the whole of the second build's navigation.
The drawer over the stock screen: suppliers, generics, companies, categories and expense types as real screens, ordered by how often each was opened.
Administration behind a drawer
Categories, companies, generics and suppliers are real screens. They just aren't in the way of a transaction.
Categories, companies, generics, suppliers and expense types are complete screens with full editing. They're simply out of the path of a transaction. Demoting them cost nothing, because the person configuring suppliers is sitting down with time, and the person selling isn't. The drawer is organized by how often things are actually opened, not alphabetically.
- Administration screens complete, just out of the transaction path
- Configuration is a sitting-down job; selling isn't
- Drawer ordered by real frequency, not alphabetically
Search is a route
The query lives in the route, so an interrupted lookup picks up where it left off.
The search query lives in the route through Expo Router, so an interrupted lookup (a customer question, a phone call, a switch to take a payment) is resumed by going back, with nothing retyped from the beginning. State in a route also means a search can be shared between staff on two devices, which turned out to matter more than expected.
- Query held in the route, so back resumes the search
- Interruptions cost nothing; nothing is retyped
- A lookup can be handed between two devices
A half-typed medicine lookup with the query carried in the route beneath the field, resumed after a payment interrupted it, and ready to hand to another phone.
Sales returns on a screen of their own, with purchase returns one segment away: an unopened thermometer going back on the shelf and its refund going back to the card, with no direction toggle to misread.
Returns split by direction
Purchase returns and sales returns are separate screens, with no toggle to misread.
Purchase returns and sales returns are separate screens, not one screen with a direction toggle, because a toggle is a thing to misread and the two are irreversible in opposite directions. Splitting them costs one drawer entry and removes the mistake entirely. That's the right trade for an action that moves both stock and money.
- Two screens in place of one with a direction toggle
- No control to misread on an irreversible action
- Costs one drawer entry, removes the mistake class
Same API, new navigation
A wholesaler delivery arriving short, offering a substitution or an owing record at the top of the screen before the stock update, written to the same records the desktop console uses.
The rebuild changed the route tree and nothing underneath it, which is why it took weeks, not months.
The rebuild changed the route tree and nothing beneath it: the same REST API, the same records, the same business rules. That's why it took weeks instead of months, and why it could be rolled out without retraining anyone on anything except where things now live. The operations themselves behaved exactly as before.
- Route tree replaced; API, records and rules untouched
- Weeks, not months, with no backend risk
- Rollout needed no retraining beyond where things live
Phase by phase
Phase 1: The First Build
The Desktop, Shrunk
Built the app as the console's structure on a smaller screen: drawer, tabs, and nested management sections. It was complete and it was correct, and staff didn't use it.
- Drawer & Tabs
- Management Sections
- Dispense Screen
Phase 2: Watching It Fail
Four Taps Is Too Many
Spent two days on the shop floor. The finding was blunt: the three things done while standing were all several levels down, and walking to the terminal was faster than navigating to them.
- Floor Observation
- Tap-Depth Audit
- Rebuild Brief
Phase 3: The Second Build
Five Screens And A Drawer
Rebuilt navigation around standing work (home, dispense, stock, reports, settings), with every management surface behind a drawer. The API didn't change; only the route tree did.
- Tab Bar
- Drawer
- Route Tree
Phase 4: Search As A Route
Resumable Lookups
Made the medicine search its own route carrying the query, so an interrupted lookup can be returned to instead of retyped, which on a busy counter happens constantly.
- Search Route
- Query In URL
- Recent Lookups
Today, the first of the five tabs: the day's dispensing, takings, open owings and fridge items waiting, the prescriptions at the counter, and the interrupted lookup one tap from resuming.
What it carries now
4
Taps to dispense, first build
1
Taps to dispense, second
5
Tabs
0
API changes for the rebuild
Taps to dispense in each build are counts from the same task performed on both. Tabs is the size of the second build's tab bar. API changes for the rebuild is zero: the same endpoints, records and business rules served both, which is why the second build took weeks instead of months.
Client name withheld under NDA. Figures are approximate, drawn from the engagement’s own reporting.
What the architecture settled
Porting a desktop information architecture to a phone reproduces the structure and loses the product.
A desktop information architecture ported to a phone preserves the structure and loses the product, because the structure was a response to a different posture.
Navigation depth is a usability budget. Spend it on what people do standing up.
Depth is a budget: four taps to the common action spends it all on screens used twice a month, and staff simply stop paying.
Two days on the floor produced a clearer brief than any amount of discussion about mobile patterns.
Two days of watching produced a brief that months of discussing mobile patterns hadn't, because what people do standing up isn't what they say they need.
When the API is right, a rebuild is a navigation exercise. That's an argument for keeping the two separable.
The rebuild cost weeks because the API was right and separable. That's the argument for keeping that boundary even when one client is the only client.
How the work was run
We showed them the first build failing instead of describing it. Two days of shop-floor observation, counted in taps, made the rebuild an easy decision, and made it a navigation project instead of an argument about whether the app was good.
Time and materials across two builds, which is what allowed the second one to happen at all; a fixed-price engagement would have delivered the first build and stopped. The API was untouched throughout, so the second build was a navigation exercise and cost weeks, not a second project.
One item, dispensed on both builds
Four taps became one. The calls to the back end didn't change at all.
The same task on both builds, from the home screen to dispensing one item. The first build reaches it through the desktop console's structure, the second from the tab bar. Watch the tap counter, then the API calls that follow. Switch tabs, or use the arrow keys once one is focused.
Dispensing one item · the same task on both builds
Timings, routes and endpoint names are illustrative- 1Tap “Open the menu”on Home
- 2Tap “Management”on Drawer
- 3Tap “Sales & dispensing”on Management
- 4Tap “New dispense”on Sales & dispensing
- Dispense screen open /(drawer)/management/sales/dispense/new
Dispensing sat inside the console's management sections, two and three levels down. Held one-handed next to a customer, walking to the terminal was faster.
- 1GET/api/prescriptions/RX-40718
- 2GET/api/stock?ids=…
- 3POST/api/dispenses
Same endpoints, same records, same business rules. What moved is where dispensing lives in the route tree, which is why the second build took weeks, not months.
A new route tree over an API that didn't move
The boundary between navigation and the back end is what made the second build a navigation exercise. The app and its routes were replaced; the API and the records beneath it are what the shop already ran.
- 01 · TriggerA tap at the counterThe day, a lookup, dispensing, stock and returns are the 5 tabs, chosen by two days of watching the shop floor rather than by asking.Rebuilt
- 02 · NavigationExpo Router route treeTabs for standing work, a drawer for administration. The search query lives in the route, so back resumes a lookup and a second device can open it.Rebuilt
- 03 · Business logicThe desktop REST API0 API changes for the rebuild. The same endpoints and business rules served both mobile builds, so every operation behaves exactly as before.Unchanged
- 04 · StateThe shop's existing recordsStock, sales, returns and supplier records are the ones the counter system already kept. The rebuild touched none of them.Unchanged
- 05 · ClientReact Native app on staff phonesExpo and TypeScript. Rollout needed no retraining on any operation, only on where each one now lives.Rebuilt
What a phone at the counter must not break
Dispensing under pressure & back-end safety
Nothing to misread under pressure
Dispensing is 1 tap away instead of 4, an interrupted lookup resumes rather than being retyped, and sales and purchase returns are separate screens, so no direction toggle sits on an action that moves both stock and money.
Fridge and part-supplied lines on their own cards
While a prescription is picked, the fridge line and the part-supplied line are each flagged on a card of their own before the label prints. A short delivery asks for a substitution or an owing record before the stock update.
A rebuild that never reached the back end
0 API changes: the same endpoints, records and business rules served both builds. The rebuild replaced the route tree and nothing beneath it, so the operations behaved exactly as before.
Staff walking back to the terminal instead of opening your app? Scope your build in 3 minutes.
Scope your buildNearby engagements
Web PlatformsThe ransomware attack that became a four-hour non-event
A ransomware attack encrypted the primary patient records server. Drilled offsite backups and a written runbook turned what could have been a scramble into a full restore inside the recovery time objective.
Healthcare & Dental Practices · Ongoing retainer
E-commerceA cart that won't check out until the prescription is real
An online pharmacy where a prescription is a first-class record with its own lifecycle, and prescription-only items simply can't leave the cart without one.
Healthcare & Dental Practices · 20 weeks
Web PlatformsThe right blood type isn't enough: it has to be someone who can get there
A donor register that matches each request on blood-group compatibility and on whether the donor can actually reach the hospital. One platform serves a web admin and a mobile client from the same records.
Healthcare & Dental Practices · 16 weeks
Let's talk
Running a large platform, shaping a first MVP, or getting a product ready for a funding round? Tell us where you are. We'll shape the process around it, and stay with you after launch.














