Skip to content

Vet Clinic OS

A clinic operations system rebuilt around the thirty seconds a veterinarian has between two patients — the only window in which a visit gets recorded or stops existing.

Client
Private veterinary clinic — name withheld under NDA
Product
Clinic operations SaaS: schedule, medical record, invoicing
Year
2026
Role
Product Designer
Platform
Web — desktop, tablet in the room, owner’s phone

Outcome. The record of a visit stopped being an all-or-nothing document. A thirty-second trace — weight, drug, dose — is made in the room and reaches colleagues immediately; the full record is written later without overwriting it. What it cost: a card that stands openly incomplete for hours, and an incompleteness every reader downstream must be able to see.

Team
Sole designer — research, product decisions, design system and the prototype
Product / research
Conversations with a practising veterinarian; no research team
Duration
Two weeks, brief to working prototype
The veterinarian’s queue for the day: patients at the clinic, patients expected, and a notice that three visits from earlier in the week are still unfinished — with the patient card and the schedule behind it
The veterinarian’s day — who is here, who is expected, and three visits still unfinished from last week. Data is invented.

Between Thursday and Saturday, the visit did not exist.

A veterinarian in a small practice sees ten to twenty patients a day. The clinic’s software is opened before the appointment and after it, and never during: while the patient is on the table, the record lives in a paper notebook. The first reason for the notebook is not privacy, it is physics — she writes with one hand, standing, because the other one is holding the cat.

Everything after that is a transfer between tools: seven of them in one appointment, two of the same number — the weight goes into the system, then by hand into a dose calculator on her phone. That is where her one real near-miss came from: a decimal point moved by hand, caught by her and by nothing else. Services reach reception by voice — “put down two hundred, I’ll clarify later” — and are not clarified. The record itself is written at home at nine in the evening, part of it from memory.

The cost of that is not paperwork. Thursday’s appointment was never entered, so on Saturday a colleague sees the same cat, finds a last entry a year old, examines from scratch and nearly prescribes a second anti-inflammatory on top of the first. What stopped it was a phone call answered on a day off. The system was no part of the safeguard, because it did not know the visit had happened.

The unit of work was never the visit. It was the trace.

The brief asked for faster closing of visits, and the metric behind it was the share closed in the system on the day of the appointment. Both are reasonable, and the person they were written for took one sentence to show they were wrong.

“The previous patient hasn’t left yet and the next one is already in the room. I have a choice: sit down and enter it, or smile at the next one. I always smile at the next one. Twelve times a day.” A product that asks for the whole record inside that window does not get half a record. It gets postponed whole — which is exactly what the current one does.

So the object the system has to hold is not the closed visit. It is the trace: the smallest record worth having — weight, drug, dose, one line of free text — made in seconds, visible to a colleague immediately, completed later without being overwritten. The measure moves with it: not completeness by the end of the shift, but whether a trace exists by the time the veterinarian leaves the room.

Make the smallest useful record survive thirty seconds, and let everything else be written later.

I checked the incumbent before I drew anything, and wrote down what I could not check.

This clinic was not coming from paper: practices of this size already run specialised software, so the product had to beat an incumbent rather than replace a filing cabinet — which makes it the only hard evidence there is. I audited it screen by screen against heuristics, with a severity scale that keeps “blocks the work” apart from “looks untidy”, and with a limit written into the report: the veterinarian’s own visit screen, the schedule and the owner cabinet were not in the material I had, and no conclusions were drawn about them.

Then desk research on the market and on the regulation around veterinary records. Three of its numbers could not be traced to a primary source, so they were marked unverified and kept out of the PRD rather than rounded into it. Everything the conversations could not confirm carries the same mark and stays a hypothesis for real customer development — including the two that changed the product, because a hypothesis that flatters your redesign is still a hypothesis.

Then scope: 31 Must-haves out of 62 requirements, with the core of the appointment declared indivisible — seven parts that ship together or not at all. Then a 44-screen sitemap, three flows, twelve low-fidelity frames. Then the design system, then twelve high-fidelity frames carrying one story end to end, from the queue to the discharge summary on the owner’s phone, with 31 edge cases built as hidden states instead of described in prose. Then a React prototype, a catalogue in Storybook, and synthetic agent runs over three scenarios, whose findings came back in four waves of fixes.

The prototype, on invented data — thirteen screens, the same build the agent runs walked through.

Open the prototype (external link, opens in a new tab)
Thirteen screens as one index — every route the prototype has, and the frame each one came from.
One component, every variant it is allowed to have — twenty-four for the button alone. The matrix is checked in the catalogue, not on the screen.

The screens passed my own audit. The prototype did not.

Booking. A free slot in the schedule created a visit with no patient, no owner and no reason — a record that exists and says nothing, in a product whose whole argument is that a record must be worth having. The scenario was in the sitemap; the screen for it was in neither the design file nor the specification. It appeared first in the prototype and had to be registered there as prototype-only: the design source and the build had diverged quietly.

Saving. The workspace showed the save status twice — once in the header, once in the side rail. On a product whose one dealbreaker is a lost draft, two indicators of the same fact is the defect you can least afford: the first time they disagree, neither is believed again. The fix was not to make them agree. It was to delete one.

And that deletion was not finished when the thing was gone. The rule pinning the save status to the bottom of the rail stayed behind, took the navigation as its new last child, and slid every menu item down into an empty column. The screens passed, the build passed, nobody saw it. I found it weeks later, shooting the frames for this page — a screenshot has no opinion about which part of a rail you meant to look at.

Four decisions, and what each one cost.

The trace and the full record are one object in two stages, not two documents.

Why
Completeness by the end of the shift cannot survive thirty seconds between patients, and a second document would let the old habit continue under a new name. The trace also has to reach a colleague immediately — otherwise Saturday repeats, with a year-old card and a doctor examining from scratch.
Cost
The card stands openly incomplete for hours, and that has to be legible to everyone downstream: the colleague reading it, the administrator billing from it, and the publication to the owner, which refuses to send a trace at all.
Three steps, one hand, and a draft that is already saved. The full record is a separate action below.

The dose is calculated from the weight already in the system, and the whole calculation is shown — formula, substitution, rounding.

Why
The real near-miss here was not a gap in knowledge, it was a decimal point moved by hand on the way to a phone calculator. Removing that transfer needs no drug reference at all: the weight is already there, and a number the doctor cannot re-type is one she cannot mistype. Showing the arithmetic makes it checkable rather than trusted.
Cost
No species contraindication warnings in the first version. The risk that worries the clinic and its lawyer stays uncovered and became a separate go/no-go decision, because a warning table we cannot license or verify is a promise the interface cannot keep.
The weight comes from the card and cannot be typed in here. Formula, substitution and rounding are all on screen.

The veterinarian’s private zone gets its own colour in the palette, not a label.

Why
“I don’t like the look of this” is a class of clinical information that currently lives nowhere: it goes into a notebook that is thrown away, and it is gone by the time the patient comes back. Bringing it into the system only works if the boundary — the owner will not see this — is impossible to miss and impossible to erase. A label survives a rebrand only if someone remembers it; a reserved hue survives it by construction.
Cost
One tone of the palette is spent for good and cannot be reused for anything else, on a product that otherwise runs on a single accent.
The private note carries its own hue and says who can see it. It is in no discharge summary and on no invoice.

Publishing to the owner is an explicit act by the veterinarian, with a preview from the owner’s side.

Why
Premature publication of a draft is irreversible in a way a bug is not, and the response to it is rational: she will write only what she is ready to read aloud — and then the card is empty, the invoice is guesswork and the discharge summary says nothing. The preview is not hygiene here. It is the condition under which anything gets written at all.
Cost
An extra step on every visit, and a discharge summary that cannot be automated even when the record is complete and nothing in it is sensitive.
What the owner will see, next to what is being held back from them, before anything is sent.

Thirty-one components, and one I deleted.

Eighty-five variables — 54 primitive, 31 semantic — 21 text styles, 31 logical components, 136 variants. Every fill and stroke resolves through a semantic variable and every text node through a named style. A full read-only scan, not a sample, found zero hardcoded colours, zero text nodes without a style and zero detached instances; the thirty geometry values still unbound are listed as debt, not hidden behind a claim of parity.

The one I deleted is the card container: three variants, zero instances anywhere in the product. A component carries exactly the anatomy it was created with, and this one had a title and a single row, while the real blocks here need two to five elements, several of them nested instances. Keeping it “for later” would have meant every screen quietly working around it, which is worse than not having it.

Coverage is not checked by eye: a script walks the catalogue against the variant matrix, and the states below are shot from the catalogue, not assembled by hand.

SaveStatus — saved · saving · unsaved · offline
Input — single / multiline / search × default · focus · error · disabled
ChoiceChip — value / drug / service × default · selected · pressed · disabled
WeightReading — clinic / owner × base · lg
TimeSlot — free / booked / closed × default · pressed
StatusTag — neutral · success · warning · error · info

What got solved, and what it cost.

Solved
A trace of the visit exists before the veterinarian leaves the room, so the colleague who opens the card on Saturday sees Thursday. The invoice is assembled from what the doctor marked during the appointment, not from a question at the reception desk. The owner receives what was published and nothing that was not.
Sacrificed
Species contraindication warnings, drug accounting and labelling, and taking payments. All three were cut deliberately and written down as cuts, each with the condition under which it returns.
Why the price was right
Each refusal covers something the product cannot guarantee on its own: a licensed source of dosing rules, a regulator’s accounting machine, someone else’s money. A system that promises any of them breaks on the first real shift and takes the doctor’s trust with it — the one thing here that does not come back. An inconvenient program she will get used to; one that lets her down once, she will not turn her back on again.
What changed in how I work
I stopped counting an audit as passed when only the screens passed it. The prototype found a screen that did not exist, and a save indicator duplicated in the one product where a lost draft ends the relationship — neither of which a review of the frames was going to catch.

The clinic is covered by an NDA: it is not named, and no figures from it are published. There is no baseline to publish them against either — the metrics here are a plan for measurement, not a claim of results, and the ones worth measuring came from the veterinarian as things you can observe: the notebook is not replaced, she leaves at seven instead of nine, the administrator stops asking what to bill. Nothing above is adoption.

This case is mostly a list of refusals.

Every decision above bought a guarantee by giving something up, and every one of those trades is arguable.

Get in touch

Let's talk

If the case above answered your question — or raised one.