twelve factor applications
TRANSCRIPT
IntroductionA methodology, agnostic to language, to build applications that intend to
● Minimise developer onboarding cost
● Exhibit portability via a clean contract with the OS and wider
platform
● Are deployable on modern cloud platforms with low administration
● Embrace continuous, multi-environment deployment
● Scale without tooling, architecture or development practice changes
Background● Formalisation by the Heroku team
● The distillation from observing, measuring and managing millions of
SaaS apps and optimising best practices, dynamics of app growth,
developer collaboration while minimising software erosion
● Motivated to highlight some systemic problems and to offer a set of
broad conceptual solutions to those problems along with a shared
vocabulary
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Codebase● A single codebase tracked within a version
control system (git, svn etc.)
● Multiple code bases == distributed system
● Multiple apps sharing code == dependency
● Will be multiple deployments of the
codebase with potentially differing versions
Codebase - Why?● Ensures clear audit history
● Trivialises rollback; deploy v - 1
● Enables clear, visible progression to live; dev,
staging, production
● Facilitates concurrent team development
● Minimises inter-app refactoring risk
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Dependencies● Explicitly declare and isolate all dependencies; name, version, ...
● Make use of vendoring / bundling of dependencies
● Utilise tooling to automate the download and updating of
dependencies
● Never rely on the implicit existence of system tools
● Never rely on the implicit existence of system wide-packages
Dependencies - Why?● Ensures a clean contract with the execution environment
● Provides guarantees around inter-deployment consistency
● Outlines explicitly the real dependencies of the implementation
● Tooling simplifies the process of maintaining and updating
dependencies
● Minimises new-developer setup friction
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Config● Extract all config from code to ensure strict code / config separation
● Store config in the environment
● Store only environment agnostic config within config files
● Consider each config explicit to a deployment, not a config group;
development, test, staging, ...
Config - Why?● Config is everything that should change between deploys, it can be
seen as the “deployment variables”
● Config may contain credentials which should be restricted; Can the
codebase be made open source at any time without compromise?
● Grouped configs are not scalable; prod, qa, roja-laptop, branch-#232
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Backing Services● Treat backing services as attached resources
● Make no distinction between local and 3rd party services
● Each backing service is a resource; dev-db, prod-db, SQS
● Resources can be attached and
detached to deploys at will
Backing Services - Why?● Misbehaving resources can be swapped at will without code
modification
● Environment can expose relevant resources; test has test-db
● Prevents vendor lockin
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Build, release, run● Strictly separate integration stages
● Build; Create an executable bundle “build” from a version of the
codebase with vendored dependencies and assets
● Release; Combine build with deployment config to form a “release”
● Run; Execute bundle in an
environment enriched with
the config
Build, release, run - Why?● Restricts runtime code / config mutability to minimise non-determinism
● Facilitates continuous integration and automated rollback
● Ensures auditable versioned artifacts
● Allows for the automated re-execution and scaling of releases
dependent on hardware state
● Minimises run complexity
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Processes● Execute the app as one or more stateless processes
● All processes share nothing; all state must reside in a stateful backing
store
● No assumption is made around the state of the environments memory
● Any valid request should be severable from any executing instance
● Use of the transient filesystem should be highly discouraged
Processes - Why?● Robustness to restarts, re-deployments, configuration changes, …
● Agnostic to underlying hardware and memory state
● Simplified request routing and robustness to failure
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Port binding● Export services via port binding
● Do not rely on the injection of a web server within the execution
environment
● Not restricted to protocol; HTTP is just as valid as XMPP
● Any app can become a backing app to another app
● Port binding defined by environment
Port binding - Why?● Reliance upon a web server adds an un-vendored system
dependency
● Increased flexibility through protocol independence
● Simplifies multi-process and multi-environment hosting on singular
hardware
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Concurrency● Scale out via process model
● Processes can not span more than one
physical machine
● Should rely on the execution environment to
handle process crashes, restarts, shutdowns
Concurrency - Why?● Execution concurrency is simplified to the
execution environment running multiple
instances of a release
● No requirement for internal multiplexing or
threading, though does not preclude either
● Adding more concurrency becomes an
automatable trivial process
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Disposability● Applications are disposable; can be started and stopped with no
notice
● Startup and shutdown time is minimised
● Graceful shutdown is performed whenever a SIGTERM is received
● Workers shut down gracefully by returning their current job
● Job processing should be idempotent
● Crash-only design is implemented
Disposability - Why?● Enables fast elastic scaling, rapid (re)deployment or config changes
● Rapid startup aids production robustness by allowing process
migration
● Reentrant jobs ensure that work processing is recoverable
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Dev / prod parity● Keep development, staging, and production as similar as possible
● Keep backing services the same across environments; sqlite
● Minimise reliance on backing service adaptors; ORM etc
● Any adaptors that are used should be identical across environments
Dev / prod parity - Why?● Lightweight local services are less compelling than they once were
given trivial containerisation of most production alternatives
● Abstractions add complexity and minimise the exposed featureset of
the underlying service
● Even well aligned alternatives can introduce trivial difference and
thus expose differing behaviour between environments
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Logs● Treat logs as event streams
● Always log to stdout, and only stdout
● Let the environment handle the distribution, routing and storage of
logs
Logs - Why?● Logging code is substantially simplified
● Interception and re-routing becomes a concern of the environment and
therefore removes code change requirements
● Logs can be expanded to output ever-increased verbosity which can
then be fed into analysis tools; splunk, kibana, ...
● Log storage can be centralised trivially without application
awareness
The Twelve Factors
I. CodebaseII. DependenciesIII. ConfigIV. Backing ServicesV. Build, release, runVI. Processes
VII. Port bindingVIII. ConcurrencyIX. DisposabilityX. Dev / prod parityXI. LogsXII. Admin processes
Admin processes● Run admin / management tasks as one-off processes
● Tasks should be part of the codebase
● Tasks should be run within the environment within which they should
affect
● Environments should expose a REPL where feasible
Admin processes - Why?● Code synchronization issues are reduced through the shared code
base
● As config, release and dependencies are shared the task cannot
affect incorrect dependencies or act based on incorrect configuration
● REPL enables direct interaction and simple running of tasks from
within the environment
see: 12factor.netme: linkedin.com/in/rojabuckjobs: tech.just-eat.com/jobs/
Questions?