1
8 Comments

SaaS onboarding should be tested against account state, not just screens

The same onboarding screen can behave like two different products depending on account state.

In a first-project test, a brand-new workspace had no team. The project form required one and directed the user to create it. The team page then directed the user back to create a project.

In follow-up runs, a team existed. The form looked healthy: clear labels, valid inputs, selected team, enabled Create button. Submission still failed with a row-level-security message.

I now think SaaS onboarding tests need an account-state matrix:

  • Empty workspace, no team.
  • Workspace with a team but no project.
  • Invited member with limited permissions.
  • Visible owner or lead role.
  • Returning account with existing data.

For each state, test the first durable outcome and compare the UI's claimed capability with the backend result. If Create cannot succeed, say so before the form and give the user a recovery path.

Screen-by-screen QA would have missed the larger conflict here. The problem only appeared when prerequisites, role labels, and persistence were treated as one flow.

Which account state causes the most support tickets in your SaaS?

on August 5, 2026
  1. 1

    You framed the snapshot fields exactly right. We capture on both paths. Errors get the full rejection context, but the successful control path is logged too, so the diff shows what a healthy run looks like versus a blocked one. That contrast is what makes a silent failure obvious. One thing we have learned is the correlation ID matters more than we expected. It lets support tie a complaint back to the exact state without exposing row data. How are you planning to use the matrix entries once support has them. Are they feeding a fix queue or just visibility.

  2. 1

    This is an important distinction. A clean screen-by-screen walkthrough misses empty accounts, partial imports, expired trials, permission differences, and returning users. A small state matrix usually finds more real failures than another round of visual QA.

  3. 1

    Invited member with limited permissions matches what we have seen too, but the twist in our data was that the owner account produced the quiet tickets. The owner hits the same permission wall, assumes it is a temporary glitch, and retries for days instead of reporting it, so it never surfaces until churn review. Have you considered logging the account state at the moment of a failed action, so support can see the state matrix entry instead of the screenshot?

    1. 1

      That owner behavior is a useful blind spot: higher-confidence users may normalize the failure and retry instead of reporting it, so ticket volume understates the problem.

      Yes, logging a state snapshot at the failed action seems stronger than relying on screenshots alone. I’d want the role/capabilities the UI believed, the backend rejection, prerequisite state, account age, and a correlation ID — but no sensitive row data. Then support can map the failure directly to a matrix entry.

      Do you capture that snapshot only on errors, or also on the successful control path so you can diff the two states?

  4. 1

    The account-state matrix is the right frame, and the sharpest item is the RLS failure: UI says Create is enabled, backend refuses. That's the dangerous class, because the UI is lying with full confidence. Screen QA can't catch it, the screen looks healthy, and the truth only lives in the gap between claimed capability and backend result.

    The state that generates the most tickets, in my experience, is invited-member-with-limited-permissions. The owner who set everything up never sees the walls, so they go untested, and the invitee hits a dead end the builder never hit. Permission states break for exactly the people who didn't configure them.

    Which state surprised you most running the matrix?

    1. 1

      The most surprising state was the one that looked most privileged: the UI showed the user as workspace Owner and team Lead, the form was valid, and Create was enabled, yet the insert still failed with an RLS error.

      The empty-workspace loop was obvious. The owner/lead mismatch was more dangerous because the interface looked healthy right up until persistence failed.

      1. 1

        The most dangerous class precisely because it inverts where you look. Everyone tests restricted states expecting friction. Nobody stress-tests the account that looks fully privileged, because the UI's confidence is highest exactly there, inversely correlated with where the gap hides. The healthier it looks, the less anyone verifies persistence behind it.

        Root cause is usually the UI check and RLS reading from different sources: frontend computes "can create" from role labels, backend enforces from actual row policies, and they drift. Owner/lead exposes it because that's where the frontend is most sure and the policy most likely has an unhandled edge.

        Are your UI check and RLS reading from one source, or computed separately?

        1. 1

          I can’t tell from the replay whether that product’s UI check and RLS are derived from one source or computed separately, so I don’t want to claim a backend root cause from external evidence alone.

          What the run does prove is a contract mismatch: the UI asserted that Create was available, while persistence rejected the same action. For Menso, that boundary matters — report the observable mismatch, verify the durable outcome, and keep implementation diagnosis clearly labeled as a hypothesis.

          If you were designing the fix, would you expose a single capability API to the UI and tests, or generate both from the policy definitions?