Back to Blog
Products

Inside ShipKit: The Architecture of a Production-Ready SaaS Backend

When we launched ShipKit, we described what it does: ship a SaaS backend with auth, billing, admin, and deploy already wired in. That post answered what. This one answers how. The architecture decisions inside a boilerplate are the ones you inherit for the entire life of your product — how secrets rotate, what happens when Stripe sends a duplicate webhook, how a request gets from nginx to your handler. Those decisions deserve daylight.

So here is the honest engineering tour: the choices baked into ShipKit, why we made them, and the trade-offs each one represents. Every pattern here exists because a real product needed it and a real deployment validated it.

The Stack, Chosen Deliberately

ShipKit is built on FastAPI — Python's typed async framework — with MySQL via SQLAlchemy for persistence. Both choices are boring, and that is the point. A SaaS backend should be built on infrastructure your future self can hire for, your hosting provider has supported for a decade, and your stack-overflow searches return answers for. The interesting work belongs in your product, not in justifying a novel database.

FastAPI specifically pulls weight in three ways: end-to-end typing (which makes refactors safe instead of terrifying), async by default (so a Stripe webhook handler does not block while another request is in flight), and Pydantic schemas (which double as request validation and API documentation). MySQL via SQLAlchemy gives you typed ORM models without giving up the ability to drop into raw SQL when you need to.

Boring on Purpose

Mature Tools, Tested Patterns

ShipKit avoids framework magic that breaks under load and clever abstractions that look elegant in a demo and ugly in production. The patterns inside are the ones that survived five years of running across six different products — the rest got removed.

Dual-Key JWT Rotation

Authentication uses JWTs — signed tokens that let your API verify a user without a database round-trip on every request. The interesting design choice is what happens when you need to rotate the signing secret, which sounds like an edge case until the first time you accidentally commit a key to git and need to fix it without logging out every user.

ShipKit's JWT implementation supports a primary and a fallback secret key. New tokens are signed with the primary; verification accepts either. When you rotate, you promote the primary to fallback and write a new primary — existing tokens keep working while new ones are signed with the new key. Once the old tokens have expired naturally, you retire the fallback. No mass logout, no emergency deployment, no support fire-drill.

It is a small pattern that turns a class of incidents into a routine config change. Most boilerplates skip it because rotation is rare. The day you need it, you want it already there.

Idempotent Stripe Webhooks

Payment integrations look simple from the outside — Stripe sends a webhook, you grant access. The reality is that Stripe sometimes sends the same webhook twice, or three times, or the network drops mid-handler, or Stripe retries because your last response timed out. A webhook handler that processes the same event twice can double-grant access, double-refund, or, worst, double-charge a customer in a way that takes hours to clean up.

ShipKit's webhook handler does four things in order:

1

Verify Signature

Validate the Stripe-Signature header against your webhook secret — before parsing anything else.

2

Deduplicate

Check the event's id against a processed-events table. If we have seen it, return 200 and skip.

3

Process With Retry

Execute the business logic with bounded retries on transient failures. Successful work is committed before the response.

4

Dead-Letter on Failure

Events that exhaust retries land in a dead-letter state for manual review rather than being silently lost.

That sequence is unspectacular and absolutely essential. It is the same handler that processes real transactions across Wigley Studios products, and it is the difference between a billing integration you can sleep through and one that wakes you at 3 a.m.

Admin Panel Foundation

Every real SaaS needs an admin surface eventually — somewhere to look up a customer, refund an order, toggle a feature flag, or investigate a support ticket. ShipKit ships the scaffolding for that surface rather than a finished product: session-based admin auth, customer management views, and a router pattern for adding product-specific pages without fighting the framework.

The reason this is scaffolding rather than a full UI is that every product needs different admin pages, and we have learned not to predict yours. What is consistent — the auth layer, the layout structure, the “impersonate a user” pattern for support, the audit-friendly action logging — is what ShipKit provides. The pages you add for your product slot in cleanly.

Project Structure That Scales With You

A boilerplate that looks great empty and falls apart at twenty endpoints is not actually saving anyone time. ShipKit's project layout follows FastAPI's modular routing conventions — each domain (auth, billing, admin, etc.) lives in its own module with its own router, models, schemas, and services. Cross-cutting concerns (database session, current user, settings) come through FastAPI's dependency injection.

Lifecycle

One Place Where the App Starts and Stops

ShipKit uses FastAPI's lifespan context to centralize startup (database connection pool, Redis on Pro, background workers) and shutdown. You never have to hunt through five files to find where a connection gets opened. Cold-start order is explicit, which makes debugging deploys boring — the good kind.

What Professional Adds

Starter gives you the backend. Professional adds the operational layer that turns a backend into a running service. Each addition reflects something we needed in production and got tired of rebuilding from scratch:

Capability Starter Professional
VPS deploy scripts (systemd + nginx)
CI/CD pipeline templates
Multi-tenant scaffolding
Redis caching & session helpers
Background worker queues
Rate limiting middleware
Transactional email integration
GitHub repo access for updates

The deploy scripts deserve a specific note: they are not generic templates. The systemd service definitions and nginx reverse-proxy configurations are the actual files running ShipKit's own production, with connection pool sizing, TLS termination, and health-check endpoints already configured. The first SaaS most developers ship has a deploy story that takes a weekend to figure out; ShipKit's Professional tier compresses that into a checklist.

If multi-tenancy specifically is on your roadmap — one application serving many isolated customers — our companion deep-dive on how Chiave does multi-tenant licensing walks through the exact pattern ShipKit's scaffolding sets you up for.

What Was Removed

A boilerplate is shaped as much by what is missing as by what is included. Things that did not survive five years and six products got cut, even when the original code was technically working:

That editorial discipline is the part you cannot see on the file tree. It is the difference between a boilerplate that survives launch day and one you start rewriting before your first paying customer arrives.

Built From Six Shipped Apps

ShipKit is not a framework designed in isolation. The architecture was extracted from the backends shared across six Wigley Studios products: PromptUI, PicSift, PostPilot, ShipKit itself, Chiave, and the Developer Labs platform. Every pattern in the box exists because a real product depended on it and a real deployment proved it — not because a blog post recommended it.

What This Means in Practice

You are not starting from a tutorial. You are starting from the architecture of products that process real Stripe transactions, serve real API traffic, and run on real infrastructure. The gap between “boilerplate code” and “production code” is already closed when you clone the repo.

Why This Matters for You

The decision most early SaaS founders face is not which backend to write — it is whether to spend the next three weeks writing one or to start on the part that makes your product actually different. ShipKit collapses that decision. The patterns above are not optional sophistication; they are what production looks like for any SaaS that lasts. You can build them yourself, slowly, and learn each one when it breaks. Or you can inherit them on day one and spend that time on the work that only you can do.

A Backend You Don't Have to Build

FastAPI, JWT with rotation, Stripe with idempotency, admin scaffolding, and deploy scripts — one-time purchase, perpetual access, extracted from six shipped products.

Explore ShipKit
BW

Brandon Wigley

Founder of Wigley Studios. Building developer tools since 2018.

Previous: Junior Devs in the AI Age All Articles