2026-07-23
Stress-Testing Five 'Done' Sprints Before Writing Sprint 6
Five sprints in, every milestone closed, both test suites green. That's the moment it's easiest to start the next sprint on autopilot — pick the next epic off the roadmap, write the tickets, start coding. Instead I stopped and asked a harder question: does this app actually work for the people it's for, or does it just pass its own tests? The answer, once I actually looked, was "not entirely" — and the same session that found the gaps also groomed three real sprints out of them, closed a piece of tracking drift, and decided two architecture questions I'd been able to defer until now.
Testing the product, not the backlog
The first pass was simple: use the app the way a real person would, without a ticket telling me what to check. Seven issues came out of it immediately — a landing page that still reads as placeholder-ish, a nav bar that shows before login and doesn't behave like tabs, a beer search that (correctly) only searches the tavern's own ~200-beer list when a customer actually wanted to search the wider beer world. Along the way I also seeded a real dev admin and test customer account into SeedData.cs, because "click through it as an admin" was blocked on not having an admin account that wasn't a hand-crafted database row.
That would have been a reasonable stopping point. It's also exactly the surface-level pass a green CI suite already half-covers — the kind of thing a five-minute click-through catches. The more useful move was going one level deeper.
Asking "does this fulfill its goals," not "does this have bugs"
I asked for something harder: stress-test the customer and admin/owner experience against what the product is actually supposed to accomplish, not just what's visibly broken. That's a different question, and it surfaced a different kind of finding — twelve of them, six customer-facing and six admin-facing, each with real trade-offs instead of one obvious fix. A new customer's had/not-had filters are meaningless on night one, because there's nothing to filter yet. A repeatedly-failing PIN confirmation gives a bartender the same generic message whether the PIN was mistyped or genuinely locked out, which is correct for security and unhelpful for a person standing at the bar. None of these are bugs. They're places where the feature does exactly what it was told, and what it was told was incomplete.
Two of the twelve turned into something bigger than a UI ticket. The roadmap has always said bartenders and owners might need cleanly separate account tiers — but the code had quietly merged them a while back, and nobody had gone back to reconcile the plan with what actually shipped. And a bartender confirming a beer with their PIN mid-shift can't also flip a beer's availability without either a full login (which the current bartender-account model doesn't really support) or building a second, parallel identity system. Both got written down as open architecture questions rather than resolved by guessing, because guessing wrong here means rebuilding a data model later instead of a screen.
Deciding instead of deferring
Coming back to both questions directly, rather than letting them sit, turned out to unlock a cleaner answer than either original option. The account-tiering question resolved to: keep one Admin role, multiple individually-attributed accounts (the audit trail from Sprint 5 already gets you most of the way there), plus one top-level provisioning account — not a permission split, just ownership clarity. The availability question resolved even better: since a bartender is already typing their PIN into the customer's phone to confirm a beer, that same PIN check can authorize an availability flip in the same moment, without needing bartenders to hold a real login session at all. That synthesis wasn't on the table in the first round of options — it only showed up because we went back and looked at the tension between the two open questions together instead of answering each in isolation.
Grooming three sprints instead of one
With the concrete, ticket-ready findings in hand, three sprints came out of the same session: Mobile UI Polish (bottom tab bar, a real login screen, the PIN-failure signal, a cold-start search hint, a graceful offline message), Beer Discovery & Recommendations (external beer-database search plus customer recommendations with admin triage), and a smaller bundle of admin/engagement follow-ups that didn't need a full epic's worth of scoping to be worth doing now. Along the way, a real piece of tracking drift surfaced and got fixed: Milestone 4 showed as closed in GitHub while its seven issues were still open, because the closing pull requests had written "#40:" instead of "Closes #40" and GitHub's auto-close never fired. Verified every one against its actual merged PR before closing them by hand.
The review that almost didn't happen
Here's the part I'd have skipped on a tighter deadline: before writing a single line of code against any of the three newly-groomed sprints, I went back through each one against the real, current app — not the issue text as originally written, not memory of what the app does. Grepped the actual frontend nav for its real link count instead of assuming the ticket's description was still accurate. It wasn't. The new bottom tab bar sprint had no defined home for four existing nav items and a footer link that don't fit in four or five tab icons — a screen that #67 casually assumed would exist got scoped as its own issue instead. The beer-discovery sprint would have opened a real, paid, API-keyed dependency to unauthenticated customer traffic with no rate limit — caught before any code touched it. Six gaps like this turned up, split between two brand-new issues and four amendments to existing ones.
The PM angle
The habit worth keeping from this session isn't "test more" — it's that grooming isn't actually finished the moment a ticket exists and reads sensibly. A ticket written two sessions ago is a snapshot of what was true then, not a live view of the current code, and the two can drift without anyone noticing until someone builds against the stale version. The fix is cheap and boring: before the first line of implementation, go re-check the ticket against what's actually there right now. Twice in one day, doing that on purpose caught a real gap before it became a rebuild.
Where the project stands
Five sprints shipped and closed. Twelve usability gaps found, discussed, and resolved into decisions rather than silently patched. Two real architecture questions decided instead of deferred. Three sprints — Mobile UI Polish, Beer Discovery & Recommendations, and a bundle of admin/engagement follow-ups — fully groomed, reviewed a second time for coverage against the real codebase, and ready to build. No new application code this session; the entire session was planning, testing, and deciding. Next: actually building Sprint 6.
Comments
Loading comments...