X

Break New Ground

The Twelve-Factor App

The Twelve-Factor App manifesto, created by engineers at Heroku around 2011, contains twelve best practices for building software-as-a-service (SaaS) applications. Although cloud development has evolved since this manifesto came out, the principles still apply to cloud native application development. This post describes how.

Cloud development has evolved since this manifesto came out, but the principles still apply to cloud native application development.

1. Codebase

One codebase tracked in revision control, many deploys

Having a single codebase per application is recommended. You can deploy the applications into multiple environments (development, test, production, staging).

For cloud native applications, this means that each service or function would have its own code repository and CI/CD flow. You work on the code locally, you push commits to the code repository, and CI/CD builds and deploys your application.

2. Dependencies

Explicitly declare and isolate dependencies

Your applications need to explicitly declare and isolate dependencies. If you’re using a third-party library, package it with your application.

For example, in Node.js, you would use package.json to declare all dependencies. Similarly, in Python, you can use requirements.txt and then use package or dependency managers like npm, pip, or Maven to install the dependencies.

Isolating the dependencies is also essential. Never depend on the host machine to have any dependencies installed. You can rely only on your code to have dependencies packaged.

The use of containers has decreased dependency-based issues. With Docker, you can package everything together in an image. In a Dockerfile, you declare and install everything that’s needed: tools that your app needs and any other libraries. Packaging your application in such a way makes it portable, and you can run it anywhere in the same way.

3. Configuration

Store configuration in the environment

Don’t include configuration with your application. For example, if you connect to a database, don’t hardcode the connection string inside the Docker image.

Instead, define the connection string as part of the environment. You can use the same Docker image and deploy it into production, and your container reads the environment’s configuration settings. Making the configuration part of the environment lets you re-use the same application image.

4. Backing services

Treat backing services as attached resources

All applications have backing services: a database, a cache, or another service. The guidance here is to treat all backing services as third-party services and never connect to them directly.

If you combine this practice with the configuration one, you access the service through configuration settings from the environment. This practice also helps make your applications loosely coupled.

5. Build, release, run

Strictly separate build and run stages

After you package your application and have the configuration, you need to build, release, and run your services.

  • As part of the build stage, you transform the source code with its dependencies into a bundle known as a build.
  • The release stage takes the output of the build stage and combines it with the configuration to create a release.
  • In the run stage, you run the release.

Ideally, this process is fully automated and doesn’t require any human intervention. The builds and releases should be reproducible and have a unique identifier (usually the application version and a timestamp).

After you create a build or a release, you can’t change it. If you need to update a build or release, you need to restart the stages.

6. Process

Execute the app in one or more stateless processes

Your application shouldn’t rely on any state that is stored locally, either on a disk or in memory. Use the shared-nothing architecture, in which you don’t share the memory or storage.

In a cloud native environment, the infrastructure that you run your applications on can be replaced at any time. Therefore, you can’t know which hosts your application will be run on. Instead, store all application states externally, in a database, cache, or something similar.

Following this approach allows for elasticity, and you never have to worry about whether the state is on the host.

7. Port binding

Export services via port binding

This practice means that you should be able to address applications by specific port numbers. Doing so lets you use names when referring to them and correctly load balance between different instances and port numbers.

8. Concurrency

Scale out via the process model

Design your applications so that they can scale independently and horizontally to achieve better resource usage. You should also be able to run multiple instances of your application at the same time. You can accomplish this by share-nothing architecture and data isolation.

9. Disposability

Maximize robustness with fast startup and graceful shutdown

Applications can be started and stopped at any time. Strive for fast startup times, responsiveness, and graceful shutdowns of your applications.

With fast startup time, you can scale up faster when needed or quickly replace any down or failing instances. For graceful shutdowns, you can listen for a SIGTERM signal in the application. You can then close any open connections, flush the logs, or do anything needed to terminate the application gracefully.

10. Dev/prod parity

Keep development, staging, and production as similar as possible

If you follow the previous practices, you’re already packaging dependencies with your applications and using environment configuration. Doing all that also lets you quickly deploy the packages to different environments and keep them synchronized.

11. Logs

Treat logs as event streams

Keep logs and log management separate from the applications. Your applications should write logs to standard output, and a logging agent such as Fluentd should capture the logs and aggregate them in a central place. That way, even if your application crashes, the logging agent can capture the logs. Storing logs in a central place lets you to go back and investigate any application issues.

The logging strategy is crucial in a cloud native environment because there are many moving parts. Each log should give you visibility into the behavior of the application.

12. Admin processes

Run admin and management tasks as one-off processes

Run administrative or management tasks as short-lived processes. Examples of such tasks include migrating databases, repairing broken data, creating reports, and creating one-time scripts to fix something.

These processes should run in identical environments as the application, using the same release, codebase, and configuration. Ship the administrative code together with the application code so that you can avoid any synchronization issues.

Conclusion

In this post, you learned about the Twelve-Factor App manifesto and the best practices from it that you should follow when developing cloud native applications.


Every use case is different. The only way to know if Oracle Cloud Infrastructure is right for you is to try it. You can select either the Oracle Cloud Free Tier or a 30-day free trial, which includes US$300 in credit to get you started with a range of services, including compute, storage, and networking.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.