AI Can Write the SaaS. It Still Doesn't Decide Where the Code Belongs.
What AI coding agents change about SaaS architecture, and why modules, adapters, boundaries, and structured context still matter. AI has changed how quickly you can start a SaaS project. Authentication, billing, dashbo
What AI coding agents change about SaaS architecture, and why modules, adapters, boundaries, and structured context still matter.
AI has changed how quickly you can start a SaaS project.
Authentication, billing, dashboards, email, background jobs, AI features, and deployment used to consume a large part of the early development cycle. Today, an AI coding agent can generate surprisingly large parts of that work in a short time.
That is useful.
But it changes where the difficult decisions are.
The problem is becoming less about writing individual pieces and more about deciding how those pieces 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 reasonable on its own.
The trouble usually starts at the seams.
Where does business logic live?
Which layer can access the database?
Where does provider-specific code belong?
How does billing stay isolated from the rest of the application?
What happens when you replace a provider?
What does the agent need to know before it starts changing any of this?
Those are architecture questions. Faster code generation does not make them disappear.
The seams are where the cost appears
Imagine a SaaS application where a route calls a service, the service talks to a repository, and the repository talks to the database.
That structure is easy enough to understand.
Now add a payment provider.
Then an authentication provider.
Then object storage.
Then background jobs.
Then embeddings and vector search.
Without explicit boundaries, provider-specific code tends to leak into the product layer. A Stripe call appears in business logic. A storage SDK gets imported directly from a feature. A queue implementation becomes part of an application service.
The application still works.
But the next change becomes more expensive.
This is why architecture often feels fine at the beginning and painful later. The first implementation is cheap. The seams you created around it are not.
I started thinking about Codapult around this problem.
Modules are more useful when they have a boundary
A SaaS foundation can expose a long list of features.
That list is not the interesting part.
The useful question is whether those features can exist without turning the whole application into one interconnected feature set.
Codapult organizes its capabilities as modules with their own routes, components, code, and database pieces. The setup wizard can permanently remove modules you don't need, including their files, routes, schema pieces, and imports. Modules that remain can also be disabled through configuration when you want to keep the code but not expose the feature yet. Codapult module architecture
That gives the project two useful properties:
- Flexible Starting Surface: You can start with a broad foundation.
- Selective Complexity: You can make the resulting application smaller as your product decisions become clearer.
For example, one product may need teams, billing, and admin features but no blog. Another may need AI and file uploads but no multi-tenancy. Another may begin as a simple public application and add account features later.
The point is not to ship every module.
The point is to have a structured starting point without creating a permanent dependency on everything that came with it.
Adapters keep infrastructure at the edge
The same idea applies to providers.
Business logic should not need to know which vendor handles authentication, billing, storage, or another infrastructure concern.
A provider is an implementation detail.
That is the reason for adapter boundaries.
Instead of allowing product code to call Stripe APIs throughout the application, the product talks to an internal contract. The Stripe implementation lives at the edge of that contract.
The same model can be used for authentication, storage, background jobs, notifications, embeddings, and vector storage.
Codapult currently provides replaceable implementations for several of these boundaries, including Better Auth and Kinde for authentication, Stripe, Polar, and Lemon Squeezy for billing, PostgreSQL and Turso for databases, and S3, R2, and local storage for files. Codapult architecture
The benefit is bigger than being able to change an environment variable.
The important decision is that the provider has somewhere to live.
If provider-specific APIs leak into product code, replacing infrastructure becomes a search-and-rewrite exercise across the application.
If the seam exists from the start, changing the infrastructure is a much more contained change.
I would rather make that boundary explicit before I need it.
A disabled feature and a removed feature are not the same
There is another architectural distinction that becomes more important as a codebase grows.
Turning something off is reversible.
Removing it changes the shape of the project.
Codapult supports both.
A module can remain in the repository while being disabled through an environment flag. Or the setup wizard can remove it entirely.
That means the foundation does not have to make a permanent decision for you.
You can keep a capability when you're uncertain.
You can remove one when you're sure it is outside the product's scope.
That sounds like a small implementation detail, but it has a practical effect: the codebase can become simpler as the product becomes more specific.
A foundation should help that happen.
AI needs an architectural interface too
There is an obvious problem with building a foundation for AI-assisted development while leaving the agent to reverse-engineer the repository from files.
A human developer joining a new project can spend days learning its structure.
An agent usually starts from the context it is given.
That makes structured context much more important.
Codapult includes AGENTS.md, CLAUDE.md, GEMINI.md, Cursor rules, and a Codapult MCP server. The MCP layer exposes structured information about the project's architecture, database schema, environment, installed modules, deployment state, conventions, and validation through 29 tools, 8 resources, and 2 prompt templates. Codapult MCP
The goal is not to make the agent smarter.
It is to give the agent a better map before it starts changing the code.
That distinction matters.
An agent can generate correct code from incomplete context.
It can also generate code that is perfectly valid in isolation and completely wrong for the system around it.
Context is useful, but context is not enforcement
There is an important limit here.
Documentation and agent instructions can explain what the project expects.
They do not guarantee that a future change will follow those expectations.
That is why I don't think architecture can be reduced to a collection of prompt instructions.
An agent can read a convention correctly and still implement the wrong dependency.
It can follow the general direction of a layer and still call a component that should not be reachable from a particular boundary.
It can reuse an existing pattern that happens to be wrong for the new feature.
The foundation therefore needs more than instructions.
It needs explicit seams that make the intended structure understandable to both humans and agents.
And the project still needs verification on top of that.
Production architecture is part of the decision
The same principle applies to deployment.
A starter that runs on one laptop is enough to begin coding.
A SaaS foundation has to answer a different question: what happens when the application has to become an actual system?
Codapult supports Vercel, but also includes Docker, AWS Terraform and Pulumi templates, Kubernetes/Helm deployment paths, external databases, background workers, storage adapters, and observability configuration. Codapult self-hosting
That does not mean every project needs Kubernetes.
It means Kubernetes is a choice rather than a limitation of the starter.
The same reasoning applies to databases, storage, and deployment providers.
The architecture should define the seam.
The product should decide what goes behind it.
Tests answer one question. Architecture answers another.
Tests are still essential.
Unit and integration tests tell you whether particular behavior works.
End-to-end tests tell you whether larger user flows work.
Type checking and linting catch other classes of problems.
But none of those automatically answer a different question:
Does this change still fit the system?
A change can preserve behavior while making the architecture worse.
A service can start importing something from a layer it was never supposed to know about.
A feature can bypass an existing application boundary.
A provider SDK can slowly spread into product code.
The application can still compile and the tests can still pass.
That is not an argument against testing.
It is an argument for treating architectural constraints as a separate concern.
What I wanted from Codapult
This is why I didn't want Codapult to be another starter where the main feature is the number of files included on day one.
The useful part is the structure around those files:
- Modules give capabilities a defined place.
- Adapters keep infrastructure decisions at the edge.
- Module Removal allows purging unused capabilities instead of carrying everything forever.
- Agent Context (MCP) makes architecture and conventions easier to inspect for AI assistants.
- Deployment Infrastructure provides scalable paths as part of the foundation rather than an afterthought.
- Testing Suite keeps verification directly in the development loop.
The current foundation includes more than 70 production-ready modules, but the real value is not the count. It is having those capabilities already integrated without making them inseparable from the product. Codapult modules
AI made architecture more visible
I don't think AI makes software architecture less important.
I think it makes bad boundaries easier to accumulate.
When implementation becomes cheap, you can create more code before the cost of a wrong decision becomes obvious.
That changes the economics of the early project.
The expensive thing is no longer necessarily writing the feature.
It is deciding where the feature belongs and making sure the rest of the system does not have to learn about every implementation detail you add.
That's why I built Codapult around modules, adapters, explicit boundaries, structured agent context, infrastructure, and tests.
AI can generate the implementation.
The hard part is still deciding where that implementation belongs.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.

