2026-04-24
UX Principles for Engineers
Transferable concepts, patterns, and vocabulary for building software that feels good to use, written for backend developers who are new to design.
I’m an engineer, not a designer. I wrote this down so I’d stop forgetting it.
The three layers
Most design problems fall into one of three layers. Knowing which layer a problem lives on tells you which tool to reach for.
| Layer | What it decides | Fails as | Tool to fix |
|---|---|---|---|
| Information architecture | What concepts exist and how they relate | ”I don’t know what this app is about.” | Concept map, rename things |
| Interaction design | What happens when you do something, and how state moves between screens | ”I got stuck / lost / surprised.” | Flow diagrams, prototypes, state machines |
| Visual design | Type, color, hierarchy, motion (how it looks and feels) | “This feels cheap / cold / generic.” | Mood boards, component libraries, a single aesthetic commitment |
A complementary framing, especially useful for products with voice (kids’ apps, coaching apps, anything with narrative):
| Layer | Focus | Core question |
|---|---|---|
| Narrative design | How the product speaks | Does the language, tone, and structure honor the user? |
| Visual design | How the product looks | Does it feel clear, considered, and worthy of the content inside it? |
| Interaction design | How the product behaves | Does every action feel effortless and reinforce the user’s agency? |
Order them in the sequence a user actually encounters the product: voice before pixels, pixels before behavior. A screen that passes one layer and fails another is still failing.
Bridge from backend: IA is schema design. Getting the data model wrong makes every API awkward. Getting the IA wrong makes every screen awkward. Both are expensive to migrate. Spend time here up front.
Orthogonalize the axes (find the independent dimensions)
Before designing anything, identify the independent dimensions of your problem. Confused designs are almost always designs where two axes were collapsed into one.
Test: A parent who wants read-aloud mode in Golpo isn’t necessarily targeting a younger child than one who picks read-independently. Reading mode and age are independent — a 6-year-old might want to read alone; a 4-year-old might want a parent along. If you can imagine a user who is high on one dimension and low on the other, they deserve separate treatment. If you can’t, they’re not separate axes.
Common axes worth separating in almost any product:
- Concrete attribute vs. abstract stance. Age vs. skill level. Plan tier vs. feature access. Role vs. permission.
- Author vs. reader. Creating content vs. consuming it: different postures, different UX needs, often different screens.
- Operator vs. end-user. Admin / parent / manager surfaces vs. the person using the product day-to-day.
- Session vs. identity vs. profile. “I am here now” vs. “this is me across sessions” vs. “this is who I am managing.” Most products conflate at least two of these.
- Private vs. shared. Work done alone vs. work done with or for others. Different stakes, different UI.
- Guided vs. free. Step-by-step wizard vs. open canvas. Often toggleable within the same product.
- Entry intent. The same user arriving via a share link, a cold homepage visit, or a notification is three different users. Don’t force them through one funnel.
Bridge from backend: This is the same move as separating authentication from authorization. They feel fused; they aren’t. Conflating them produces bugs in backend and confusion in UX.
Core principles
On the product as a whole
Jakob’s Law. Users spend most of their time on other products. Novel ≠ better. Innovate where it creates real value; conform where it doesn’t. A grid of cards is a grid of cards. Don’t reinvent it unless you have a reason.
Principle of least astonishment. What happens should match what the user expected. Every surprise is a small failure, even positive ones.
One product, one voice. A feature that feels imported from a different company is a failure. Coherence beats individual screen quality.
On the first session
Time-to-value. The clock starts when the user lands and stops when they feel the product working for them. Every onboarding step is a tax against this clock. Spend the budget wisely.
Deferred registration. Do not ask for an account before the user has felt value. Let them play anonymously first; claim their work on sign-in. This is why Figma, Canva, and TikTok feel frictionless.
Avoid the blank page. In creative tools, the empty state is the hardest moment. Pre-fill, suggest, or seed with examples. A thoughtful zero-state is worth ten polish passes elsewhere.
Ask concrete, derive abstract. Users answer “how old are you?” easily; they stumble on “what’s your reading posture?” Ask the concrete proxy, compute the abstract stance, and expose the abstract as an override.
On complexity
Progressive disclosure. Show the minimum; reveal complexity on demand. Power-user settings belong behind a toggle, not on the main surface.
One thousand no’s for every yes. Every element on a screen must earn its place. If a section feels empty, fix the composition: more whitespace, bigger type, bolder hierarchy.
Forgiving formats. Accept more than you demand. Dates, phone numbers, IDs: strip whitespace, normalize, guess intent. The user should never be punished for a formatting mistake the computer could have fixed.
On feedback and state
Every action has a reaction. If the user does something, the product acknowledges it within ~100ms, even if the real result is async. Silence is the worst feedback.
Undo beats confirm. Prefer a reversible action with an undo over a “are you sure?” modal. Modals interrupt; undo respects the user’s flow.
State visibility. The user should always be able to answer “where am I, what just happened, what can I do next?” from the screen alone. No guessing.
Reusable patterns
Happy path, then edges. Design the most common successful flow end-to-end first. Only then fan out to error states, empty states, edge cases. Designing edges first produces a product that feels defensive instead of purposeful.
Hierarchy of decisions. On any screen, there is one primary action, zero or one secondary actions, and several tertiary ones. Make the primary action obvious (color, size, position). Demote the rest. “Everything bold, nothing bold.”
Forgiving first, then strict. The first time a user does something, be forgiving: accept any input, offer examples, don’t punish mistakes. As they become power users, let them opt into stricter, faster, denser surfaces.
Zero state, empty state, error state, loaded state. Every screen has four states. Design all four. The empty state is almost always the weakest in a real product. Invest there.
Pre-commit vs. post-commit. Destructive, expensive, or irreversible actions want a pre-commit pattern: a confirmation, a preview, or a reversible delay. Everything else should be instant.
Convention over invention. A hamburger menu in the top-left. A search icon that looks like a magnifying glass. A primary CTA that’s filled and on the right. These conventions are boring on purpose. Follow them for 95% of your UI; save your novelty budget for the 5% that matters.
Design process
- Understand the user. Who are they? What are they trying to do? What do they already know? Everything else compounds from this.
- Enumerate flows. List every way a user might move through the product. Draw them. Label entry points, decisions, async waits, terminals.
- Orthogonalize. Identify the independent axes. Pin the ones you aren’t addressing now.
- Commit to a direction. Decide the aesthetic, the voice, the mode. Do this before pushing pixels. Half the bad designs in the world are designs made without committing.
- Design the happy path. End-to-end, one flow at a time.
- Fan out to edges. Empty states, errors, re-entry, interruptions.
- Show variations. Don’t fall in love with your first idea. Hold it next to two alternatives. Pick by comparison, not by attachment.
- Cut. Every element that didn’t earn its place.
Vocabulary
| Term | Meaning |
|---|---|
| Affordance | A visual cue that signals how something can be used (a button that looks pressable). |
| Information scent | How strongly a link or label hints at what’s behind it. Weak scent = users bail. |
| Fitts’s Law | Targets that are bigger and closer are faster to hit. Primary actions deserve real estate. |
| Hick’s Law | More choices = more decision time. Limit options or group them. |
| Progressive disclosure | Reveal complexity only when needed. |
| Empty state / zero state | The screen a brand-new user sees before any content exists. |
| Dead end | A screen with no obvious next action. Always a bug. |
| Destructive action | Something that can’t be undone or costs real money. Deserves distinct treatment. |
| Primary / secondary / tertiary | The decision hierarchy on a given screen. |
| Hero flow / golden path | The single most important flow in the product. The one you polish first. |
| Entry intent | Why the user landed here (cold visit, share link, notification, etc.). Often deserves separate design. |
| State machine | The formal set of states a UI can be in and the transitions between them. |
| Scaffolding | Temporary structure that supports the user until they don’t need it. Training wheels, inline hints, example content. |
| Deferred registration | Letting users do real work before asking for an account. |
| Happy path | The successful, normal-case flow. Design this first. |
| Dogfooding | Using your own product daily. The single best way to find what’s broken. |
Bridges from backend to design
You already do most of this thinking, just in a different domain.
- Information architecture is schema design. Get the nouns and relationships right before you worry about pages.
- Interaction design is state machines. Every screen is a state; every button is a transition. Design the graph, not the pages.
- Empty states, error states, loading states, loaded states are your four required branches. Handling one and not the others is like catching success but not exceptions.
- Undo is transaction rollback. Prefer it over confirmation dialogs wherever the work to implement is reasonable.
- Orthogonalization is separation of concerns. The same discipline that makes clean modules makes clean UIs.
- Progressive disclosure is the interface analog of good defaults. Everyone gets the reasonable thing; power users opt in.
- Forgiving formats is Postel’s Law. “Be conservative in what you send, liberal in what you accept” applies to user input too.
Common traps
Adding filler to fill space. A screen feels empty, so you invent a “stats” section or a decorative illustration. Solve it with composition: more whitespace, bigger type, bolder hierarchy. Let the page breathe.
Designing for the power user first. You know the product, so you surface the advanced options prominently. Design for the first-time user instead. Hide power features behind progressive disclosure. Reveal density only once the user has signaled they want it.
Asking every question your database can store. Onboarding becomes a form. Ask the fewest questions that unlock real personalization. Derive the rest. Defer the optional.
Falling in love with the first idea. Always hold your favorite next to two alternatives. You don’t know if an idea is good until you’ve seen it beat something.
Treating novelty as quality. Spend your novelty budget on the 5% that matters. Conform on the rest. Jakob’s Law is undefeated.
Confirming everything. Every action gets a “Are you sure?” dialog. Reversible actions + a brief undo window respect the user’s flow better. Reserve modals for truly destructive or expensive actions.
Living document. Add to it when you learn something that would have helped you yesterday.