A few months ago, I was running QA on a fintech platform a customer-facing portal where businesses fund a wallet and spend from it. Every wallet feature passed testing. Deposits credited correctly. Transactions deducted correctly. The ledger balanced to the kobo.
There was just one problem: nowhere in the portal could a user see their wallet balance.
Not on the dashboard. Not on the wallet page. Not anywhere. Users could add money and spend money, but the single number they cared about most how much do I have left? didn't exist in the UI.
Every backend test passed.
Every API returned correct data. Every ticket was closed. By our own definition of done, the wallet was finished.
It wasn't. And the reason it slipped through says a lot about how most teams do QA.
We test that features work. We don't test that they make sense.
Here's the standard QA question: "Does this feature behave according to the acceptance criteria?"
And here's the problem: acceptance criteria are usually written from the system's point of view. "Deposit credits the wallet." "Transaction deducts from the wallet." "Insufficient balance returns an error." All true, all tested, all green.
Nobody wrote the criterion from the user's point of view: "A user can always see how much money they have." So nobody tested it, so nobody built it, so nobody caught it until QA ran the flows as an actual customer instead of as a test executor.
This is the gap. Functional QA verifies the system's promises. UX QA verifies the user's expectations. They are not the same list, and the second list almost never gets written down.
Three more bugs that were "working as intended"
The missing balance wasn't an isolated case. On the same engagement, static analysis and manual passes turned up a pattern of issues that no unit test would ever flag:
The silent logout. The frontend's token refresh was broken when a session token expired, the refresh call failed and the app quietly dumped the user back to the login screen. Mid-task. No message, no "your session expired," nothing. From the system's perspective, this was correct security behavior: expired token, terminate session. From the user's perspective, the app was randomly logging them out and eating their work. Same event, two completely different bugs and only one of them was on our radar.
The error message written for the developer. API errors surfaced raw to the user:
Request failed with status code 422.Technically the error was handled it didn't crash, it displayed something. But "handled" and "helpful" are different bars. A 422 on a form should tell the user which field is wrong and what to do about it. Anything less is an unhandled error wearing a suit.The dead end. A brand-new account, before any activity, showed empty tables with no explanation. No "you haven't sent anything yet," no pointer to the first action. First-time users the exact people forming their opinion of the product got the least designed screen in the app.
None of these would fail a test suite. All of them fail the user.
Why this is a Technincal PM or QA problem, not a design problem The easy conclusion is "we needed a designer." Sure a designer would have caught the missing balance at the mockup stage. But most client projects I've delivered didn't have a dedicated designer, and waiting for that budget line to appear means shipping broken experiences indefinitely.
The realistic conclusion: whoever owns QA owns the last line of UX defense. On teams without a designer, that's the TPM, the QA engineer, or the developers themselves. The good news is you don't need design training to catch this class of bug. You need a different set of questions.
The UX pass: five questions to run on every feature
After that engagement, I added a second QA pass to my process. It runs after functional testing passes, takes maybe 20 minutes per feature, and asks:
1. Can the user see the state they care about? Balances, statuses, quotas, progress. If the system knows it, the user should be able to find it.
2. What happens when this fails? Trigger the failure deliberately. Read the error message out loud. Would your non-technical client understand it and know what to do next?
3. What happens when this is empty? New account, zero records, no history. Is there guidance, or just a blank table?
4. What happens when this is slow? Throttle the network. Is there feedback, or does the UI freeze and invite a rage-click double submit?
5. What happens when the session ends? Expire the token mid-task. Does the user get told what happened, or do they just get thrown out?
Notice that every question is about a state the happy path never visits. That's exactly why functional QA misses them: test cases are derived from acceptance criteria, acceptance criteria are derived from features, and features are described in terms of what should happen not what the user experiences when it doesn't.
Fix it upstream: write UX into the acceptance criteria
The QA pass catches these bugs. Writing better acceptance criteria prevents them.
Compare:
Before: User can make a transaction from their wallet.
After: User can make a transaction from their wallet. The current balance is visible before and after the transaction. If the balance is insufficient, the user sees a message stating the shortfall. While the transaction processes, the UI indicates progress and prevents duplicate submission.
The second version is longer to write and dramatically cheaper to build than to retrofit. It also gives QA something concrete to test the user's expectations are now in the spec, not in someone's head.
The takeaway
"It works" answers the question did we build the thing right? "It's done" should also answer did we build the right thing for the person using it?
The wallet on that platform worked perfectly. It just wasn't done because the most important feature of a wallet isn't depositing or spending. It's knowing what's in it.
Run the five questions on the last feature you shipped. I'd genuinely like to hear what you find.
Top comments (0)