At Heroku, we’ve had the privilege of running and managing millions of amazing apps built by our users. Over a year ago, Heroku co-founder Adam Wiggins published the Twelve-Factor App, based directly on these experiences. It distills best practices for building modern cloud applications into a 12-factor methodology specifically designed to maximize developer productivity and application maintainability.
Twelve Factor apps are built for agility and rapid deployment, enabling continuous delivery and reducing the time and cost for new developers to join a project. At the same time, they are architected to exploit the principles of modern cloud platforms while permitting maximum portability between them. Finally, they can scale up without significant changes to tooling, architecture or development practices. Because the Twelve Factor methodology is a core part of how successful apps are built on Heroku, it is now documented in our Dev Center.
In this post, we review the aspects of Heroku that help you build apps based on these tenets. For instance, decomposing your app into a set of lightweight processes is a prerequisite for running on Heroku and is the embodiment of the Twelve Factor principle of executing the app "as one or more stateless processes". However, there are several other, subtler, factors that should also be considered when deploying to Heroku, or any modern distributed environment. For those not familiar with the original Twelve Factors, we'll summarize them here. For developers that are already familiar with the Twelve Factor methodology, we'll re-introduce them in the specific context of building and deploying to Heroku.
During development
Applications should be thought of as self-contained, self-describing and independent entities. In practice this means each application should declaratively define all dependencies without any reliance on existing system packages. Nor should it embed the location of any external dependencies in the app source, instead relying on such configuration to be specified in the runtime environment.
Developers will immediately recognize their buildpack’s use of language-specific tools, such as Ruby's Bundler and Clojure's Leiningen, as providing the necessary level of dependency declaration and isolation here, and will be familiar with Heroku's heroku config:set
CLI command used to update external dependencies without modifying the app's source code.
Using modern language toolchains alongside the strict separation of code and config that the Heroku CLI and Add-ons program require ensures a portable application and provides a set of environment-independent practices that enable you to run in the same fashion locally as your app does in production.
At runtime
Apps on Heroku are subject to several runtime parameters that ensure the app can be efficiently run in a distributed environment and is resilient to adverse conditions. These are in-product manifestations of several best-practices in application operation.
Consider the ephemeral filesystem which ensures a stateless runtime, or the fast process startup/shutdown requirements which result in an application whose runtime distribution can be quickly adjusted in response to underlying system deviations.
One of the most visible and fundamental concepts of Heroku is that of the process model. By running your application as one or more lightweight processes, and not a monolithic executable, you can more granularly scale the app to match a diverse workload — a sort of in-app horizontal scaling.
The Procfile
, your app’s process manifest, informs the runtime of your app’s unique composition and tools like Foreman (locally) and the Heroku dyno manager (remotely on Heroku) manage its execution.
When managing
Beyond the automated runtime service provided by the Heroku platform, it must also be possible to execute user-initiated tasks against the app as well as see the app's runtime log output. In compliant applications, these management and visibility requirements are well supported on Heroku.
To execute a one-off task, such as a database migration or arbitrary command via REPL, simply provision a one-off dyno. To see your app's log output, configure your app to log to stdout and its real-time log stream, aggregated across the distributed Heroku runtime, will be available via the CLI with heroku logs -t
or in one of the many logging add-ons.
Conclusion
Heroku is not only a platform; it is also a set of practices that embody the right way to develop modern applications. Understanding and applying these practices when developing your app ensures the most resilient and performant experience on Heroku.