Why Codapult was built around modules, adapters, explicit boundaries, and MCP context instead of another pile of starter files.
Why I built Codapult around modules, adapters, and explicit boundaries instead of another pile of starter files.
AI has changed what it means to start a SaaS project.
A few years ago, getting authentication, billing, a dashboard, email, background jobs, AI features, and deployment working together could take a large part of the early development cycle.
Now an AI coding agent can generate surprisingly large parts of that work.
That is useful.
It also changed the problem.
The difficult part is becoming less about writing the individual pieces and more about deciding how those pieces should fit together before the codebase becomes expensive to change.
An agent can add Stripe.
It can add authentication.
It can add an AI chat endpoint.
It can add a background worker.
It can add another database table.
Each change can be perfectly reasonable on its own.
The trouble starts at the seams.
Where does business logic live?
Which layer is allowed to talk to the database?
Where does provider-specific code belong?
How does billing stay independent from the rest of the application?
What happens when you decide to replace Stripe?
What happens when the application needs a real job queue instead of an in-memory one?
What does the coding agent need to know before it starts making those changes?
Those questions are architectural questions, not code-generation questions.
That is the problem I wanted Codapult to solve.
There are plenty of ways to scaffold a Next.js application.
I wasn't interested in creating another repository where you delete a few demo pages, change the logo, and start building.
I wanted the starting point to contain the infrastructure SaaS projects repeatedly end up building, while keeping the boundaries visible enough that an AI agent can work inside them.
Codapult is a Next.js 16 SaaS foundation built around that idea.
The current project includes 70+ removable modules covering things such as authentication, billing, teams, AI, admin, uploads, jobs, notifications, analytics, content, and more. Optional capabilities are structured as modules rather than being spread throughout the application.
But the number of modules isn't really the interesting part.
The interesting part is what happens to them once you start building your actual product.
One thing I deliberately didn't want was a giant abstraction layer where everything is hidden behind framework-specific magic.
The project structure is deliberately ordinary.
It's a Next.js App Router application.
There is src/app for routes, src/components for UI, src/lib for application logic, src/config for configuration, content for MDX, and infra for deployment infrastructure.
The useful part is what those directories are allowed to mean.
API routes follow a consistent flow:
auth check → rate limit → validation → business logic → response
Server actions validate input and enforce permissions.
Adapter modules expose common interfaces while keeping provider implementations behind them.
That gives an agent something much more useful than a folder naming convention: it gives it a map.
This became one of the main design principles behind Codapult.
Your product shouldn't need to know whether authentication happens through Better Auth or Kinde.
Your business logic shouldn't have Stripe calls scattered through it.
Your file handling shouldn't depend on a specific storage vendor.
Your job processing shouldn't force the rest of the application to know whether the implementation is in-memory or Redis-backed.
Codapult uses adapters for these boundaries.
Authentication, payments, storage, background jobs, notification transports, embeddings, and vector storage all have replaceable implementations selected through configuration. For example, payments can use Stripe, LemonSqueezy, or Polar, while storage can use local storage, S3, or Cloudflare R2.
The important thing isn't that switching a provider can sometimes be one environment variable.
The important thing is that the provider decision has a place to live.
That sounds small.
It isn't.
Once provider-specific APIs leak into product code, changing infrastructure becomes a product rewrite surprisingly quickly.
I would rather keep the seam from day one.
There is another distinction I cared about.
Turning a feature off with an environment variable is useful.
But it isn't the same as removing it.
Codapult has both mechanisms.
The setup wizard can remove optional modules before development begins. Removed modules can be stripped from the codebase along with their files, routes, database schema pieces, and imports. Modules that remain can still be enabled or disabled through configuration where appropriate.
That matters because a SaaS foundation should not force every future product to carry every feature forever.
A project might need teams and billing but not a blog.
Another might need AI and file uploads but no multi-tenancy.
Another might only need the public marketing surface initially.
The goal isn't maximum functionality in the final application.
The goal is a useful starting point that can become smaller as you make product decisions.
There is an obvious irony in building a SaaS foundation for AI-assisted development while treating AI as an isolated feature.
I didn't want that.
Codapult has a shared AI application layer rather than having every AI feature talk directly to a model provider in its own way.
The AI layer covers model routing, embeddings, vector storage, RAG, conversations, tools, agents, guardrails, metering, and batch processing.
The same principle applies here as it does with payments and storage.
The application should depend on a stable internal contract.
The provider should remain at the edge.
That makes it easier to change models or infrastructure later without teaching every product feature about every provider.
This is probably the part I care about most now.
An AI coding agent doesn't work in the same way a human developer does when they join a new repository.
A human can spend a few days learning the structure.
An agent generally starts by reading whatever context you give it and then making decisions from that context.
So I wanted the repository to expose its architecture explicitly.
Codapult includes AGENTS.md, CLAUDE.md, GEMINI.md, Cursor rules, and an MCP server so compatible agents can inspect structured project information instead of guessing from a handful of files.
The MCP server currently exposes 29 tools, 8 resources, and 2 prompt templates for things such as project context, configuration, schema access, checks, plugin management, code generation, and deployment readiness.
I don't see that as a replacement for good code.
I see it as making the codebase easier for an agent to understand before it starts changing it.
That distinction becomes increasingly important as coding agents take on larger tasks.
Another thing I didn't want to postpone was deployment architecture.
A starter that works only on one developer laptop is useful for getting started.
It isn't enough for a SaaS foundation.
Codapult can run on Vercel, but the project also includes Docker, AWS Terraform/Pulumi infrastructure, Kubernetes/Helm templates, external database support, background workers, storage adapters, and observability configuration.
That doesn't mean every project needs Kubernetes.
It means choosing not to use Kubernetes is a product decision, not a limitation imposed by the starter.
The same applies to databases.
The default setup can be developed locally with a SQLite file and can use Turso/libSQL or PostgreSQL for hosted environments.
Again, the point isn't supporting every possible infrastructure combination.
The point is keeping important decisions at explicit boundaries.
None of this removes the need for tests.
Codapult ships with Vitest for unit and integration tests and Playwright for end-to-end browser tests, with the testing setup already configured.
Tests answer an important question:
"Does this behavior work?"
Architecture answers another:
"Does this change still fit the system?"
I care about both.
A good SaaS foundation shouldn't try to replace either one.
After building all of this, I think of Codapult less as a boilerplate and more as a pre-built set of architectural decisions.
It gives you a working Next.js 16 application.
It gives you SaaS primitives that normally have to be assembled one by one.
It gives those primitives explicit boundaries.
It keeps infrastructure replaceable.
It lets you remove what you don't need.
It gives AI coding agents structured context about the project.
And it gives you deployment and testing infrastructure instead of leaving those decisions for some future "after MVP" phase.
The code is still yours.
You can modify it.
You can self-host it.
You can replace the providers.
You can change the architecture when your product actually requires it.
The foundation is there to make those changes controlled rather than accidental.
I don't think AI makes SaaS foundations irrelevant.
I think it makes the quality of the foundation more visible.
When writing code becomes faster, the cost of making the wrong architectural decision doesn't disappear.
It can actually become easier to accumulate.
You can generate ten thousand lines of code very quickly.
You can also generate ten thousand lines of code that all technically work but don't form a system you want to maintain.
That's why I built Codapult around the parts that are expensive to reconsider later:
boundaries, adapters, modules, infrastructure, tests, and structured context for the agents writing the code.
AI can generate the implementation.
The foundation still decides where the implementation belongs.
That's the part I wanted to make easier.
Ready to build your next SaaS with AI without drowning in architectural debt?
Explore Codapult to see how an AI-native Next.js 16 foundation keeps your modules decoupled, your adapters flexible, and your AI agents aligned with your architecture. Read the Codapult Docs or learn more about our MCP Server integration.