How to Structure a SaaS Codebase
Learn how to organize the folders, files, and layers of a SaaS application so it's easy to build, fix, and grow.
What Is a Codebase Structure?
Imagine walking into a library where every book is thrown into one giant pile. Finding anything would be a nightmare. Now imagine that same library with a clear system — fiction on the left, history on the right, each book in its right place. A SaaS codebase structure works the same way. It's the system you use to organize all the files and folders in your application so you can find things quickly and know where to put new ones.
A typical SaaS app has three big parts working together:
- The frontend — the website or app screen users see and click. Built with tools like React, Vue, or plain HTML/CSS.
- The backend — the invisible engine that handles logic, payments, user accounts, and data processing. Built with Node.js, Python, or similar.
- The database — where all your data lives: user info, posts, orders, settings. Stored in systems like PostgreSQL or MongoDB.
A good structure keeps these parts separate but connected. Each piece has a clear job, and each file has a clear home. When a bug shows up, you know exactly where to look. When you want to add a feature, you know exactly where to put it.
A Messy House Is Hard to Live In
When a codebase has no clear structure, things go bad fast. Bugs hide in unexpected places. Adding a small feature means touching code all over the app. Two developers working on the same project accidentally overwrite each other's work. And when a new person joins the team, it takes them weeks just to figure out how anything fits together.
With a clear structure, the opposite happens. Developers can work in parallel without bumping into each other. Bugs are easier to track down because related code lives together. Features are self-contained — you can add a new section without rewiring the whole app. And new team members can onboard in days, not months.
💡 Key Insight
The time you save by planning your structure upfront is borrowed time — you'll pay it back with interest every time you have to debug or extend a poorly organized codebase.
The Standard SaaS Folder Layout
Most SaaS apps follow a pattern that separates the three big layers: frontend, backend, and shared pieces. Here's what a typical folder structure looks like:
my-saas-app/ ├── client/ ← The frontend (what users see) │ ├── src/ │ │ ├── components/ ← Reusable UI pieces (buttons, cards) │ │ ├── pages/ ← Full page views (login, dashboard) │ │ ├── hooks/ ← Shared logic for components │ │ └── App.jsx ← Main frontend entry point │ └── package.json │ ├── server/ ← The backend (invisible engine) │ ├── routes/ ← URLs the frontend can call │ ├── controllers/ ← Logic for each route │ ├── models/ ← Definitions for database tables │ ├── middleware/ ← Code that runs before the main logic │ └── index.js ← Backend entry point │ ├── shared/ ← Code used by BOTH frontend and backend │ └── constants.js │ ├── tests/ ← Automated tests │ └── README.md ← Instructions for new developers
The key idea is separation of concerns — each folder holds one type of thing. Frontend code lives in client/, backend logic in server/. When a developer wants to fix the login page, they go to client/pages. When they want to fix how login is verified on the server, they go to server/routes. Nothing is ambiguous.
The shared/ folder is especially useful. If both the frontend and backend need to use the same list of error codes or user roles, you put them in shared — one place to edit, both sides stay in sync.
A Simple Express Backend
Here's what a tiny backend structure looks like in practice using Node.js and Express. This example shows how routes and controllers stay separate — a clean pattern that scales well:
// The server entry point — wires up routes const express = require('express'); const userRoutes = require('./routes/users'); const app = express(); app.use(express.json()); app.use('/api/users', userRoutes); app.listen(3000, () => console.log('Server running'));
// Routes file — just directs traffic to the right controller const express = require('express'); const router = express.Router(); const {