Most SaaS products I have seen fail did not fail on code quality. They failed because the team confused activity with progress.
Another settings screen feels like shipping. A third integration feels responsible. Meanwhile nobody can describe the loop a paying customer runs every week.
This guide is how I approach SaaS app development when the goal is an MVP that can become a real product, not a demo that collects dust.
SaaS is a workflow business
A subscription app is not a website with login added later. You are selling a repeated job done reliably: approvals, inventory, deal tracking, content publishing, whatever the customer hires you for.
That means auth, account boundaries, billing state, onboarding, admin visibility, and a roadmap driven by what people actually do in the product, not what you guessed in a Notion doc.
Layers every serious SaaS product needs
The best early SaaS feels narrow. One painful job, end to end.
Name the workflow before the feature list
"Weak idea" example: project management for small businesses.
Stronger version: client approval for agencies tired of email threads, vague feedback, and lost sign-off.
Write the loop as steps:
- User shows up with a specific problem.
- They create or import the thing they manage.
- They pull in whoever else is involved.
- They finish the main action.
- They have proof it worked.
- They have a reason to come back Thursday.
The six-step SaaS value loop
If you cannot write that loop on one page, you are probably building a feature collection.
Cut scope without cutting trust
I am aggressive about deferring: advanced reporting, theme pickers, public APIs, white-label, custom domains, automation builders with twelve triggers.
I am conservative about: login and recovery, who owns data, whether billing matches Stripe, permission boundaries, email delivery, backups, and what happens when a background job fails.
On Zesty a broken invite or wrong stock count during service is not a polish issue. It is a trust issue. Reliability is UX.
What I defer vs what I protect
- Advanced reporting
- Multiple themes
- Deep integrations
- Granular role systems
- Public APIs
- White labeling
- Login and account recovery
- Data ownership and permissions
- Billing accuracy
- Core workflow reliability
- Transactional email delivery
- Error handling and backups
Pick boring architecture
Early stack decisions should optimize for "can we operate this on a small team?" not "will this look good in a diagram?"
Typical MVP for me: one web app, Postgres, proven auth, server-side business rules, a job queue, transactional email, Stripe (or similar), Sentry or equivalent, a thin admin view for support.
You do not need microservices at month two. You need logs when webhooks stop firing.
Model the business, not just the UI
Even simple B2B SaaS needs clear entities. I sketch these before screens:
Entities I define early
User
Person who logs in
One record per human
Account / org
Billing and ownership boundary
Even solo products benefit
Workspace / project
Where work happens
Keeps teams sane later
Membership
Links users to accounts
Required before invites
Role / permission
What a member can do
Start with two or three roles
Subscription
Commercial state
Model lifecycle, not is_pro
Plan
Limits and packaging
Obvious upgrade path
Usage event
Metered activity
Only if pricing needs it
is_pro is not a billing system. Real states include trialing, active, past_due, canceled, unpaid, paused, incomplete. I learned that the hard way on products with tiered plans.
Permissions are a server problem
Sign-in working is table stakes. The bug that kills you is User A seeing User B's workspace because a query forgot account_id.
Hide buttons in the client if you want. Security lives in the API.
Questions I answer before invites ship
- Can one user belong to multiple orgs?
- Who can invite members?
- Who can change billing?
- What happens to access when someone is removed?
- Are deletes soft, hard, or recoverable by support?
Billing is a product surface, not a checkout button
Pricing page, trial rules, checkout, failed payments, plan changes, cancellations, receipts, and what your support team sees when someone writes "I paid but I'm locked out."
Subscription billing lifecycle states
Checkout success is not subscription truth. Webhooks are. If you do not handle retries and idempotency, you will reconcile subscriptions manually at 11pm.
Onboarding should end in value, not a tour
Users do not need a carousel of your navigation. They need to finish one useful thing. Measure activation: did they complete the core action, not did they click through five modals.
Build admin tools earlier than feels comfortable
The moment real users arrive, support needs answers: who are they, what plan, did webhooks land, did jobs fail. If every answer requires a SQL query, you will hate your launch week.
Track events you will actually read
Signup, first core action, trial conversion, cancel, repeat use of the feature you care about. Five meaningful events beat two hundred you never open.
SEO should match the product story
Content works when it answers problems your buyers already Google. Problem guides, workflow posts, comparisons, templates. Pair product work with writing that comes from building, like our notes on using AI without generic output.
Traps I see on almost every MVP scoping call
Buyer is everyone
Problem: Weak positioning and pricing
Better move: Name a role, company size, and painful Tuesday
Roadmap is feature requests
Problem: Noise instead of learning
Better move: Watch hesitation, support, and drop-off
Design as decoration
Problem: Pretty but confusing
Better move: Clarity, status, next action
Happy-path testing only
Problem: Silent production failures
Better move: Test webhooks, retries, billing twice
Copying mature competitors
Problem: Bloated MVP
Better move: One workflow, end to end
Lock down access before strangers pay
Once money is involved, assume someone will poke at your API with two browser tabs and a copied URL. The usual failure is not exotic hacking. It is broken access control: User A changes an ID in the request and reads User B's data.
My pre-launch bar here is simple. Every protected route checks auth on the server. Every route that fetches or updates a record by ID checks that the current user is allowed to touch that record. Never trust userId, orgId, role, or plan from the client. Use shared helpers (requireMember, requireOwner, canAccessResource) instead of one-off checks that drift over time.
Before launch I create two normal users, two organizations, one admin, one suspended account, and one expired subscription. Then I try to break things on purpose: swap IDs, reuse old sessions, call paid endpoints after canceling, replay webhooks, guess file download links.
Passwords belong in slow hashes, not home-grown schemes. Sessions should rotate after login and privilege changes. Cookies need HttpOnly, Secure, and a sensible SameSite policy. If you store tokens where JavaScript can read them, you have made XSS an account-takeover problem.
Validate request bodies, query params, and webhook payloads with schemas. Return safe errors. Stack traces, SQL fragments, and internal bucket names belong in logs, not API responses.
Access control checks I run manually
Authorization
- User A cannot read, update, or delete User B's records by changing IDs
- Org 1 member cannot access Org 2 data through search, export, or pagination
- Suspended or removed users lose access even with an old session cookie
- Expired or unpaid subscriptions cannot unlock paid features from the frontend alone
Input and errors
- No raw request body passed straight into database updates
- Sort, filter, and pagination fields use allowlists
- User-generated HTML or Markdown is sanitized before render
- API errors are consistent and do not leak internals
Use OWASP ASVS as a verification checklist if you want structure. You do not need to memorize every control. You do need to prove tenant isolation and payment gates before charging.
Rate limits, quotas, and abuse controls
Rate limiting is not a growth hack blocker. It is how your app survives launch day, credential stuffing, and that one customer who writes a script against your export endpoint.
I layer limits rather than picking one global number:
- per IP for anonymous traffic
- per authenticated user
- per organization or account
- per sensitive route (login, signup, password reset, email verification, upload, export, expensive AI calls)
Login throttling should track the account, not only the IP. Distributed guessing is common. After enough failures, add delay, temporary lock, or CAPTCHA. Password reset and email verification deserve tight limits too, or you become someone else's spam cannon.
Separate rate limits from quotas. Limits stop bursts. Quotas enforce plan packaging (storage, seats, monthly exports). Both should be server-side. The UI is just a mirror.
Decide what happens when your limiter store is down. Read-only marketing pages can fail open with a CDN fallback. Login abuse protection, payments, and destructive actions should fail closed or queue for review, not silently allow unlimited tries.
Keep databases, caches, and object storage off the public internet. If Redis or Postgres is reachable from the world, that is a launch blocker, not a todo for later.
Abuse controls worth shipping early
- Login, signup, and password reset are throttled and monitored
- File upload and download routes have size limits and per-account quotas
- Export, search, and heavy API routes have stricter caps than normal reads
- Webhook handlers verify signatures and reject replays
- Alerts exist for login spikes, webhook failures, and 5xx jumps
Legal pages and launch paperwork
Legal pages are boring until a customer, payment provider, or enterprise procurement team asks for them. I treat them as part of production readiness, not marketing polish.
At minimum, before you take payment from strangers:
- Privacy policy that matches reality: what you collect, why, how long you keep it, who processes it (email, analytics, payments, AI vendors), and how users request deletion or export.
- Terms of service covering acceptable use, account termination, limitation of liability, and how disputes are handled.
- Refund and cancellation terms aligned with your billing provider and what support can actually do.
- A visible support or contact path. Hiding behind a form with no SLA still needs a human escalation route.
If you use analytics cookies, marketing pixels, or embedded third-party scripts, add a cookie notice that matches your jurisdiction. If you sell to businesses in the EU or UK, expect questions about a DPA and subprocessors. If the product runs AI on customer content, say so plainly in the privacy policy and terms, not buried in a changelog.
I am not a lawyer. Templates and AI drafts can speed up a first pass. A real review for your entity, markets, and data flows is still worth it before you scale.
Legal and trust surface before go-live
- Privacy policy linked from signup, footer, and checkout
- Terms of service linked wherever users create an account or pay
- Refund, cancellation, and trial language matches Stripe (or your provider) settings
- Cookie and analytics disclosure matches what is actually installed
- Support email or contact route is monitored by a human
- Enterprise buyers can find security, data handling, and subprocessors without a scavenger hunt
Stay up when things break
Security is not the whole story. Launch week also tests whether your app survives slow queries, stuck jobs, and a payment webhook that retries four times.
Before go-live I want health checks that fail when the database is down, background jobs that retry safely, a dead-letter path for failures you need to inspect, and backups someone has actually restored. Alerts for 5xx spikes, webhook errors, and failed backups matter more than a perfect uptime badge on day one.
You do not need a platform team on day one. You do need to know when the app is on fire without a customer tweeting about it first.
Production-ready means strangers can use it
Not perfect. Not feature-complete. Safe enough that you are not babysitting every signup, and honest enough that legal and security questions have real answers.
Bar I use before launch
Product and billing
- Sign up, sign in, reset password all work on mobile
- A real user can finish the core workflow start to finish
- Webhooks update subscription state; failures are visible
- Critical email sends; bounces do not disappear
Security and operations
- Cross-account data access is impossible, not unlikely
- Rate limits protect login, reset, upload, and expensive routes
- Backups exist and someone has tried a restore
- Errors in monitoring include enough context to debug without exposing secrets to users
- Support can answer plan and status without opening raw database tables
Legal and trust
- Privacy policy and terms match what the product actually does
- Checkout and onboarding link to the policies users are accepting
- Refund and cancellation rules are documented and support-ready
A roadmap that matches how I actually ship
Six-phase SaaS development roadmap
Validate
ICP, painful job, first value moment
Core loop
One workflow works end to end
Trust systems
Safe for paying strangers
Launch narrow
Usage data and support signal
Positioning
Homepage, onboarding, content aligned
Repeat what works
Integrations, automation, acquisition
The advantage is restraint
The SaaS apps worth building understand one painful job and make it easier every week. Engineering discipline plus product restraint beats a long feature list.
See how that shows up across client and in-house work on the work page, or contact me if you want help scoping an MVP you intend to ship.