Table of contents
Introduction
In the rapidly changing landscape of software development, building scalable and maintainable applications is crucial for success. The 12-factor methodology provides a set of best practices for developing software-as-a-service (SaaS) applications that are easy to build, deploy, and scale.
words to understand
Portability - Ability of an application to run on different environments without requiring modifications to its codebase. This means that the same application can be deployed and executed seamlessly across various platforms.
Scalability - The ability of an application to handle increasing amounts of workload or user traffic without sacrificing performance . It involves the capability to efficiently allocate additional resources to meet growing demand
Flexibility - The capability of an application to easily adapt and accommodate changes.
Why 12 Factor app?
In past, Suppose you've built an application and now you want to introduce it to the world. you have to receive a server to host your application . once you get a server , you were dependent on that server for the rest of your life. if you wrote code that could only live on that server*.* It couldn't live on other servers. If the user was in the middle of something and your server crashes,Everything that the user did until that point was lost and they had to do all of that again, means applications were tightly bound to specific servers, leading to limitations in scalability, reliability, and portability. To overcome this server dependency has revolutionized application development to modern cloud-based flexibility, In it with cloud platforms, provisioning servers and hosting applications can be achieved rapidly. Modern applications must embrace portability, scalability, and continuous deployment. This is encapsulated in the "12 Factor App" principles.
Twelve-Factor App methodology
1. codebase :
let's begin with a very basic example using Python and flask web framework. In above code we will display a simple message in our browser.It uses the flask framework for hosting a web server and simply prints the message.
now you bring your friends along as additional developers and now everyone is working on their own development environments, but on the same code base and they're all copying their code to a central Hub whenever ready now, they're stepping on each other's toes working on the same files at the same time and creating conflicts and you need a solution that can help to collaborate
and that's where git comes in. So get helps all developers to work on the same application at the same time and collaborate efficiently. Because if git everyone installs and configures gate on their machines and that's going to help in easily pulling the latest code from the central hub using the
$ git pull
command and they add their own changes and push it back using the command
$ git push
Now currently we only have a web application in the future. We may have an order processing service or a delivery service and one of the practices followed in the past is to have a single code base and all related applications and services under it
Now when you have multiple application services like in above image you no longer have a single App instead you have a distributed system and as per the 12 Factor app multiple apps sharing the same code is a violation of the of 12 Factor and it should be separated into it's own individual code
However, within each code base you will have multiple deployments the same code base will be used to deploy to Dev or staging and production environments. So that's how it should be set up.
2. dependencies :
the second rule of 12 factor app is to explicitly declare an isolate dependency.let's see what that means. Now we installed the flask framework a python web application framework prior to writing our code.
Therefore it is essential to install the flask python framework in our local development environment before beginning development.
$ pip install flask
Now, this is just one dependency of our flask web application*.* But as the application grows, we may end up with many additional third- party dependencies*.* So at 12 Factor app never relies on implicit existence of system-wide packages, meaning you cannot assume that the dependencies such as flask in this case will exist on the system where you will be running your app. So this rule in the 12 Factor app recommends to explicitly declare an isolate dependencies.
So, let's see what that means, Many applications have multiple dependencies that must be installed prior to running the application in the case of python and These dependencies are typically listed in a file named requirements.txt in a specific format as shown in the example provided now, it's important to note that the version number is specified after the package
and the PIP install command will install all the dependencies listed in the requirements or txt file during the build process.
pip install -r requirements.txt
So that clarifies how to declare dependencies.
But what about isolating the dependencies, which is the second part of this rule. So we are working on developing two separate services or separate applications on our laptop and what if one app requires one version of flask and another app requires another version of flask. so, this is where docker containers comes in, Docker allows us to run application in a self-contained environment that is isolated from the host system. It is a more efficient and reliable way to manage dependencies.
3. concurrency :
So far, we have containerized our application and run it as a Docker container. This execute one process of our application and this instance of application is able to serve several users. But what happens when we have more users visiting our site so, we have to scale up the resources vertically by increasing the resources to the server.
However, we're going to have to take down the server for it and is going to cost downtime and we're going to ultimately hit a maximum limit on resources that could be added for that server.
But with new servers accessible within a matter of minutes, we are able to provision more servers and spin up more instances of applications today with great ease. We can then have a load balancer in between that can balance the load across the different instances of the application.
In the 12 Factor app processes are a first class citizen. and it says that applications scale out horizontally and not vertically by running multiple instances of the application.
4.processes :
12 Factor processes are stateless and share nothing. Let's see what that means. We have decided to add a new feature to our app to show visitor account on our website every time a new visitor visits our page. We have to show the total visitor account. For this we update our code to include a visit count Global variable and increment it each time a request comes in. And that works. Well when we have one process running because the visit count is stored in the memory of that process when we run multiple processes. They all have their own version of variable stored in them. And as such it displays different numbers for different users depending on which process served those users.
The same is true for other details as well such as user session information. When a user logs in to our website, we store certain session information about that user such as where that user logged in from when the user is login expires Etc. This session information is needed on the server to keep that user logged in. But if this is stored in the process memory or locally in the file system of that process then when a future request of that user is directed to another process.
The user may be considered logged out as the session information isn't available there. Now there are load balancers that are session of air and can redirect users to the same process each time. And this is called sticky sessions. However, that is still going to be an issue. If for some reason the process crashes and all locally stored data will be lost then. Which is why 12 Factor processes are stateless and shared nothing and sticky sessions are a violation of 12 factor and should never be used or relied upon.
Which is why we must not store anything in these processes instead. It should all be stored in an external backing service. This way all data and session information is stored in a place that can be easily accessed by re all processes and it doesn't matter which process a user is routed.
as all processes now have access to the same set of data the external service could be a database or caching service like redis.
5.Backing services :
So we integrated redis as a caching service or external service to our app to store the visitor account. There may be other similar services such as nsmtp service to send emails and S3 integration to store images.
all of these are backing services and must be treated as attached resources. What does that mean? Let's take the example of integration with redis, redis is an attached resource for our app and irrespective of where it is hosted maybe locally or in a cloud environment wherever it may be. It should work without having to change our application code. You shouldn't have anything in the code that is specific to a locally hosted Redis service or a remotely hosted ready service for that matter. We should be able to point our app to another instance and it should just work.
6.config :
As you may have noticed our python code includes hard-coded redis host and Port values.
This presents a problem when deploying the application to different environments such as production staging and development. And as each environment may use a different various instance requiring changes to the host and Port values. This is not considered the best practice as it can lead to inconsistencies and errors when deploying todifferent environments to ensure that our environment configurations are separate from our main application code and under Version Control. We will keep them separate. We Define a separate file name .env
and store these in them. python automatically loads the data defined in the .env file as environment variables to the app. We then fetch these from the environment variable in the code. the 12 Factor app stores config in environment variables This allows us to use different configurations for different deployments such as for testing staging or production environments.
And additionally it makes it possible to open source the project at any time without the need for any code level changes and without exposing sensitive configuration information to the published.
7.Build,Release and run :
The 12 Factor app uses strict separation between the build release and run stages. So let's see what these stages are first. So the current workflow involves the development phase where you write your code on your laptop. This could be on your favorite text editor like vs code . And now the code in the text format is not good enough to be run. so, to run it as an application by the end user. It needs to be in an exe format. means a Docker image as it converts the code from a text format to a binary or executable format is known as building the code and that's the build phase. we simply use the docker build command
$ docker build
to build a Docker image for the application using our Docker file.
Now once built the executable along with the config file for its environment together becomes the release object. Remember we had different config files for different environments. So, a combination of the executable along with the configuration, it becomes the release object and that's the release object in the release phase. Every release should have a unique release ID. This could be released with a release version such as v1,v2,v3 or it could be a release with the time stamping associated With it, so it's easy to recognize when it was created. Now any minor change in the code should create a new release? And so the typo in our code base should have created its own new release version.
And finally you have the Run face where you run the release object in its respective environment. So the exact same build is used to run in different environments and that ensures that we have the same code base running in all different environments in a consistent fashion. Now any minor change in the code will result in a new build process resulting in a new release and a new deployment. Now by clearly separating our build and run faces. We can effectively manage our build artifacts and deployments by having a distinct build phase we can store our build artifacts in a designated location allowing us to easily roll back to previous releases or redeploy a specific release as needed and this improves our overall ability to manage and maintain our software.
8.Port Binding :
In previous steps, you may have noticed that accessing our flask web application was as simple as typing the URL and port number in a web browser on your local machine.
In this case. It's 5,000 and this is because python flask framework listens on Port 5000 by default. Now if we were running multiple instances of our application on the same server, we should be able to bind the port to other ports on the server such as 5001 or 5002 Etc and other services may have similar Port bindings configured for example, red is listens on Port 6379.
Now our app exports HTTP as a service by binding to a specific port and listening for incoming requests on that Port now unlike traditional web applications. The 12 Factor app is completely self-contained and does not rely on a specific web server to function.
9.Disposability :
The next one is disposability. The 12 Factor apps processes are disposable meaning they can be started or stopped at a moment's notice. Earlier we talked about scaling 12 Factor app should be able to scale up to provision additional instances when requirement increase in a moment's notice in a matter of seconds. for this to happen processes should strive to minimize startup time. Meaning you shouldn't rely on complex startup scripts to provision your app. The same is true for reducing instances when load decreases processes should be disposable when no longer required. The 12 Factor apps processes should shut down gracefully when they receive a sick term signal from the process manager. So, let's see what that means. When the Dockers stop command is initiated Docker first sense the Sigterm signal and after a gray's period if the container is not stopped Docker sends the Sigkill signal to forcefully terminate the process running inside the container.
So, why the two signals, we want to allow the application enough time to shut down gracefully. Our application may be processing requests from hundreds of users at a time a graceful our application may be processing requests from hundreds of users at a time a graceful shutdown allows the application enough time to stop accepting new requests at the same time complete processing all existing requests. This way users who are waiting for a response from the app are not impacted. for this, the app should be able to handle the sigterm signal to avoid any unexpected data loss or resource leaks that can occur if the process is terminated forcefully with a sigkill signal here is an example of the flask application accepting the Sig term signal and then terminating the process.
10.Dev Prod Parity :
Earlier,we talked about three different environments where the application will be deployed. The div environment is where the application is developed by developers The staging environment is where it is deployed to be tested against a production and prod environment is where the application is hosted to be accessed by users.
Now a traditionally it would take changes built by developers in the development environment weeks or even months to go into production. You had one set of people write code and another set of people deployed it in a production environment and you probably had one DB used in depth such as a light database like sqlite and another one in prod such as postgresql. So there is a Time gap for the timing takes code to go from def to prod environments and the problem with that is there may be other things changing in the app from the time that it was developed to the time it goes into production that might affect the functionality of the change. There is a personal Gap where the Ops people deploying the change has little to no knowledge of the new changes resulting in making it hard to identify issues cost by the new changes and there is a tools Gap where different tools used in different environments mainly to unexpected consequences.
Now when deployed in production this is where the 10th principle of the 12 Factor app comes in. It is about the parity between different environments. The 12 Factor app is designed for continuous deployment by keeping the gap between development and production small and the 12 Factor developer resists the urge to use different backing Services between different environments. So with continuous integration and continuous delivery and deployment tools today. We are able to reduce the time. It takes for changes to go from dev to production environments in a matter of hours or even to a matter of minutes in some cases the developer who writes code should also be involved in deploying and watching it in production environments and with tools. We must aim to keep the same tools as much as possible with more than tools being lightweight and containerization tools like Docker making it easy to set up development environments. This shouldn't be hard to achieve.
11.Logs :
Let's now talk about logs. Now our application outputs certain logs about the process starting the port is listening on and every request that comes in and is served by the server is logged as a separate line.
So the logs would also capture any errors in code and this way we can troubleshoot issues if there were some failures. So traditionally applications followed different approaches to storing logs one was to write the logs to a local file named log file or something like that. The problem with this approach is that since we are living in the world of containers the container may be killed anytime and the logs are lost moreover. The application is coded to write to specific log files on the file system, which is also another problem. Now in other cases applications try to push logs to certain Central logging system, like fluentd.
while centralized management of logs is encouraged tightly coupling specific logging solution to the app itself is discourage. So we do not want our app to be stuck to a single and so in this example the code where we actually send logs to the specific logging provider. This is a discouraged.
However, so the 11th principle in the 12th of Factor app is about logs management at 12 Factor app never concerns itself with routing or storage of its output stream. store logs in a centralized location in a structured format. So the 12 Factor apps must not try to write to a specific file or be tied to a specific logging Solution 12 Factor apps must write all logs to its standard out or to a local file in a structured Json format.
that can then be used by an agent to transfer to a centralized location for consolidation and Analysis purposes. So all logs must follow a structured format as it will make it easy to query and analyze. Now elk stack and Splunk are other good examples of centralized logging solutions that can be considered.
12.Admin Processes :
So this is how our setup looks like right now. The red is database stores the count of total visitors and say for some reason we realized that this number is inaccurate or that we wanted to reset it. We need a way to get into the app and reset the count as a one- time operation. Now, this is a one-time admin or management task that will have to perform using a script like this.
we may have similar tasks such as to migrate databases or fix specific user records Etc. The admin process is principle of the 12 Factor app methodology suggests that administrative tasks should be kept separate from the application processes specifically, it recommends that any one off or periodic administrative tasks such as database migrations or server restarts should be run as a separate process or application. However, it should be run on identical systems as the app running in the production environment in Example we could spin up another Docker container to connect to the same redis database and run the reset script and to reset the numbers. And then it's it ends.
The admin processes principle of the 12 Factor app methodology recommends that administrative tasks should be kept separate from the application process and that they should be run in an identical setup and be automated scalable and reproducible.
Conclusion :
you're now equipped with the knowledge and tools that you need to build more than Cloud native applications that meet the demand of today's world. Now throughout this course, we have covered the 12 factors exploring the concepts and showing you practical examples of how to implement them in your own applications. And we've seen how the 12 Factor app methodology can help you build applications that are scalable resilient and easy to manage and we've shown you how to use modern tools and techniques to achieve these goals. Remember the 12 Factor app is not just a set of best practices. It's a mindset by embracing the principles of the 12 Factor app. You can build applications that are not only efficient and reliable but also easy to maintain and evolve over time and whether you're building a small micro service or a large scale Enterprise application the 12Factor app methodology provides you with a solid foundation for success.