The 12-Factor App: Modern Practices & Leapcell Guide
The Twelve-Factor App is a methodology for building robust, scalable, and maintainable modern web applications, originally articulated by developers at Heroku. It provides an institutional framework for solving common problems in application development and operations, especially within the cloud-native era.
Following these twelve factors helps developers create applications that are optimized for continuous deployment, high portability, and smooth scaling.
Leapcell is designed as a 12-Factor App platform. By following the Twelve-Factor methodology, you can leverage Leapcell's features to build and deploy your applications more efficiently.
This document builds upon the classic twelve factors, supplementing them with modern interpretations and best practices on the Leapcell platform.
The 12 Factors Modern Interpretation & Leapcell Practices
I. Codebase for One Service
One codebase tracked in revision control, many deploys.
All code for an application should be stored in a single repository using a version control system like Git. While multiple deployment environments (e.g., development, production) can exist, they must all originate from the same codebase.
Leapcell's deployment model is fundamentally based on this principle. We advocate for the best practice of "one service per repository," which enhances the clarity of Git commit history and vastly simplifies project management and comprehension.
II. Declare Dependencies in a Manifest File
Explicitly declare dependencies in a manifest file.
All dependencies should be explicitly declared via a manifest file (e.g., package.json
, requirements.txt
) and managed with an appropriate isolation tool.
Precise dependency management is crucial for ensuring service portability. If your build process is complex (e.g., requiring both system packages via
apt-get
and language-specific libraries viapip
), we strongly recommend creating abuild.sh
script to unify the build steps and guarantee a consistent environment.
III. Env as Config
Store config in the environment.
Strictly separate configuration (e.g., database URLs, API keys) from code and inject it through environment variables.
A modern best practice is to use a
.env
file for local development. On the Leapcell platform, your configured environment variables are automatically injected into the runtime environment, allowing your application to access them seamlessly and ensuring a smooth transition from local to production.
IV. Backing Services
Treat backing services as attached resources.
Any service the app consumes over the network (such as databases or caches) should be treated as a pluggable resource, attached via a URL or credentials stored in the config.
Leapcell's Serverless services embody this philosophy, featuring a read-only filesystem except for the
/tmp
directory. This design enforces the externalization of state, enabling high-velocity dynamic scheduling of our infrastructure and ensuring compute resources are provisioned to you at maximum speed. Additionally, Leapcell provides highly available PostgreSQL, Redis, and Object Storage services to effectively offload the burden of state persistence.
V. Build, Release, Run
Strictly separate build, release, and run stages.
This separation ensures that releases are immutable and can be easily rolled back. modern CI/CD pipelines automate this process, allowing for rapid and reliable deployments.
Leapcell encourages a clear distinction between your production branch (e.g.,
main
) and development branches. For every new commit to the production branch, Leapcell automatically triggers a new build and release, deploying your latest code to production and fully embracing the CI/CD workflow.
VI. Stateless Processes
Execute the app as one or more stateless processes.
The application should execute as a "share-nothing" stateless process. Any data that needs to persist must be stored in a stateful backing service.
this principle is crucial for achieving rapid horizontal scaling and ensuring that processes can be started or stopped at any time without losing state. Leapcell's Serverless services are designed with this in mind, allowing for seamless scaling and management of stateless processes.
VII. Port Binding
Export services via port binding.
The application should be self-contained and listen for requests by binding to a port.
The platform uses the port given by your application config to manages the routing of external traffic.
VIII. Concurrency
Scale out via the process model.
Handle increased load by scaling horizontally (adding more processes) rather than vertically (making a single process more powerful).
Leapcell's serverless mode automatically scales instances based on incoming traffic, ensuring optimal resource utilization and responsiveness, leveraging the benefits of a stateless process model.
IX. Disposability
Maximize robustness with fast startup and graceful shutdown.
Processes should be disposable, meaning they can be started or stopped at a moment's notice.
Leapcell would send SIGKILL signal to terminate unresponsive processes. you can configure your application to handle this signal and perform any necessary cleanup before exiting.
X. Dev/prod Parity
Keep development, staging, and production as similar as possible.
Closing the gap between environments reduces bugs that only appear in production.
We recommend using the same codebase with separate branches for development and production, and
env.local
files to manage environment-specific configurations.
XI. Logs
Treat logs as event streams.
An application should not concern itself with routing or storing its log files. Instead, it should write its event stream, unbuffered, to standard output (stdout
).
Leapcell uses a modern logging infrastructure: all logs are sent to a centralized service for analysis and monitoring, allowing you to search terms or filter logs based on various criteria.
XII. Admin Processes
Run admin/management tasks as one-off processes.
Any administrative tasks, such as database migrations, should be run as one-off processes.
Leapcell currently does not provide a pre-start command that runs before the application starts. You can execute such tasks during the build phase or run them manually on your machine.