Common Mistakes, Anti-Patterns & Pitfalls

By Pritesh Yadav 11 min read

Most bad software is not the result of one giant blunder. It is the slow build-up of small, well-known traps that every builder falls into. This chapter is a catalogue of those traps. For each one you get the problem, why it hurts, and the fix. Read it as a checklist you can run your own work against.

One question ties the whole chapter together. It comes straight from this project's own rulebook: "Would a shopkeeper understand this, or is it for a developer?" Your real users are non-technical print-shop owners. They do not read manuals. They navigate by intuition. Almost every trap below is a way of secretly serving the developer instead of the shopkeeper.

Key idea: When a user "fails" at your interface, the design failed — not the user. That single shift in blame is the source of every fix in this chapter.

Trap 1 — Designing for yourself (the curse of knowledge)

The deepest trap is invisible: you build for the only user you fully understand, which is you. This is the curse of knowledge — a thinking bias where, once you know how something works, you can no longer imagine not knowing it. So you assume everyone shares your mental model. (A mental model is the picture in someone's head of how a thing works.)

Don Norman, the cognitive scientist who wrote The Design of Everyday Things (1988; revised 2013), gave us the perfect symbol: the Norman door. That is a door where you cannot tell whether to push or pull, because its looks won the fight against its usability. Norman said the design should show you how to use it "without any need for signs."

Analogy: A flat metal plate says "push." A graspable handle says "pull." When a door has a handle but you must push it, no amount of intelligence helps — the object lied to you. Your settings screen does the same when a button looks like a label, or a label looks clickable.

Two precise Norman terms matter here. An affordance is what an object lets you do (a button affords pushing). A signifier is the visible cue that tells you where and how to act (the word "Push," an underline on a link). Most everyday UX failures are missing or wrong signifiers, not missing affordances. The thing was clickable; nothing told the user so.

Best practice: Watch a real, non-team person use your screen in silence. Their "stupid" questions are pure gold — each one marks a place where your knowledge filled a gap the design should have filled.

Trap 2 — Adding features instead of removing friction

The second trap feels like progress, which is why it is so dangerous. Teams measure themselves by features shipped rather than problems solved. Marty Cagan calls this the feature factory or the build trap: the roadmap is a list of features, and "busy" gets mistaken for "valuable."

The evidence that this wastes work is brutal. Pendo's 2019 Feature Adoption Report — built from real usage across 615 software subscriptions — found that 80% of features in the average product are rarely or never used, while about 12% of features drive 80% of daily usage. The older Standish Group CHAOS data tells the same story: 45% of features are never used and another 19% rarely. So this is not new — it is the norm.

Common mistake: "The customer asked for an export-to-XML button." So you add it. Now every user must scan past it forever, your maintenance budget grows, and one person uses it twice a year. Every feature you add taxes everyone's ability to find the features that matter.

Fix: set goals as outcomes (did the shop owner get their first order out faster?), not as a count of shipped buttons. Discover the real problem before designing a solution. And treat removal as a feature — ask your team, "When did we last delete something?" For a print SaaS, the win is usually fewer steps to publish a product, not one more checkbox in settings.

Trap 3 — Dark patterns (deceiving the user)

Dark patterns — a term coined by UX researcher Harry Brignull in 2010, now often called deceptive design — are interface tricks that push users into things they did not mean to do. Two classic ones:

Confirmshaming
Guilt-tripping the user through the "decline" wording, e.g. a decline button that reads "No thanks, I don't want to save money."
Roach motel
Easy to get in (one-click signup), deliberately hard to get out (a buried, multi-step cancel flow).

These work in the short term and rot you in the long term. They "create ill will and destroy customer loyalty." They are also now legal risk: the EU's Digital Services Act, US FTC guidance, and California's CPRA all restrict them. The FTC's 2023 action against Amazon over Prime cancellation is a real-world "roach motel" enforcement case.

Key idea: A tricked customer does not come back, and tells others. For a small print shop whose growth is word-of-mouth, that is fatal. Make declining as easy and neutral as accepting; show prices up front; make cancel as easy as signup.

Trap 4 — Leaking the machine's internals

This is where the shopkeeper test bites hardest. Nielsen Norman Group's usability heuristic #2, Match between system and the real world, says: speak the user's language, not internal jargon. Heuristic #9 says errors must be in plain language with a way to recover.

The machine says (bad)The human reads (good)
200 OK / "System error""Saved successfully" / "We couldn't save — check your connection and try again"
message.exportError (a raw translation key)"We couldn't create your export. Try again in a moment."
A row labelled Path bTSxCeDjco or a bare UUID"Headline text" or "Logo.png"
Example: A real failure in this very codebase: a broken export surfaced message.exportError and "System error," design layers were named Path bTSxCeDjco, and a loading skeleton spun forever. Every one of those is an internal value that escaped onto the shopkeeper's screen. The fix is to finish the "last mile": turn each machine value into finished, human UI — derive a name from the content (a text layer shows its text; an image shows its filename).

Trap 5 — No loading, empty, or error states (the blank screen)

NN/g heuristic #1, Visibility of system status, says the system must always tell the user what is happening. A blank or frozen screen breaks this rule — a human reads "blank" as "broken." (Rough timing rules from Nielsen: under 0.1s feels instant; show a spinner past ~1s; show a real progress bar past ~10s.)

Every data-driven view needs all three states, or it is not shippable:

   FETCH DATA
       |
   +---+----+----------+
   |        |          |
 LOADING  EMPTY      ERROR
 skeleton "No        plain message
 (not a   products   + "Try again"
 blank    yet — add  (never a
 page)    one →"     silent fail)
Common mistake: A control that waits on a third-party widget (reCAPTCHA, a payment box, a map) goes dead while the script loads. On this project's /login, the reCAPTCHA loaded after the form, leaving "Log In" greyed out with no reason given. Reserve the space, show a "Loading verification…" state in its slot, explain any disabled button, and handle timeouts with a retry.

Trap 6 — Inconsistent labels and patterns

Heuristic #4, Consistency and standards, says users should never wonder whether two different words mean the same thing. There are two kinds: internal consistency (within your app) and external consistency — Jakob's Law: users spend most of their time on other apps, so they expect yours to behave like those. Every inconsistency forces re-learning and chips at the user's confidence.

Best practice: One action gets one word everywhere — not "Transverse" on one screen and "Horizontal" on the next. Spell out cryptic abbreviations (LH, W/H) or give them a tooltip. Before inventing a new list, form, or modal, copy how your existing ones already work.

Trap 7 — Destructive actions with no confirmation

Heuristic #5, Error prevention, says the best error message is the error that never happens. Delete, archive, deactivate, and cancel should confirm exactly what is lost, in plain words, and the confirm button should restate the action.

  • Bad: "Are you sure?" with a "Yes" button.
  • Good: "This will permanently delete 'Premium Business Cards' and remove it from all active orders. This cannot be undone." → button labelled Delete Product.

One nuance: do not over-confirm. For frequent, reversible actions, the gold standard is Undo (heuristic #3, user control and freedom), not a popup on every click. Reserve hard confirmations for the irreversible, high-consequence ones.

Trap 8 — Fake "Saved!" that silently drops data

This is the chapter's signature trap and the most damaging. The form sends a field, the UI cheerfully says "Saved," but that field was never validated or stored — so it vanishes. The user trusts a lie, which is worse than a visible error.

The technical roots are mundane: a field collected in the UI but missing from the server's validation rules; a database column missing from the model's writable list; a snapshot record (an order or quote that freezes data at purchase time) that forgets to copy the new field. Any one of these silently discards real customer data on checkout, sign-up, or address forms.

Key idea: Every input must round-trip: validate → save → read back → render. Prove it with a test that submits the field and asserts it persisted. A field with no such test is not done — your "Saved" message must reflect the true state, not an optimistic guess.

Trap 9 — Trusting opinions over behaviour

Rob Fitzpatrick's The Mom Test (2013) is named for a simple truth: even your mom will lie to be nice if you ask whether your idea is good. The goal of customer talks is to learn, not to be validated. Its three rules: talk about their life, not your idea; ask about specifics in the past, not hopes about the future; talk less, listen more. "Compliments are the fool's gold of customer learning."

Example: McDonald's wanted to sell more milkshakes. Focus groups suggested better flavour — sales did not move. Clayton Christensen's team instead watched: roughly 40–50% of shakes sold before 8:30 a.m., bought by lone commuters. The shake's real "job" was a one-handed, long-lasting companion for a boring drive. Its rivals were bananas and bagels, not other shakes. Stated opinion missed it; observed behaviour found it.

Fix: separate the problem (real and durable) from the solution (a guess), and watch what users do, not only what they say.

Trap 10 — Analysis paralysis vs shipping to learn

The opposite failure is endless research and polishing so you never have to be wrong in front of a real user — so you never learn anything. Eric Ries' Lean Startup (2011) offers the cure: the Build–Measure–Learn loop and the MVP (minimum viable product — the smallest thing that produces real learning). Reid Hoffman put it bluntly: "If you're not embarrassed by the first version of your product, you've launched too late."

Common mistake: Over-correcting into recklessness. "Embarrassing first version" must never mean "silently loses customer data." Ship small scope to real users — but Traps 7 and 8 (destructive-action safety, data integrity) still apply with full force.

Trap 11 — Mistaking pretty for usable

The aesthetic-usability effect (shown by Kurosu & Kashimura's 1995 ATM study, and Tractinsky in 1997) is that people perceive attractive things as more usable — sometimes more strongly than they actually are. The danger: a pretty UI masks real problems, so defects hide during testing and never get fixed. Beauty only buys forgiveness for minor issues; for severe ones, users lose patience anyway.

Best practice: Judge a design by behaviour — task success, error rate, time to finish — not by how it looks or by stated satisfaction. Heuristic #8, aesthetic and minimalist design, means remove the irrelevant, not add decoration — which loops right back to Trap 2.

Key takeaways

  • Run every screen through one test: "Would a shopkeeper understand this?" If it shows a code, key, UUID, or jargon label, it fails.
  • The curse of knowledge makes you build for yourself — the only cure is watching real, non-team users.
  • Roughly 80% of features go unused; default to removing friction, not adding capability.
  • Every data view needs loading, empty, and error states; a blank screen reads as broken.
  • "Saved!" must be true — validate, save, read back, render, and test it; a silent data drop is worse than a visible error.
  • Dark patterns and prettiness both buy short-term wins and cost long-term trust; listen to problems, watch behaviour, and ship small to learn.

Continue reading