Recently I worked on a feature that didn’t have any UI. When a customer is on a payment plan, their account has a due date, and the plan has its own payment schedule. The feature kept those two in sync. A job ran every night, looked at the plan and any payments that had come in, and updated the due date on the account.
Most of the time when I’m building a shared vision on a team, I lean on designs. We put a wireframe or a prototype in front of stakeholders. With this feature, there wasn’t anything to put in front of anyone. I could describe the rule, and everyone in the room would agree with it, but I didn’t really trust that we all meant the same thing.
I’ve written before about using a product requirements document on this same feature. The PRD got us to agree on the problem we were solving. To work out the actual requirements, I used a scenario table.
A scenario table is a list of situations.
Each row is one situation the system has to handle and what the product should do when it happens. The table had columns for the scenario name, the trigger, the expected behavior, a status, and any open question with the person who could answer it.
I started by reading the existing code. It already handled payments, overdue statuses, and plan balances. That told me a lot about which situations were real. Some rows I could mark as confirmed in code right away. Others I flagged because the code did something I didn’t expect.
Then I added the situations nobody had written down yet. What happens if someone pays ahead? What if a payment gets reversed? What if two accounts share a plan and one is paid off? I didn’t know the answers to most of those yet, so they went in as open questions. The table ended up at a couple dozen rows.
I rewrote the table with my audience in mind.
A scenario table only helps build a shared vision if the people who need to agree with it can read it. My first draft was accurate, but I wrote it in a pretty technical way and didn’t keep the end users of the table in mind. Here’s how one row read:
Balance calc nets the running total; no per-installment threshold. Any shortfall that leaves balance > 0 for the period keeps the account behind.
A developer can check that against the code. But the people who needed to agree with it were the client’s operations staff and product leads. They know the business really well. Asking them to read balance math made it harder for them to tell me when I had it wrong.
So I rewrote it for them. I kept the technical version as an appendix for the dev team, and the plain version became the one we used in meetings. That row became: “A short payment still leaves them behind. The due date holds where they fell behind.” The rule is the same, but someone on the operations team could now tell me whether it was right.
I also regrouped the rows. The technical version listed them in the order I found them. For the rewrite, I grouped them by situations the staff already knew, like paying ahead, falling behind and catching up, or the plan changing. That helped a lot more than I thought it would. With related rows next to each other, it was easier to identify gaps. If there’s a row for missing a payment, people naturally ask what happens when the person catches back up.
Every row also got a status: settled, needs a decision, or needs someone to verify what the code already does. In meetings we could skip the settled rows and spend our time on the open ones.
Reading it aloud revealed more requirements.
We went through the table row by row with the client’s product leads. It was slow, but almost every session turned up something. A few examples:
- Everyone assumed paying ahead would move the due date forward. The code didn’t do that. That row went from obvious to new work pretty quickly.
- We assumed there was a way to close a plan. There wasn’t, so a closed plan with a balance would keep showing as overdue. That turned into a decision for the client’s leadership.
- When two accounts shared a plan, the system only knew when the whole plan was done. An account that was paid off would keep getting new due dates.
- A due date shouldn’t land on a weekend or a holiday. Figuring out how to handle that took a spike.
The table also helped us close things out. In one session, we answered several policy questions that had been open for weeks, and once they were marked settled, nobody reopened them.
The table kept being useful after the meetings.
The rows gave us a good starting point for writing stories. It wasn’t one row to one story, though. Some stories ended up touching several rows. I used an AI assistant to draft the stories and an import file for our tracker, and then edited them from there.
QA walked the same rows during testing. When a due date came out a day off, we could point to the row that said what it should have been.
Before the pilot, the client asked for something to help train their support staff. The plain-language table was most of the way there. We reorganized it around what they would actually see and added a section on behavior that looks wrong but isn’t.
Try it on your next feature without a UI.
I think scenario tables are most useful when the feature is a rule more than a screen. Background jobs, syncs, calculations, and notifications are all good candidates.
If I did this again, I’d still start from the code, keep a technical and a plain version, and group rows by what’s happening to the user. I’d also keep the table in one shared place. Ours lived on a board everyone on the team could edit, and the copies I exported tended to drift in confusing ways.