This site lists the protips that we shared with students during our courses
Most applications that you write will consist of a large number of files and it can pretty soon be overwhelming to navigate the project.
This post tries to give a few simple tips on how to get a good start organizing your code. Writing about folder structure is a bit of a minefield since no one standard has emerged yet. I will, therefore, give generic tips and then pointers to suggestions and thoughts.
The main thing to think about when organizing the project is that the structure should be easy to follow along in- and easy to pick up when joining a development team.
Therefore you should always strive to group logical units together. This is related to making your modules cohesive (but with low coupling). For example, you want to put all the files for the API in the API
-subfolder. You wouldn’t put client code in the server
folder etc. This is easy to make up stupid examples about, but can also be a tricky trade-off sometimes.
In this document, I’m going to use an example with a SPA client, an API (that the client uses) and finally a server that serves up the different parts. But, again - I’m not going into too many details.
In the root you first put things that concerns the entire project. This is a starting point for most developers and is displayed by default when navigating the project on, for example, GitHub.
Here are some of the more common items that are/can be found in the root
Readme.md
- first and most importantly want a readme.md
-file that describes what the project does and is.
It should contain:
src
folder - where the source code for the entire application resides. See below..gitignore
- typically the .gitignore (and the entire .git folder) are one for the entire project and could be in here.
Dockerfile
/ docker-compose.yml
- if your project uses docker the root is a good spot to put the file that is used to start up your applicationconfig
- configuration for the application, like environment files (.env) or other keys. This folder could also reside in each part (client, server, etc) but that will be up to the individual applications. Having the config in one folder makes it easy to exclude it from being checked in (should you have secrets in here for example)scripts
/ tools
- over time most applications will need tools and scripts (to reset databases, make reports, etc) - this is a good spot to store themDon’t put too many things in the root since it makes it very tricky to navigate - only the things that belong & affect the entire application.
So: here’s what our ideal root directory would look like:
├── src
│ └── all the source code for our application
│
├── config
│ └── all the config that we'll need for our application
│
├── scripts
│ └── bashScript1.sh
│ └── bashScript2.sh
│
├── tools
│ └── tool1
│ └── tool.bin
│ └── tool.js
│
├── .gitignore
├── docker-compose.yml
└── README.md
The source code of the application should go in the src
(or similar) folder. In this folder, you can further divide the application up into the parts that make up the application. For example:
I like to divide the API
and the server
but I can almost guarantee that they are others that would argue against that. My argument is that in this setup we can now build the server
directory and it will be the mother application that we run to get the parts.
But just as a few examples that show how my opinion could be challenged:
When doing a create-react-app
your start-script contains a server in itself and you will not need a separate server, but the API
can be it’s own server, using express
, for example.
If you are using a tool like Next.js or Netlify to serve up your client application you will not make the server
folder but rather let the API
be its server.
If you are using serverless functions, you are typically packing each function as a separate folder, or as part of the client application, in a functions
folder and then deploying them separately with a script.
Whatever solution you go for, make sure that the structure is clear and easy to understand and start.
So: here’s what the inside of our src folder could look like:
├── client
│ └── all the source code for our client
├── server
│ └── all the source code for our server
├── API
│ └── all the source code for our API
└── README.md
The package.json
is the application manifest and is the marker for where an application lives. For example, the root of the server
directory is where the server
-application. Hence I would expect a package.json
in this folder. This is also where the package-lock
file will be generated.
Typically this folder also holds a starting point for the application (the "main"
-section of the package.json
-file). If you don’t have great reasons for other naming conventions, use the default convention in Node of index.js
.
Another good reason for an index.js
-file at the root of each folder is that you can use that as a gateway/entry point for code that references the folder.
For example, let’s say that I have a DB
-folder with some database access code in. Maybe I can access two different databases and hence create a file (or a folder) for each: userDataAccess.js
and ordersDataAccess.js
. By adding a db/index.js
in I can now expose the functions in a simple way like this:
const users = require('userDataAccess.js');
const orders = require('ordersDataAccess.js');
module.exports = {
users,
orders
};
The client code will now be very simple:
const db = require('./lib/db/');
db.users.getUserById(22345);
// or
const userDbClient = require('./lib/db/').users;
userDbClient.getUserById(22345);
An easy structure that helps our code to become more readable and useable. And still leaves room to grow in.
So: an example of a really bare bones server folder:
├── lib
│ └── db
│ ├── userDataAccess.js
│ ├── ordersDataAccess.js
│ └── index.js
├── index.js
├── package.json
└── package-lock.json
Cohesive means that things that logically belongs together should be closely connected. When it comes to folder structure there are a few good examples for what that means:
Components in a React app - I like to put each of the components in a component
-folder and then have each component in its folder. In this folder, each component can have all the files it needs: the logic (JavaScript code), the styling, and tests for example.
This means that the “mother” app.jsx
then just can put all the components together. Usually, I let the app.jsx
live in the root of the components
folder.
├── components
│ └── navbar
│ │ ├── navbar.jsx
│ │ ├── navbar.test.jsx
│ │ └── navbar.css
│ └── map
│ │ ├── map.jsx
│ │ ├── map.test.jsx
│ │ └── map.css
│ └── app.jsx
│
├── index.js
├── public
│
└── ...and all the rest that a react app needs
Routes in an Express app - similarly I like to separate the routes of an API into a routes
folder and then leave the index.js
file in the root. In this file, I can keep the “infrastructure” of the application, separate from the handling code of each route.
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
// routes
const routes = require('./routes/routes');
const userRoutes = require('./routes/userRoutes');
app.get('/', routes.handleRoot);
app.get('/users', userRoutes.getAllUsers);
app.get('/users/:id', userRoutes.getUserById);
app.post('/users', userRoutes.addUser);
// ./routes/usersRoutes
const getAllUsers = (req, res) => {
res.send('Getting all users');
};
const getUserById = (req, res) => {
res.send(`Getting user ${req.params.id}`);
};
const addUser = (req, res) => {
res.send('Adding a user');
};
module.exports = {
getAllUsers,
getUserById,
addUser,
};
As the application grows you might want to break the handling code further. Notice that my example has one userRoutes
that have that code and another routes
for all the other routes. Further down the line, I might move the userRoutes
to a separate folder under routes/users
and then make new folders with route-handling code for other needs. Only add complexity when you have a good need to do so.
lib
folder. This might be database access code, utility functions, or authentication checks - in short functionality that is needed in several places.The placement of tests is a debate that has been going back and forth quite a lot in the community. My suggestion is to place test code as close together with the production code (the code the test is testing) as possible. Which gives:
ageCalcuclator.js
should have an ageCalculator.tests.js
file next to it. In the same folder. Most testing tools will pick up patterns like this automatically and it’s easy to exclude *.tests*
from build scripts etctests
(or integrationtests
) folder in the root of the API projectsrc
-folder.
client
-type of test and then have an e2e
-folder in the root of the src/client
Many tools have generators for the most common frameworks like npx create-react-app
or npx express-generator
. For tools like these, it’s almost always better to follow the generated structure rather than to fight it with your own opinions.
As I promised here are some opinions on how to structure applications
There are many different opinions and ideas on how to structure an application/project. Whatever you decide to go with ensuring that you are always helping the person reading the code to easier understand and find what they are looking for. That person is very likely you in a month or two.