12 Factor App in NodeJS Part 0: Intro

sumitbhanushali

Sumit Bhanushali

Posted on November 30, 2023

12 Factor App in NodeJS Part 0: Intro

We have all heard that attachments are bad, well this applies to software development too. Your app maybe too dependent on OS or Cloud Provider. Your libraries might only work on specific OS or your app must be too much dependent on cloud sdks. Some OS are better suited in certain tasks than other and a policy change in some cloud provider can lead to huge expenses.

This leads to huge obstacle when you need to scale your app and can lead to huge expenses since moving out of particular cloud or OS is a herculean task. Your App should be flexible enough to make sure you dodge such bullets easily deployments must be as smooth as possible to be able to ship more features faster and without any bugs with minimal intervention of any human resource.

App must be able to scale automatically up or down on demand which leads to savings on cloud bills on non active hours and better User experience by delivering faster responses at peak times.

That’s why engineers from heroku came up with 12 Factor app methodology which applies to web apps in general. These methodologies when applied will help scale your app easily, make your app platform agnostic and make deployments smoother by maintaining proper build, test and deploy stages and keeping minimal difference between dev and prod environment. You can easily deploy your apps on aws, azure, gcp, heroku or even on on-premise servers

As mentioned in the name 12 Factor app is a list of 12 factors, I will list them here briefly and will write a detailed post with code implementation in NodeJS; ExpressJS + TS to be specific in later posts

I. Codebase

Track your codebase in revision control system such as git. Why? It helps maintaining multiple versions of an app in single codebase

II. Dependencies

Language runtime and its Package manager should be the only prerequisite to run the app. Other dependencies should be declared explicitly in a dependency declaration manifest file and should be isolated i.e no global packages can be used inside the app. This results in deterministic builds and thus makes it extremely simple to setup dev and prod environments

III. Config

An app’s config is everything that is likely to vary between deploys (staging, production, developer environments, etc) which are credentials to external services such as Database(MySQL, Redis), Cloud Providers or External APIs like twitter, dropbox, etc. Such configs should be stored in environment variables. This prevents accidental leaking of sensitive information and reduces hassle of updating such configs by just updating environment variables instead of manually editing file on server

IV. Backing Services

A backing service is any service the app consumes over the network as part of its normal operation. Examples include datastores (such as MySQL or MongoDB), messaging/queueing systems (such as RabbitMQ or Kafka), caching systems (such as Redis). App should be able to swap out a local MySQL database with one managed by a third party (such as Amazon RDS) without any changes to the app’s code. This makes app reliable and more easy to scale since Resources can be easily attached to and detached from deploys at will. For example, if the app’s database is misbehaving due to a hardware issue, the app’s administrator might spin up a new database server restored from a recent backup. The current production database could be detached, and the new database attached — all without any code changes.

V. Build, Release, Run

Build Stage is when we are done with developing code and are confident for deploy. We thus run a build command which outputs an executable file which cannot be modified by us.

Release Stage is where we combine our build with config which makes app able to run. This releases should be tagged with unique ReleaseID.

Run Stage is where we run our release and make our app accessible to outside world.

This stages should be strictly seperate and any change in code should trigger new build, release and run stage. Run stage should be as simple as possible, process or system restarts can easily rerun our run stage without requiring manual intervention. This also simplifies rolling back to previous release when required

VI. Processes

Our App runs on processes. It can be single process on developer’s machine or multiple processes on production. This processes must be stateless, all states should be stored in a storage solution. Temporary state can be stored in caching solution like redis and permanent state in Database like mysql. App should be able to handle process or system restarts and recover to right state. Also, since our app is running on multiple processes, same user can be routed to different process and its state information becomes inaccessible. Making our processes stateless makes our app reliable and ready to scale

VII. Port binding

Your service must be self contained and be able to bind to a port to serve requests without relying on other software such as nginx to create a web-facing service. This must happen entirely in user space, that is, within the app’s code. [why]

VIII. Concurrency

In the twelve-factor app, processes are a first class citizen. Using this model, the developer can architect their app to handle diverse workloads by assigning each type of work to a process type. For example, HTTP requests may be handled by a web process, and long-running background tasks handled by a worker process. Since our apps are already running on stateless processes, fearless concurrency is easily achieved and our app can scale without making any changes to code

IX. Disposability

Stateless processes are disposable, meaning they can be started or stopped at a moment’s notice. This facilitates fast elastic scaling and makes zero downtime deployments possible. Our app should take minimal time to startup and shutdown gracefully. Our app should also be prepared for sudden death. All these makes sure our app is reliable, easy to deploy and scale elastically

X. Dev/Prod Parity

To prevent “This works on my machine”. This happens because developers by using adapter solution use lightweight solution on development machine and different solution on prod. Some inconsistencies between these two can cause production to break which makes our app unreliable and thus damaging business

XI. Logs

Our app should not worry about routing or storage of its output stream. It should not attempt to write to or manage logfiles. Instead, each running process writes its event stream, unbuffered, to stdout. It will be responsibility of execution environment on how it should handle stdout. In case when dev is developing on hist local machine, the developer will view this stream in the foreground of their terminal.

In staging or production deploys, each process stream will be captured by the execution environment, collated together with all other streams from the app, and routed to one or more final destinations for viewing and long-term archival.

XII. Admin Processes

Admin processes such as running database migration or running some script to debug should be run in an environment that is identical to target environment. Twelve-factor strongly favors languages which provide a REPL shell out of the box, and which make it easy to run one-off scripts. In a local deploy, developers invoke one-off admin processes by a direct shell command inside the app’s checkout directory. In a production deploy, developers can use ssh or other remote command execution mechanism provided by that deploy’s execution environment to run such a process.

💖 💪 🙅 🚩
sumitbhanushali
Sumit Bhanushali

Posted on November 30, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

12 Factor App in NodeJS Part 0: Intro
typescript 12 Factor App in NodeJS Part 0: Intro

November 30, 2023