The Twelve-factor app is a set of 12 principles or best practices for building web applications which now days are more commonly known as Software-As-A-Service (SAAS) applications. It was published by the co-founder of Heroku, Mr. Adam Wiggins in 2011. These principles are the result of all the experiences and observations that the people working at Heroku, which is a Platform-As-A-Service, have gained over a large variety of Software-As-A-Service applications ever deployed on it.
These principles help us to create the applications that use declarative programming for automation of setup which in turns reduces the development time and cost of the projects when a new developer joins it, doesn’t rely on any particular OS and are easily portable, are highly suitable for deployment on cloud and allows continuous deployment and are easily horizontally scalable without requiring many changes in the codebase. The factors are as follows:
Single codebase per application tracked in version control with many deploys. Let us break the official statement of it and try to understand the two main terms in it. In naive terms, a codebase is all the human-written source code that doesn’t include the class or object files generated by the tools. A deploy refers to a single running instance of that particular application. Each application must have only a single codebase. Each such codebase must be managed in a version control system. Some popular VCS (Version Control System) includes git, svn and mercurial. It there exists multiple codebases, then it’s not an application, it’s a distributed system. Each component of that system will be known as an application that shall follow the principles of the 12-factor apps.
Explicitly declare and isolate dependencies. We all must have observed this at least once in our programming career that we have downloaded a python library for some purpose and then found out that we have downloaded the wrong version of the library or have the wrong version of python installed in our system. This generally happens because the task of managing the dependencies is given to the developer. Hence, this factor states that we must always declare the dependencies in the manifest file, a file containing the metadata for the dependencies like name, version. It increases the speed of the development as now the developer is free from the task of managing the correct version of the libraries. There is no need for explicitly downloading the required JARs anymore.
Store config in the environment. The source code and the configurations must be completely separated from each other. We must store all the configurations like DB credentials, path, URI in the environment variables as in general practice in the industry, application’s configurations vary from environment to environment like dev, test, prod, etc. Also, no configuration should be stored in git in plain text. There is a very simple way in which you can check whether your current application follows this Config principle or not by asking yourself that whether you can make your application open source right now without making any changes and without compromising any of your credentials.
4. Backing Services
Treat backing services as attached resources. If we talk in very simple terms then any service that your application consumes over the network is known as a backing service. Your application must treat these services as resources which it is consuming over the network. It gives us the advantage that our services become easily interchangeable and offer great portability to our application.
A simple example is supposed your application is currently using a local PostgreSQL database for its operations and it is afterward replaced with the one hosted on the server of your company by just changing the URL and the database credentials.
5. Build, Release and Run
Strictly separate built and run stages. The deployment of your application must be properly separated into three non-overlapping non-dependent phases namely build, release and run. The build phase constitutes the compilation of the code which in the end generates the artifact like a JAr or WAR file. The second phase, release, take the artifact file generated at the end of the previous phase and adds the configurations for a particular environment to it. The last phase includes the running of the instance of the application.
Execute the application as one or more stateless processes. An application is said to follow this principle if its instances can be created and destroyed any time without making any effect on the overall functionality of our application. To achieve and fulfill this principle, our application must store any type of data generated by it in any persistent (stateful) datastore. But it doesn’t mean that we can’t use the in-memory of the processes of our application. We can use it to store as temporary storage. In simple terms, the usage of sticky sessions must be avoided. Sticky sessions, in simple terms, refers to catching the logged-in user’s session data in the local memory of the process and then directing each subsequent request from that particular user to the same process. The problem with sticky sessions is that it causes uneven load balancing among the instances of the application.
7. Port Binding
Export services via port binding. Any application that follows this principle is completely self-contained and standalone. It exports HTTP as a service and doesn’t require any server like a tomcat to listen to requests. It binds itself to some particular port and listens to all the requests hitting on that port. You must also have observed it while designing an application or a microservice which runs on a particular port and you hit requests on that particular port using postman to get a response. This information that to which port our service will be listening to is also stored in the configuration file itself.
Scale out via the process model. An application that follows this principle must be divided into smaller different processes instead of a single large application. Each such process must be able to start, terminate and replicate itself independently and at any time. This principle allows scaling our application very easily. By scaling out we refer to horizontal scaling in which we run multiple instances of our processes. It adds concurrency to our application in a very simpler way due to the existence of independent horizontally scalable processes.
Maximize robustness with fast startup and graceful shutdown. Robustness of an application refers to the graceful starting and termination of its processes without affecting the overall application’s functionality. For example one of the processes of our application is storing the details of a newly added employee to the company into a database. But while doing so, in between an unexpected error occurs which causes the process to terminate in between unexpectedly. However, the state of our application or database must not be affected by it and the process must fail-safe. Also, it must start quickly whenever required.
10. Development/Production Parity
Keep development, staging, and production as similar as possible. It simply means that the development and production environment must be as similar as possible. The processes being used, technologies and the infrastructure must be the same. This will help you in a way that whatever error that can happen over time will happen at the development stage itself instead of surprisingly occurring in the production. This helps in the continuous deployment of our application and reduces the development time and efforts also.
Treat logs as event streams. Logs are very essential to understand the internal working of the application and it can be of different levels and are generally stored in a file named “logFile” in the storage. Ideally, our twelve-factor application must not be worried about the storage of the logs. Whenever a request enters into the system, corresponding logs are made and they are treated as a sequence of events that can be used to debug when some problem occurs.
12. Admin Processes
Run admin/management tasks as one-off processes. Most of the applications require a few one-off tasks to be executed before the actual flow of the application starts. These tasks are not required very often and hence, we generally create a script for it which we run from some other environment. On the other hand, the twelve-factor methodology says that such scripts must be made a part of our codebase itself managed in the version control system. These tasks should also follow twelve-factor principles.