The UX process: how usability feedback becomes code

· bombay

Last updated 17 Feb

bombayprocesstoolsusability-testingux

Most development workflows treat UX as a phase. You design, you build, you test with users at the end, and you hope for the best. By the time feedback arrives, the code is already shipped.

This post describes a different approach — one where usability feedback is continuous, structured, and automatically routed into the build pipeline.

The loop

The process is a tight cycle:

Build → Test → Feedback → Fix → Build

Each step has specific tooling behind it. The goal is to make the loop as short as possible — ideally, a UX issue identified in testing becomes a task, gets refined, and lands as a fix in the same session.

Step 1: Build with UX guidelines baked in

Before testing ever happens, the code is written against a set of UX guidelines embedded directly in CLAUDE.md — the instructions file that governs how Claude Code works in each project.

These aren't aspirational. They're enforced rules:

  • No confirmation dialogues. Use inline confirmation controls or implement undo instead. Dialogues interrupt flow and train users to click "OK" without reading.
  • Save state. Users should return to the interface exactly as they left it — selected view, scroll position, open panels.
  • Keyboard shortcuts for common operations. Document them. Make them discoverable.
  • Buttons for actions, links for navigation. Don't mix them. Set sensible tab indices. Add tooltips (native browser tooltips, not custom).
  • No placeholder text as field hints. Use a visible paragraph instead — placeholders disappear on focus.
  • No question-mark cursors. Ever.

By embedding these in the instructions file, every feature is built with these constraints from the start. Claude Code follows them automatically.

Step 2: Test with think-aloud tasks

After building a feature, the next step is testing. The /usability-test skill scaffolds think-aloud test sessions — the kind where you watch someone use your interface while narrating their thought process.

For every new feature, the system suggests 2–3 specific tasks a user could attempt:

  • "Find and filter projects by task count"
  • "Add a new task to the dashboard without using the menu"
  • "Change the refresh interval in settings"

Good test tasks are specific, goal-oriented, and don't hint at the solution. They test whether the interface communicates intent, not whether the user can follow instructions.

Edge cases and error states get their own tasks. What happens when the user tries to add a task to a project that doesn't exist? What happens when the connection drops mid-save?

Step 3: Capture feedback

Feedback from testing sessions goes into _docs/feedback/ as structured files. Each file captures:

  • The task attempted
  • What the user did (observed behaviour)
  • What they expected (stated expectation)
  • Where they got stuck (friction points)
  • Severity: critical / major / minor / cosmetic

This isn't a database. It's markdown files in a folder, version-controlled with git. Simple, searchable, auditable.

Step 4: Analyse with /ux-lint

Between testing sessions, the /ux-lint skill runs heuristic analysis against the interface. It checks for violations of established UX heuristics:

  • Visibility of system status — Does the interface show what's happening?
  • Match between system and real world — Does it use language users understand?
  • User control and freedom — Can users undo, go back, escape?
  • Consistency and standards — Are similar things treated similarly?
  • Error prevention — Does the design prevent mistakes before they happen?
  • Recognition over recall — Can users see their options, or must they remember them?

Each violation gets a traffic-light severity rating (critical, major, minor) and a specific recommendation. The output is actionable — not "improve usability" but "add a loading indicator to the project list refresh button."

Step 5: Feedback becomes tasks

Here's where the inbox method connects. Every UX issue — whether from testing, from /ux-lint, or from your own observation — becomes a file in .in/:

.in/fix-missing-loading-indicator.md
.in/add-keyboard-shortcut-for-filter.md
.in/improve-empty-state-messaging.md

From there, the standard pipeline takes over: refine, implement, review, close. UX issues flow through the same system as feature requests and bugs. No separate tracker. No context-switching.

Patterns from practice

After running this process across several projects, a few patterns keep appearing:

Most issues are about visibility. Users don't know what state the interface is in. Adding loading indicators, success confirmations, and clear empty states fixes a disproportionate number of problems.

Keyboard users find bugs faster. Testing with keyboard-only navigation reveals focus management issues, missing tab stops, and inaccessible controls that mouse testing misses entirely.

Inline feedback beats modal feedback. When something goes wrong, showing the error next to the control that caused it works better than a toast notification or a dialogue box. Every time.

The biggest UX wins are subtractive. Removing a confirmation dialogue, removing a settings page, removing a step from a workflow — these consistently test better than adding features.

The result

The tight loop — build with guidelines, test with tasks, capture feedback, analyse with heuristics, fix as inbox items — means UX issues have a half-life measured in hours, not sprints.

Nothing special about the tooling. It's markdown files, shell commands, and a set of rules in a config file. The insight is that UX feedback belongs in the same pipeline as everything else. When it does, it gets fixed.


Have questions or feedback? Find me on LinkedIn or GitHub.

Partially written by a human