The practical JS 12 factor application | I. Codebase

Django Shelton
5 min readJan 3, 2019

Note: This is part a 12 part series so stay tuned for more!
Next>>

Over the last few years I’ve found using using the 12 Factor principles to guide the way I build applications to be incredibly helpful in producing scalable, maintainable, and sensible results.

However, concrete examples of the principles in action and how they relate to JavaScript have been difficult to find, and so I’m hoping to solve this with this series of 12 articles covering, funnily enough, the 12 factors.

The material here is definitely not going to be ground breaking or revolutionary, but will hopefully provide some solid fundamentals and best practices in relation to JavaScript (both client and server side). As such it’s probably most relevant for anyone new-ish to JavaScript looking to understand how to build a real application in a scalable way, or for the more experienced developer who likes to refresh the fundamentals now and again.

I. Codebase

One codebase tracked in revision control, many deploys

Image from: https://12factor.net/images/codebase-deploys.png

The first factor is a simple but very important one, partly as it frames the language we use to talk about “apps” for the rest of the factors. It boils down to:

  • Use version control (Git etc), storing code in a repository or _repo_. A codebase is the repo (or set of repos with the same root commit).
  • One codebase per app. There is always a one to one relationship between codebases and apps.
  • Apps themselves comply with 12 factor.
  • Multiple apps sharing the same code is a violation of 12 factor.
  • Each codebase is deployed (a deploy being a running instance of the app) multiple times, likely to a production location, one or more staging locations, and every developers local copy.
  • Different versions may be active in each deploy, with local containing commits staging does not, and staging containing commits production does not.

There are a few things I perceive to be benefits of this factor:

  • You deploy applications, not systems. This means you can control which parts of the application need to change, speeding up release times and getting results to users faster. It also breaks your system down into smaller components (good!)
  • Shared code should be moved into libraries which are included in applications via dependency management. This reduces the amount of places you need to change shared code, and likely breaks your system down into smaller components (still good!)
  • With multiple deploys, changes can be verified, tested, and approved before being deployed to users (production). This allows you to make and deploy changes quickly with the confidence that your production deploy will produce the same results as other deploys, as they are part of the same codebase.

How does this apply to JavaScript?

Let’s say you’re building a web form and an API which takes the input of the form and puts it into a database. You want to do validation both client side and server side to improve security and the user experience. You also want to test changes you make before releasing them to users to ensure that existing functionality isn’t broken.

While this should be a very simple scenario, there are a number of traps you could fall into which could lead to a lot of trouble further down the line.

Firstly, you could try to build and deploy this system as a single application (codebase). However, this means you have no granular control over which part of the system you’re trying to change. Say you find a bug in the API, once fixed you’ll have to rebuild and redeploy the web form and API, despite only changing one of those parts. In a world where reinstalling node_modules can take multiple minutes and the dependencies for client side and server side applications can be wildly different (think React vs Express) this can be a very expensive mistake to make. Alternatively, split this into multiple codebases which will keep your node_modules size down, reduce build time, and allow flexibility down the line — now there’s nothing stopping you from exposing the API to third parties, mobile applications etc if that need arises.

Once you’ve done this, it could be tempting to simply write some validation rules in both applications, and hope they match up. Tests for neither application will fail, but whenever a rule changes it requires developers to manually update the rules in both applications, and I certainly wouldn’t trust myself to nail that every time. JavaScript also has a lot of unintuitive parts around type coercion, dates, and who knows what else, making validation rules and changes to those rules non trivial. Luckily you’re probably already using the JavaScript package manager, npm, to install something like Express or React, so you can create and publish your own npm package and simply use this in both applications. Then all you need to do is ensure the version numbers in both applications are consistent, and voila they’re compatible. If you don’t require custom rules you can use great existing tools like Validator to achieve the same result.

There are a host of different ways to deploy JavaScript applications, from Heroku to AWS but the details of this live outside the scope of this article. What’s more relevant is how you test both pre and post deploy in order to give confidence in that deploy. You’re going to likely want to use a unit testing tool like Jest, a UI testing tool like WebDriver, and some combination of the two to run integration, component, and E2E tests.

So do you have any examples?

Of course! If you take a look at the Food Standards Agency GitHub account you will see multiple repos referencing register-a-food-business. These are the different applications and libraries that make up the “Register a food business” service. You’ll notice there’s a split between the front-end and service repos, which represents a front-end application and a service which puts data into a database.

There is also the register-a-food-business-validation repo which contains the shared validation rules between the applications.

Finally, the UI tests repo enables testing different releases, and the test suites inside each application are also run on build and deploy using Azure Pipelines.

Thanks for reading! If you have any comments or questions please post them below and I’ll get back to you. I’ll be periodically posting the rest of this series on Medium as well so keep posted if you fancy reading the rest.

You can also follow me on Twitter or GitHub if you’re wild enough to think what I do is interesting.

--

--