This series is written in the open, from a real production system. This chapter is the feedback loop: widget, notification, console, and a real bug from report to resolved. [All chapters and diagrams live in the public repo.]
The single most valuable object in this company is a bug report from a
real user. It is worth more than a roadmap session (it's evidence, not
opinion) and more than an analytics dashboard (it comes with intent and
context). Everything in this chapter exists to shorten the distance
between user notices problem and user hears it's fixed — because a
loop that closes while they still remember filing it converts a critic
into a collaborator.
The feedback loop, rendered live: widget → attachment upload → reply-to-reporter email → triage → fix → deploy → resolve.
The widget: lower the friction to zero
A floating button on every page. No login required — anonymous users
leave an optional reply-to email (and many do). The message field asks
for anything: bugs, confusion, ideas. The attachment field accepts
screenshots and screen recordings (MP4/WebM/MOV, ≤ 15 MB) —
recordings after we learned that users describing a broken flow in
words produce ambiguous prose, while thirty seconds of screen video is
a repro script. Two small decisions that matter:
- The comment is the payload; the attachment is enrichment. If object storage hiccups mid-submit, the feedback still saves and the loss is merely noted in the logs — never lose the words because the picture failed.
- The page URL and user-agent ride along automatically. "It's broken on /payments" arrives saying exactly which /payments.
The notification: reply-able by construction
Submission queues an outbox email (Chapter 03's transactional pattern)
to the founder with everything inline and — the detail that took a
bounce-storm to learn — Reply-To set to the reporter's address.
Staff hitting "reply" answers the user, not a dead mailbox. The email
also carries a deep link straight to the console entry.
The console: a tiny ticket system, honestly scoped
A single page with four tabs — New → Triaged → Resolved / Archived
— attachments rendered inline (with an auth-header blob fetch, because
<img src> can't carry tokens), recordings playable in a <video>
player, and a status workflow with timestamps. That's the entire ticket
system. No SLAs, no assignee matrices, no Jira — a workflow is only as
wide as the team that maintains it, and this one is maintained in the
same codebase as the product (fix-adjacent, therefore fix-adjacent in
the diff).
Case study: one bug, report to resolved
The best way to show the loop is a real one. A user reported: "On
clicking Edit invoice button, new invoice page is opened. Not able to
edit invoices." Same morning, a second report from the same user:
double-clicking Create made duplicate invoices.
Repro (docker + seeded DB): both reproduced instantly. Diagnosis
went two layers deep — the visible bug was Angular route-input timing
(the editor read its id before the router bound it), but reproduction
exposed something worse underneath: the invoice repository loaded
entities without line items, so line updates 404'd and — the
corruption class — adding a line recomputed totals against an empty
set, silently wiping the invoice's math. One user's confused click had
uncovered a data-integrity hole. Fix: an effect for the route
input, and Include(LineItems) at the repository level (which also
fixed quote conversion losing its lines). Ship: tests, commit
referencing the feedback, push — the Chapter 07 pipeline had it live
same-day. Close: status → Resolved; the reply (one click, thanks to
Reply-To) told the user exactly what had been wrong and that their
report caught a second bug they never saw. That user has filed more
feedback than anyone — because the loop visibly worked.
Why this beats analytics
Analytics tells you that something is wrong ("drop-off on step 3").
Feedback tells you why ("there's no currency picker on this form" —
a real report that same week). Instrument funnels, yes; but read your
inbox like a scientist. At small scale, ten verbose humans outperform
any dashboard you can build.
Recap. Zero-friction capture with recordings, reply-able
notifications, a ticket workflow exactly as wide as the team, and a
loop fast enough that the reporter experiences the fix. The feedback
loop isn't support — it's your highest-signal product process.
Reading this and want to file bug #37 yourself? Create a free workspace at
foxyinvoice.com, then redeem founding code
U8B4Z8S87X on the Upgrade page — 6 months of Pro, free, no card. If anything
breaks, there's a feedback button in the app. I read every one.
Next: Chapter 11 — Reach: SEO, AI crawlers, and being the machine-readable answer.
Top comments (0)