A group of people within Heroku developed the 12 Factors in 2012. This is essentially a manifesto describing the rules and guidelines that needed to be followed to build a cloud-native application.
The twelve factors are
- Codebase – One codebase tracked in revision control, many deploys.
- Dependencies – Explicitly declare and isolate dependencies.
- Configuration – Store configuration in the environment.
- Backing Services – Treat backing services as attached resources.
- Build, release, run – Strictly separate build and run stages.
- Processes – Execute the app as one or more stateless processes.
- Port binding – Export services via port binding.
- Concurrency – Scale out via the process model.
- Disposability – Maximize robustness with fast startup and graceful shutdown.
- Dev/prod parity – Keep development, staging, and production as similar as possible.
- Logs – Treat logs as event streams
- Admin processes – Run admin/management tasks as one-off processes
Factor 1 – One codebase tracked in revision control, many deploys. There can only be one codebase per application. The codebase must be managed by a version control system. Various deploys are generated from this codebase, each one for a different environment—development, staging, and production. This looks too simplistic and non nonsensical but if you think about it in the world of SOA and microservices we have to think through our version control strategies. Each service should be maintained as its own codebase and should be version controlled independently.
However there is no one size fits all with codebase strategies and teams move from Mono repo to Multi repos based on various factors. Mono Repo is where all Microservices are housed within a single repository. In multi repo each microservice codebase is tracked in its own independent repo.
Factor 2 – Dependencies used by a project and their versions must be explicitly declared and isolated from code. Explicitly stating versions results in lower compatibility issues across environments. This also results in better reproducibility of issues occurring in specific version combinations. Dependencies not only include packages but also platforms, SDK’s etc. Package dependencies can be manged using package management tools like Nuget, NPM etc. Container technology simplifies this further by explicitly specifying all installation steps. It also specifies versions of base images , so that image builds are idempotent.
Factor 3 – Application configuration, that differ across environments such as external dependencies, databases, credentials, ports etc are manifested at runtime. This configuration should not be hard coded in the source code but dynamically modifiable from outside of the application. The use of environment variables that can be injected when deploying an application in a specific environment is recommended.
Factor 4 – Databases, API’s and other external systems that are accessed from the application are called resources. The application should be abstracted away from its external resources. These resources should be attached to the application in a loosely coupled manner. They should be replaceable by different instances without any impact to the application. Backing services should be abstracted into individual components with clean interfaces.
Factor 5 – A single codebase is taken through the build process to produce a compiled artifact. The output of the build stage is combined with environment specific configuration information to produce another immutable artifact, a release. Each release is labelled uniquely. This immutable release is then delivered to an environment (development, staging , production, etc.) and run. If there are issues this gives us the ability to audit a specific release and roll back to a previously working release.
Factor 6 – All processes and components of the application must be stateless and share-nothing. An application can create and consume transient state while handling a request or processing a transaction, but that state should all be gone once the client has been given a response. All long-lasting state must be external to the application and provided by backing services. Processes come and go, scale horizontally and vertically, and are highly disposable. This means that anything shared among processes could also vanish, potentially causing a cascading failure.
Factor 7 – A twelve-factor app is fully self-contained and does not depend on the use of an external server, such as IIS or NGNIX, to be exported as a service.It is self-contained and is never injected into any kind of external application server or container. A twelve-factor app must export the HTTP service by port-binding, meaning that the application also interfaces with the world via a URL. The application might run as http://localhost:5001 on a developer’s workstation, and in QA it might run as http://188.8.131.52:5000, and in production as http://service.company.com. An application developed with exported port binding in mind supports this environment-specific port binding without having to change any code.
Factor 8 – Applications should scale out using the process model. Elastic scalability can be achieved by scaling out horizontally. Rules can be setup to dynamically scale the number of instances of the application/service based on load or other runtime telemetry. Stateless, share-nothing processes are well positioned to take full advantage of horizontal scaling and running multiple, concurrent instances.
Factor 9 – Processes are constantly created and killed on demand. An application’s processes are disposable, and can be started or stopped rapidly. An application cannot scale, deploy, release, or recover rapidly if it cannot start rapidly and shut down gracefully. Shutting down gracefully implies saving the state if necessary, and releasing the allocated computing resources.
Factor 10 – All environments should be maintained to be as similar as possible.
Factor 11 – Logs should be treated as event streams.Logs are a sequence of events emitted from an application in time-ordered sequence. A cloud-native application writes all of its log entries to stdout and stderr.You can use tools like the ELK stack (ElasticSearch, Logstash, and Kibana), Splunk etc to capture and analyze your log emissions. Applications should be decoupled from the knowledge of log storage, processing, and analysis. Logs can be directed anywhere. For example, they could be directed to a database in NoSQL, to another service, to a file in a repository, to a log-indexing-and-analysis system, or to a data-warehousing system.
Factor 12 – Maintenance tasks, such as script execution for data migration, initial data seeding and cache warming should be automated and performed on time.