The other day, I needed to create a new application architecture integrating different pieces of code that should run together. The resulting system should be composed of server-side/backend components connecting to client side/frontend components.
I wanted to prototype the solution to make sure that the different components that I imagined would work together. I also wanted to make sure that there would not be any major problem/incompatibility that would prevent the foreseen solution to work. I was building a proof of concept.
All the components of the solution being new, I needed to build them at the same time, going back and forth between them to make sure that they would communicate properly with each other.
As we know, one of the key element of effective development is to have a rapid feedback loop. It means that, the time between writing code and seeing the result of said code should be as small as possible.
This is generally achieved by having the development environment running the code and watching any change to the source code in order to re-launch it as soon as possible. Tools like Webpack’s devServer, parcel, nodemon, jest (for tests), etc. are generally used for that.
In the case of my proof of concept being composed of different components that all need this rapid feedback loop, I was looking for a way to have that benefit for all the solution’s components together as easily as possible.
It became obvious that if I wanted to work on different parts of the solution at the same time and have them running an talking to each other, the easiest way was to have them all in the same source repository.
This is where I came across the concept of monorepo (I know it exists since a long time but I never tried it before). In a nutshell, a monorepo aims at maintaining different projects constituting a common solution inside a single git repository. The different components will be treated together from a development point of view and this setup will ease the development lifecycle.
By convention, the different running components will be stored inside an
apps folder while any other shared code/library will be stored inside the
There are solutions to help the developer managing the monorepo. Some are built in package managers like npm or yarn (with the notion of workspace) but there are also some tools dedicated to monorepos handling.
The Node Package Manager (NPM) has builtin capabilities to nest multiple projects inside a parent project. It is called NPM workspaces.
NPM workspaces is a feature that allows developers to manage multiple related applications or packages within a single workspace. It simplifies dependency management and code sharing between packages and improves development efficiency and code quality. With NPM workspaces, developers define a set of related packages in a
package.json file and manage them as a group within a single workspace.
This allows packages to be developed and tested independently, while also sharing dependencies and being managed as a group. By simplifying dependency management and code sharing, NPM workspaces can help improve development efficiency and code quality.
For this particular POC, I used TurboRepo. This tool reuses the power of npm workspaces and bring new features:
Incremental builds. Turborepo will cache the result of a build (also works with test results) to avoid doing twice the same job. If the source hasn’t changed, the output should be the same, so it is cached.
Content-aware hashing. To determine what has changed, turborepo does not rely on file modification date but rather on file content hash. This means that if you reverted your change to the previous state, the build result is still valid.
Parallel execution. Uses all possible CPU cores to speed up every possible process.
Remote caching. By centralising the build results in a shared cache, your teammates and CI/CD pipeline can leverage the builds done locally. I did not try this feature.
Zero runtime overhead. Once your assets are build, there is no trace of turborepo in the resulting artifacts nor in the sourcemaps.
Using a monorepo, and especially with the help of Turborepo, allowed me to have a very responsive development environment: I could be working on any part of the general solution and, as soon I hit save on a file, the related project gets reloaded automatically and I could see the result right away.
This setup not only allowed me to work on different applications but it also enabled sharing common code in libraries used by different applications, helping me keeping code clean and DRY.
The power of Turborepo made this whole process even faster by relying on caching. When I start working, it does not take multiple minutes rebuilding all projects. It rather loads the build done last time from the cache.