Building distributed, scalable, and resilient systems is a more and more difficult task. When we choose serverless architectures, it’s not different, even serverless giving us many of the things we need for this kind of solution.
In this context, the 12-Factor App model, first proposed by Heroku in 2011, is still a valuable guide to align good practices in development, operations, and maintenance of modern applications. And more: it fits almost naturally with the serverless model, but there are important differences.
In this article, we will explore each of the 12 factors, understand how to apply them in serverless environments (especially on AWS), and show examples from the special episode of the Sem Servidor podcast, with Evandro Pires, Caio Henrique Pardal, and Gabriel Oswaldo.
What Are 12-Factor Apps?
The 12-Factor App model defines a set of good practices to build modern SaaS applications that are scalable and portable. It is not a strict method, but a philosophy of architecture that tries to guarantee:
- Easy maintenance over time
- Portability between environments
- Horizontal scalability
- DevOps practices included by default
These factors can be used with monoliths or microservices, but they become even more important in serverless architectures, where services are small, temporary, independent, and highly scalable by default.
“The only thing we know for sure when we build software is that it will get worse over time,” said Gabriel Oswaldo in the podcast. “The 12 factors appear as a guide to slow down this erosion.”
Factor 1: Codebase
"One codebase tracked in revision control, many deploys"
The application should have one single versioned codebase, stored in a system like Git. Even if there are many deployments (like production and staging), they must come from the same codebase.
This is already well-known in software development. But in serverless, the challenge is how granular this codebase should be.
Even with many Lambda functions, the ideal is that each system has a clear and versioned codebase, with consistent deploys.
“It’s important to understand who is calling the Lambda, what is its context, and how the functions are grouped around a goal. That helps organize the codebase better.” — Caio Pardal
Serverless best practices:
- Use monorepos organized by business domain
- Group Lambdas by business context, not by single function
- Tools like Serverless Framework, SAM, or SST help with unified versioning
Factor 2: Dependencies
"Explicitly declare and isolate dependencies"
All dependencies must be declared clearly, isolated, and versioned, to make the application environment reproducible and predictable.
In serverless, each function has its own environment. Poorly managed dependencies increase build time, cold starts, and make maintenance harder. All of this results in something worse: unnecessary costs.
“Lambda Layers help share dependencies, but you need to be careful with versioning,” says Gabriel. “Sometimes it’s better to include dependencies inside the function than to create a generic layer that becomes a mess.”
Serverless best practices:
- Declare dependencies in
package.json
,requirements.txt
, orpom.xml
, isolated by function for better optimization, but calculate the productivity cost of this granularity - Use Lambda Layers with strict versioning
- Prefer small libraries (avoid big monolith libraries)
Factor 3: Config
"Store config in the environment"
Application configurations, like credentials, environment variables, and external URLs, must be outside the code, and possible to change without new deployments.
Environment variables are the best place to store configurations, but they should never be in the code.
“This is essential in distributed systems, because each function can have different configs,” explains Evandro.
Serverless best practices:
- Use AWS Systems Manager Parameter Store or Secrets Manager
- Never store tokens or keys in the code
- Use IAM to limit access by function
Factor 4: Backing Services
"Treat backing services as attached resources"
External services like databases, queues, caches, or APIs must be treated as attachable and replaceable resources, without needing big changes in the code.
In serverless, everything is a backing service: databases, caches, queues, authentication systems. Decoupling is already the default.
“The cool thing about serverless is that it makes you think plug-and-play,” says Evandro. “You connect S3, Dynamo, API Gateway... everything as decoupled resources.”
Serverless best practices:
- Treat everything as a service: S3, RDS, DynamoDB, Cognito, etc.
- Replace services without coupling (example: switch Redis for DynamoDB as cache)
- Use environment variables to configure URLs and access
Here we have an important discussion in serverless. On one side, some people say serverless forces vendor lock-in - because of these native connections with the cloud provider. On the other side, the 12-Factor App encourages using software engineering standards to reduce this dependency. So, it creates a good balance.
Factor 5: Build, Release, Run
"Strictly separate build and run stages"
These three stages in the app’s lifecycle (build, package, and execution) must be separated clearly. This allows control and traceability between versions.
In serverless, the build usually creates a zip package or a container image. The release can be done using CI/CD. The run happens in Lambda.
“Lambda already forces you to do this right,” says Gabriel. “You have to package, upload to the cloud, and use a pipeline.”
Serverless best practices:
- Use tools like CircleCI, GitHub Actions or even CodePipeline
- Automate deploys with Infrastructure as Code (IaC)
- Use blue/green or canary deployments — Lambda has this natively
Factor 6: Processes
"Execute the app as one or more stateless processes"
The application must run as one or more stateless processes. Persistent data or context must be stored outside the function.
This is the heart of serverless when using Lambda: temporary executions with no local state.
“Lambda is literally a stateless process that scales to zero” remembers Gabriel.
Serverless best practices:
- Store state in DynamoDB, S3, or external databases
- Avoid global variables inside functions
- Use Step Functions to orchestrate long-running processes
Factor 7: Port Binding
"Export services via port binding"
The app should expose itself as a service, listening on a defined port and accessible via protocol, without needing external application servers.
In serverless, this is done by API Gateway, AppSync, or EventBridge (to share only a few examples). The app “listens” to events or endpoints and reacts to that.
Serverless best practices:
- Define API contracts with OpenAPI/Swagger or event contracts with EventBridge, including versioning
- Use API Gateway or AppSync to expose the app
- Use separate handlers for each endpoint — this helps with portability
Factor 8: Concurrency
"Scale out via the process model"
Scalability should come from running independent processes in parallel, allowing horizontal scaling when demand increases.
Serverless already scales automatically per request. Each invocation creates a new isolated process.
“Scalability is built-in. You just need to make sure your code is ready for it” says Caio.
Serverless best practices:
- Check AWS concurrency limits (concurrency reserve)
- Use queues (SQS, Kinesis) to manage traffic spikes
- Avoid race conditions by using transactional control in the database
Factor 9: Disposability
"Maximize robustness with fast startup and graceful shutdown"
App processes should be disposable, they can start and stop quickly. This helps with resilience, continuous deployment, and scalability.
Cold starts are the main challenge here. But, with good practices, you can reduce the impact of it. And remember: microservices also have cold starts! When they need to scale, they take some time to start receiving requests — this is also a cold start. The difference is the scale and granularity.
“Lambda is born and dies. So everything must be ready to run from the beginning with no previous state. That’s pure disposability.” — Gabriel Oswaldo
Serverless best practices:
- Depending on the case, use lightweight runtimes (Node.js, Go)
- Keep dependencies small
- Use Lambda Provisioned Concurrency when necessary
Factor 10: Dev/prod parity
"Keep development, staging, and production as similar as possible"
The dev, staging, and production environments should be as similar as possible to avoid errors caused by differences.
Serverless helps a lot here. In traditional environments, keeping dev and staging close to production is expensive. In serverless, you can create environments that are very similar and low-cost — you only pay for what you use.
“We use a dev environment very close to production because everything is serverless. Tools like serverless-offline or localstack help, but the real benefit comes when you automate everything with IaC.” — Caio Pardal
Serverless best practices:
- Use separate stacks per environment with IaC (like Terraform, SAM or Serverless Framework)
- Automate deploys per environment
- Emulate services locally with localstack or SAM CLI — but many people prefer using the real cloud
Factor 11: Logs
"Treat logs as event streams"
Logs must be treated as continuous event streams, sent to an external system where they can be stored, indexed, and analyzed.
Lambda sends logs to CloudWatch automatically. The challenge is to collect, aggregate, and monitor them properly. And another challenge is cost. Careless logging in serverless can get expensive.
“CloudWatch is the default in serverless, but it’s not enough. We aggregate logs with Datadog and also use JSON format to make analysis easier.” — Gabriel Oswaldo
Serverless best practices:
- Aggregate logs using CloudWatch Logs Insights or AWS OpenSearch
- Integrate with Datadog, Grafana, Lumigo, or similar tools
- Use structured logs in JSON format — Powertools for AWS Lambda helps standardize this
Factor 12: Admin Processes
"Run admin/management tasks as one-off processes"
Admin tasks and scripts (like database migrations or maintenance commands) should be done in isolated processes, outside the main app flow.
In serverless, use one-time functions, automated scripts, or temporary jobs.
“With Lambda it’s easy to create admin scripts. We have several functions just for data maintenance, triggered by events or scheduled on EventBridge. That’s way better than mixing it with the main app.” — Gabriel Oswaldo
Serverless best practices:
- Use scheduled Lambda functions, Step Functions, or Fargate for batch jobs
- Never include maintenance scripts in the main app flow
- Separate admin functions with different IAM roles
Conclusion: Serverless and the 12 Factors Work Together
Many of the 12 factors are already built into serverless, but understanding the principles behind them helps avoid traps like too much coupling, messy deployments, or poor observability.
“The secret is to know the principles and apply them smartly. Serverless encourages good practices, but also needs responsibility.” — Evandro
The 12 factors are still a reliable guide to build software that ages well. And when applied to a well-architected serverless environment, they don’t just make sense — they increase your ability to deliver value with speed, scale, and quality.
🎧 Listen to episode 39 (only in Portuguese) of the Sem Servidor Podcast about the 12-Factor App — available on audio platforms or YouTube.
Sem Servidor Podcast
The Sem Servidor podcast is an initiative to share serverless and cloud computing knowledge in Portuguese.
🎙 Listen to many episodes on our website: semservidor.com.br
About Me
This is Evandro Pires. I'm a husband, father of two, but also an AWS Serverless Hero, Serverless Guru Ambassador, Entrepreneur, CTO, Podcaster, and Speaker.
Cut costs and boost innovation by building a serverless-first mindset with sls.guru
Join our team and help transform the digital landscape for companies worldwide!
Top comments (0)