The SaaS Factory: Bootstrapping Products Faster with Monorepos
I got tired of setting up the same infrastructure for every new project. Here is how I turned my workflow into an assembly line.

For years, I'd start a new app and end up solving the same problems from scratch. I was reinventing logic differently every time because I didn't have a reliable foundation to build on. I'd spend weeks on infrastructure before writing a single line of product code. It wasn't just slow—it was exhausting.
I've moved away from this "start-from-scratch" mentality. Instead, I treat my entire setup as a single machine: a **SaaS Factory**. By using a monorepo, I've turned those weeks of setup into minutes of configuration.
The Journey to the Factory
This approach didn't happen overnight. It's the result of building dozens of apps over the years, many of which never really went anywhere. I'm still in the middle of it—trying to find that one SaaS that pays the bills and feels right to work on.
Along the way, I've spent a lot of time in big companies as a software engineer. Working on enterprise systems taught me which tools are reliable and which ones just add noise. I've taken those enterprise patterns and tech choices and simplified them into something I can actually use alone.
The monorepo is the core of the factory. It's the one place where all these pieces live together. These days, I don't start from zero. I just add a new folder to the factory. It's how I build everything now.
Separating Implementation from Logic
The system works because of a strict separation between implementation and logic. My monorepo is divided into two primary directories:
- apps/: This is where the products live. Each folder is a deployable application—a Next.js dashboard, a React Native mobile app, or a documentation site. These are focused solely on user experience and routing.
- packages/: This is where the core logic lives. It contains shared libraries like the UI component library, the database SDK, and internal tooling. Any app can import these locally without needing to republish to a registry.
Anatomy of an App
Inside the `apps/` directory, each project follows a predictable, modular structure. I don't believe in monolithic "all-in-one" frameworks for complex products.
frontend/
A Next.js or React application dedicated purely to the UI and client-side state.
backend/
A dedicated FastAPI service handling the core business logic and data processing.
supabase/ or infra/
Where the plumbing lives. `supabase/` handles SQL migrations and edge functions, while `infra/` manages local or cloud setups (AWS/GCP) via SST or Terraform.
Makefile
The glue that standardizes commands like `make dev` or `make deploy`.
Anatomy of Packages
The `packages/` directory is where I store everything that can be reused. Instead of copy-pasting code, I build internal libraries that every app can import. Here's a quick look at the core of my shared infrastructure:
supabase-sdk
This is the source of truth for my data layer. It provides a set of shared utilities and clients pre-configured for Next.js environments (SSR, Middleware, and Client Components). By centralizing authentication guards, global state listeners, and storage hooks here, every app in the monorepo handles identity and data consistently without redundant setup.
python/payment
Handling money is one of those things you don't want to mess up. This is a dedicated Python package for managing payment lifecycles and subscriptions. It implements a **Factory Pattern** to support multiple payment providers. While I primarily use Stripe, the abstraction allows me to switch or add providers by simply adding a new gateway implementation, keeping the core business logic in my apps completely decoupled from the specific payment API.
Why FastAPI over a Next.js BFF?
A common question is: "Why not just use Next.js API routes or a BFF (Backend-for-Frontend)?" While Next.js works well for the web, I prefer keeping my core logic in **FastAPI** for a few specific reasons:
Comfort and Productivity
Honestly, I'm simply more comfortable building production-ready backends with Python and FastAPI. I prefer the clean, readable syntax of Python over TypeScript for complex business logic. When I return to my own code months later, I find FastAPI services significantly easier to read and maintain than their TypeScript counterparts.
The developer experience—especially with Pydantic for data validation and automatic OpenAPI documentation—is hard to beat when building complex systems.
True Separation of Concerns
By decoupling the backend from the frontend framework, the business logic isn't tied to the web's lifecycle. This makes it straightforward to share the same API between a web dashboard, a mobile app, and even background worker scripts without duplication.
Performance and Async Support
FastAPI is built on top of Starlette and Pydantic, making it one of the fastest Python frameworks available. Its native support for `async/await` and easy integration with heavy data processing libraries (like those in the Python ecosystem) gives it an advantage over standard Node.js API routes for resource-intensive tasks.
The Trade-off: Deployment Complexity
Of course, there's one major caveat: I can't just hit "deploy" on Vercel for the entire stack. While Next.js makes deployment straightforward, a FastAPI backend requires a dedicated provider like AWS, Google Cloud, or DigitalOcean.
For many SaaS apps, staying entirely within the Supabase ecosystem is more than enough. However, because I prefer the control and maintainability of a FastAPI backend, I choose to manage the additional infrastructure complexity on AWS. For me, that trade-off is worth it for the long-term health of the codebase.
Keeping Mobile and Web in Sync
My mobile applications aren't isolated islands. In this monorepo, the React Native and Flutter apps live right next to the web apps.
When I update a validation schema or a business rule in a shared package, both the web and mobile apps get that update instantly. There's no "syncing" repositories or wondering if the iOS app is using the latest API types. The monorepo enforces that synchronization by design.
The Workflow in Practice
The "factory" approach pays for itself the moment I start a second or third project. It's no longer about starting from zero; it's about assembling existing components into a new configuration.
The process for every new app is straightforward:
- Bootstrap in minutes: I create a new directory in `apps/` and mirror the standard structure—`frontend/`, `backend/`, and `infra/`.
- Using Shared Packages: Instead of building a new UI or auth system, I simply import `@repo/ui` and `@repo/supabase-sdk`. These provide the stuff I've already debugged and improved across other projects.
- Plugging in Logic: If the app needs payments, I plug in the existing `python/payment` package. If it needs specialized processing, I pull from my collection of shared utilities.
Because the "plumbing"—the auth, the UI, the database, the payment lifecycle—is already solved in the `packages/` directory, I can focus 100% of my energy on the actual features that matter.
Technical Agility: Failing Fast
While this setup makes it easier to bootstrap, it also makes it easier to fail. I don't build every feature on day one.
Because the core business logic is centralized in shared packages, tearing down an app that doesn't work out is as simple as deleting a folder in `apps/`. The valuable logic remains in the `packages/` directory, ready to be reused in the next experiment. This architecture doesn't just help me build faster; it allows me to pivot without losing the technical progress I've made.
A monorepo is a multiplier. It lets you build the hard stuff once and reuse it everywhere. If you're tired of the "Day 0" setup grind, it might be time to stop building apps and start building your own factory.
Read More from the Author
The Technical Debt of AI Speed
AI helps us ship faster than ever, but it's also creating a new kind of technical debt. Here is how to manage the speed without losing quality.
Why "Just Vibing" Isn't Enough: The Case for Guided Vibe Coding
AI code generation is fast, but without guidance, it lacks taste and optimization. Learn why you must be the Engineer, not just the pilot.
Why I Chose Mac Over Windows for Programming
A developer's journey through OS wars, from Windows to Ubuntu, and why I finally settled on the Apple ecosystem.