Skip to main content
Cloud

12 factors of Cloud Native Application Development

By December 5, 2020December 18th, 2020No Comments

Digital transformation initiatives in enterprises today are driving innovation and agility as well as delivering superior customer experiences for global consumers. Embracing technologies and software development methodologies such as cloud native, cloud computing, containers, and DevOps have been pivotal to digital transformation.

Today, we must build systems that are globally available, leveraging limited human capital resources, and doing so in a way that is maintainable all the while, getting to market first. This massive demand on software engineers has caused us to respond with new patterns in developing our software.

The 12 factors of cloud native application development is a pattern emerged based on a large number of deployments by agile teams developing web application software. Listed below are the 12 Factors:

Factor 1: Codebase

The management of the codebase is critical for the twelve-factor methodologies. The codebase itself must be tracked in a version control system(VCS). Majority of teams globally use Git, you can use any form of version control. An application, while it exists in a single code base, may have multiple deploys of that application. 

Since, VCS have the entire history of the application. It is easier to trace a bug where it exists and where it was introduced. This allows us to determine if quality checks need to be implemented. 

For those not using VCS to manage your code, it’s time to start using VCS to track a single application per codebase, and deploy often from that codebase.

Best tools for Version control system

Factor 2: Managing Dependencies

With twelve-factor applications, you avoid dependency failures by providing all of the dependencies that your application needs to run. The dependencies are declared in a manifest appropriate to the development language. This manifest not only specifies the dependency, but the appropriate version to leverage. Your build system will then package that dependency for the application to use during deployment and runtime. 

Another major benefit of this factor is easier onboarding for a new developer. A developer will not have to go through any complex setup to get the application running locally. Since the application deployed locally will behave the same as if it’s deployed remotely, there is little effort for him or her to get started. Now, as long as you build your application correctly using this factor, your app should run anywhere, in any container, and on any operating system.

A few examples of dependency managers are listed below in the format name: (language, default repo, default manifest, default lock):

  • Composer: (PHP, Packagist, composer.json, composer.lock)
  • Gradle: (Java, Maven Central, build.gradle, gradle/dependency-locks/*.lockfile)
  • Node Package Manager (NPM): (NodeJS, NPM, package.json, package-lock.json)
  • Yarn: (NodeJS, NPM, package.json, yarn.lock)
  • Bundler: (Ruby, RubyGems, Gemfile, Gemfile.lock)
  • Pipenv: (Python, PyPI, Pipfile, Pipfile.lock)

Factor 3: Application Configuration

In a multi-environment, multi-data center ecosystem, configuration is almost guaranteed to be different between those environments and our third factor focuses on that configuration. 

Config includes those items which are specific to an environment and not to an application. Database connections are a great example. The pattern for config is essentially externalizing it from your application. Each framework has a different way of dealing with this but no matter the management, the values are not embedded in the code itself. 

A pure 12 factor strategy for providing configuration to your application, is through environment variables or any other similar mechanism. These are provided at run time from the container, operating system, or another component completely, all directly to your application. Your application will use that config during its life cycle. The application itself only utilizes a placeholder in the code for that config value.

Some configuration management tools for your application

Factor 4: Backing services

Imagine for a moment, that every service you need in order to execute functions in your application were attached directly to it as if they were a component. This is the aim of the fourth factor of cloud native applications. 

In a 12-factor application, each of the services is mounted to your application as a backing service. Backing services in a cloud native application, include any service that is communicated with over a network. Think about database connections, cache providers, file sharing services like SFTP or Amazon S3, email services. 

In a 12-factor application, remote services are treated the same as the local services and that is the key to this pattern. This is such a powerful factor for reducing the code complexity of dealing with network resources. These backing services are bound by URL to the remote or local resource identically. And that URL is provided by the config that’s managed by those rules defined by the previous factor. Binding backing services to your application during runtime, instead of compile time, will allow you to be much more flexible with your ecosystem as a whole.

12 factor cloud native Business Services Illustration

Factor 5: CI/CD Build, Release, Run

The fifth factor is the first one that deals more with how our code is managed in the ecosystem, rather than the actual code itself. What this factor aims to convey is that there should be distinct phases of the life cycle of a deployed application version. 

Now, CI/CD definitely makes this whole process easier, especially when leveraging pipelines. The build stage begins with a developer or a development team. He or she decides that the code is ready for deployment and either triggers a code review or they simply trigger the build themselves. The codebase is transformed into the build by a process specific to the language and framework. 

The build stage is the only stage that actually incorporates code changes. And as such, new builds must be triggered to get new features or bug fixes into the environment. 

A release takes the artifact or artifacts from the build stage and combines it with its configuration to create a runnable component. The config is based on the current environment target and the artifact itself. The code change impacts the build and the configuration change impacts the release. It is possible to modify the configuration without impacting the build. So a new release can occur without a new build. One important note is that each release should be tagged with a time stamp or version number. Once a release is created, it is considered unmutable. 

The final stage is the run stage. Basically, the run stage is nothing more than taking the release and executing the appropriate system processes to make your application run. 

Now these three distinct phases of build, release, and run occur throughout the life of the application over and over again. It is highly recommended to implement CI/CD so you don’t repeat yourself.

Best CI/CD Tools

Factor 6: Running processes

In a 12 factor cloud native application, you should be running processes that are stateless. You should assume that there is no guarantee that data in memory will be either outside the run-time of the current process. In fact, relying on data either in the file system or in memory from operation to operation, is a violation of the premise of this factor. 

Memory usage should be single threaded, and short lived. Anything that needs to be stored from operation to operation needs to leverage a database or a cache. This becomes even more critical as we look at the life cycle of a 12 factor cloud native application and how applications can have instances created, destroyed or scaled at will. When dealing with sessions, 12 factor looks at sticky sessions as a violation of the methodology. 

So you should use technology for caching session data in an external data store instead of using local memory. In addition to changing how your application starts and runs. Cache technologies are always an option to leverage cases where you are relying on the memory of the system. But there are also ways to re-write your application and specify API calls, to make your application truly stateless.

Stateless Application Architecture

Factor 7: Port binding

In a 12-factor cloud native application, there is no separate container handling the requests and responses for your application. Your application needs to handle the protocols of communication, but when writing your application, you don’t know how it will be executed, so you would have to assume the defaults.

To solve the challenge of multiple processes in the same OS container, running the same protocol, the 12-factor patterns use a strategy of port binding. Each process has its communication protocols bound to a usually non-standard port, allowing it to run in a container in an isolated fashion. 

When in a development environment, you leverage some form of manual port binding, either providing the port as an environment variable locally, or through a command line parameter. Once in a deployment environment, however, your application is served its messages through a routing technology that handles the communications. Now, enabling port binding is often, once again, a framework, or pattern, to solve.

The following code snippet shows how to map a port value that’s passed in an environment variable.

const express = require('express')

const app = express()

const PORT = process.env.PORT || 8080
app.listen(PORT, () => {
  console.log('App listening on port ${PORT}')
  console.log('Press Ctrl+C to quit.')
})

Factor 8: Scaling with processes

Concurrency is a challenge in the legacy monolithic world. Enter the concurrency model of the modern day 12 factor cloud native application. We need to break all of the services into unique applications and have them run as distinct processes. This factor states simply that you would achieve the various forms of concurrency by spawning additional instances of your application processes. There are a multitude of problems that you can solve with this model. 

One thing to take note of with this factor is the concept of scale. Concurrency in the 12 factor world isn’t just about applications. Backing services are just as important. You may actually find that this is the hardest problem to solve. Always take into account the ability to scale both your application and their backing services, especially when dealing with global distribution.

Factor 9: Disposability

Twelve-Factor cloud native applications have to be designed and built to be disposable. An application should be able to handle constantly being started and stopped at a moment’s notice. Another reason disposability is so critical, is the scalability of an application. 

A process that starts fast and shuts down fast allows for an environment with auto-scalable application instances. The benefit of such a model is that under higher load, the container can add more instances dynamically, and then as load decreases, it can scale them back down. This allows you to serve the user needs without major slowdowns while also providing for reduced costs by preventing waste and resources. 

Finally, this factor is important because crashes happen. By building your applications to be easily shut down and started up, it makes your overall system more fault tolerant. 

The first aspect of a disposable process is fast start up times. Your application design needs to take into account everything that has to be done during start up, and focus on ways to make them happen as quickly as possible, often with parallel tasks. In addition, you need to focus on what isn’t needed immediately when a process starts up, and move it out of the start up routine. By moving these tasks to slightly later in the life cycle, your application can start listening for requests sooner. 

The second design aspect is handling shutdowns appropriately. Your application should be designed, that when a shutdown request comes in, it’s handled gracefully. This can include refusing new connections to the application while it finishes processing existing ones. It can also include storing appropriate data to the data store or cache, so that other processes can pick that information up. This graceful shutdown model should also be used whenever possible in crash scenarios.

In addition, you need to ensure you have really good error handling and crash protection, so that you can clean up your application, before it goes out of scope. The following code snippet shows how application should respond to SIGTERM by shutting down gracefully

const PORT = process.env.PORT || 8080
const server = app.listen(PORT)

console.log('Message service started')

process.on('SIGTERM', () => {
    console.log('Shutting down message service')
    server.close()
});

Factor 10: Dev/prod parity

The 12-factor way of dealing with lapse of time, from dev to prod, often yields a wide gap between what production looks like and what development looks like, increasing a risk of bugs, and that very simply is to not allow a wide gap between your environments. 

In a 12-factor world, the differences between production code and development code are a few working features at most. Once a feature is completed, it is quickly rolled to production to keep the non-prod and prod environments in sync as much as possible. 

Factor 11: Use your logs

Log messages are critical for operations. Especially in helping troubleshoot issues that may arise in running applications. In 12-factor application logs are treated as an event data stream. Each application writes its logs to standard out in the form of a stream. Each application shares the same stream. The logs can then be aggregated to another system like ELK for archival and reporting. You may need to leverage log routing technologies, depending on your execution environment. Logs are the best way to know what’s going on in your applications, they also help you find issues, including potential security exploits.

Best Log aggregation tools

Factor 12: Admin process

Admin tasks or management tasks are necessary in software development. We have to build a task to generate reports, migrate databases, repair some broken data. In a 12-factor application, these tasks are treated as first-class processes. They are themselves applications that are spawned when needed, usually by some overall management application. These processes run in their own execution environment, to completion, and then shutdown. This code base will then be treated as its own 12-factor application, including all of its own deployments. If the task needs to be part of a release, such as a process that sets feature flags, you need to do some triggering on the deployment of the main application. But that is usually not the norm. Often these tasks are batch processes that run at regular intervals, or asynchronous message listeners that react to a message on a queue or a topic. 

Some tools used for Admin process

Conclusion

Twelve-factor cloud native application development is a complete mind-shift within the organization. This is not a change that can be taken lightly and really needs buy-in from the entire development organization and management.

Bytize can provide professional consulting engagements to move teams into this model. To be fair, your developers have to have a complete shift in how they develop and deploy cloud native applications. The results, however, are worth the challenges. If you see a deployed feature in days instead of months, the return on investment for the development team becomes apparent. If your team is already embracing Agile methodologies, this transition will be almost natural. But it is still a transition.

The twelve-factor methodologies are not the silver bullet for solving distributed computing. As more systems move into this space, the pattern itself evolves. For more information, check out your favorite Cloud provider or visit 12factor.net, where you can find more from the original contributors.

Rajesh Saravanan

Rajesh Saravanan

CEO and Co-Founder at Bytize

Leave a Reply