It's best practices in developing web applications that guarantee smooth implementation, scaling, and operationalization of the solution.
It has three phases of evolution:
12-Factors app for building a scalable solution
15-Factors app for meeting cloud native and microservices architecture
Moving forward for cloud native architecture
It's best practices in developing web applications that guarantee smooth implementation, scaling, and operationalization of the solution.
The development comes from Heroku, the PaaS platform, as principles for building cloud-native scalable applications, so it comes from the real practices, not a theoretical point of view.
Scalability
Environment consistency
Flexibility
Cloud Native
Operationalization
Continious improvement
Practice: One codebase tracked in version control, many deploys
Having one single repository for source code, managing different branches that should be deployed on each environment, and a feature-based branch in order to have the proper level of traceability.
Old Approach: Multiple codebases for different environments, instead of One codebase tracked in version control, many deploys
Benefits:
Simplify Version control
Simplify collaboration
Enablement for release train
Example:
Master branch for production
Development branch
Staging branch
Feature-based branch
Fixes branch
Ad-Hocs branch
Related Design patterns:
NA
Practice: Explicitly declare and isolate dependencies
Isolate solution dependencies, instead of using the system's internal components, as it should be explicitly declared and isolated.
It can be:
External integration components
Databases
As implementation practices, you have package.json in Node.js, Pom.xml in Java, and build. gradle in Java, as well as .csproj in C # projects.
Old Approach: Relying on system-level packages, manual installs, instead of Declare all dependencies explicitly and isolate them
Benefits:
Ensure consistency across environments.
Readiness for portability and shifting the solution from environment to another
Example:
Git repo with multiple deployments (prod, staging).
Related Design patterns:
NA
Practice: Store config in environment variables:
Store configuration in the environment, not in the code.
Old Approach: Hardcoded configs in code or files (e.g., web.config, .env), instead of Store configuration in environment variables
Benefits:
Separates config from code; enhances security.
Increase maintainability
Increase operationalization
Example:
Database connection strings
Average price calculation strategy
Default currency
Related Design patterns:
External configuration design pattern
Realtime reconfiguration design pattern
Practice: Treat backing services as attached resources.
Each service that you are using should not take effort if We need to replace it.
For example, if you have to replace the SMS gateway service, you do not need to reimplement it again within the code, which implies implementing the configuration practice.
Old Approach: Hardwired service bindings (e.g., fixed DB address in code), instead of Treat backing services as interchangeable resources
Benefits:
Facilitates swapping services without code changes.
Example:
Switch from local SQL Server to cloud DB without code change.
Changing the SMS gateway without code change
Use Redis instead of in-memory cache
Related Design patterns:
External configuration store
Practice: Strictly separate build and run stages.
At this Principle, We segregate between the 3 phases:
Build: Convert code into a runnable package, which includes: Compilation, bundling assets, and resolving dependencies.
Release: Combine the build with config (env vars) to create a release, Including Assign version, attach config/secrets.
Run: Execute the app in an environment using the release package.
Old Approach: Manual steps, no separation between build/release/runtime, instead of Separate build (compile), release (package), and run (execute)
Benefits:
Enhances deployment reliability and rollback capabilities.
Example:
Pipeline CICD
Docker Image build
Related Design patterns:
NA
Practice: Execute the app as one or more stateless processes
What?
When the application is stateful, it relies on saved user session data.
Your solution should be implemented in stateless sessions, in order to serve multiple requests, without additional overheads on the server.
Old Approach: Long-running monolithic apps with internal session state, instead of Stateless processes, disposable and scalable
Benefits:
The stateful model will be on the database level, and the handling for the state will be injected at the implemented business layer, in order to:
guarantee that the solution will run properly in case it needs to be restarted.
Application of the retry pattern
Application of scheduler jobs pattern
Resiliency, especially in microservices architecture
Where to store the state?
in stateless architecture, the status is not recorded in the code, it's stored in a different layer:
Enterprise Caching layer
Database layer
Example:
Web APIs for integration with the mobile solution
Background scheduler jobs
Transaction compensation design patterns
Related Design patterns:
NA
Practice: Export services via port binding
Moving forward to self-contained services, without the need to an application server, like Apache or IIS, as the old approach requires an application server, and it binds the application to a specific IP and port.
In this principle, the application/services will be self-exposed via specific port binding, even hardcoded, or configured from external configuration/
Old Approach: Depends on external servers like Apache/Nginx to serve the app, instead of Self-contained service exposing a port (e.g., via Kestrel in ASP.NET Core and container-based microservices )
Benefits:
Simplifies service discovery and interaction.
Portability, as you have hundreds of microservices, you will not be able to configure each one, which makes it suitable for containerization
Security
Example:
NodeJS Apps: express.listen(5000)
Related Design patterns:
NA
Practice: Scale out via the process model
This principle focus on the ability of the software for scale-out and scale-down according to the workload, without modification in the solution architecture.
Within OpenShift, you can configure the minimum PODs, the maximum PODs, and when to extend the PODs.
So, the infrastructure level detects the workload, and creates new instances from the application to serve operational needs.
Old Approach: Scaling by adding threads or servers manually, instead of Scale out using multiple processes/instances per workload type
Benefits:
Enhances application performance and availability.
Enhance scalability
Example:
Openshift MinMax scaling
Related Design patterns:
NA
Practice: Fast startup and graceful shutdown
The objectives of this principle are:
Easy to start up
Easy to scale out
Easy to scale down
Easy to shut down, with the cleaning resources
Old Approach: Slow startup, unsafe shutdown, poor resilience, instead of Fast startup and graceful shutdown
Benefits:
Improves deployment agility and resilience.
Related Design patterns:
NA
Practice: Keep development, staging, and production as similar as possible
Make the environment similar, for the same dependencies, tools, and database, otherwise, containerization.
The objectives of this principle is to prevent any risks when moving the code from development to the production environment, as a results from the variation between environments, different components and factors.
Old Approach: Different tools/versions/configs between dev and production, instead of Keep development, staging, and production environments as similar as possible
Benefits:
Reduces environment-specific bugs.
Related Design patterns:
NA
Practice: Treat logs as event streams
As a large-scale solution, in order to confirm that your solution is alive, in a healthy state, you should have a continuous stream of logs, that capture what is running in your solution.
It should be independently implemented, like EFK
Old Approach: Store logs in local files or ignore them, instead of Stream logs to stdout/stderr and aggregate externally
Benefits:
Facilitates centralized monitoring and debugging
Related Design patterns:
NA
Practice: Run admin/management tasks as one-off processes
You have 2 types of functionalities:
Business functionalities, which should be embedded within your business modules
Administrative functionalities, which should be embedded within your business modules, to enable the operationalization of the solution, like:
Managing users
Data cleansing
Backup/restore operations
Old Approach: Manual admin tasks on production machines (risky), instead of Run admin tasks as one-off processes inside the app context
Benefits:
Keeps admin tasks consistent with the application environment.
Related Design patterns:
Scheduler jobs
Adding 3 new factors:
API-First architecture/strategy is a new architecture approach the shift the mindset of software development for APIs to consider APIs as independent product, with different journeys in order to achieve business objectives, independent from the UX journey that may mislead/abuse the API architecture and design.
You can just imagine it if you just think that your final product is the APIs, and you will commercialize it alone, without UX.
It means, you have API as a product, so you have:
Product manager
Product Roadmap
And product lifecycle
As example, the target persona of your product (APIs) which will use your product, like mobile app, web app, and smart watch.
So, We are starting with the API, not the application.
So, It's about "How will you image the solution upon the API first, not how to build the API upon the design first solution".
Async Development
Reduce development Cost
Reduce time-to-market
Same like software engineering lifecycle, passing all journeys as the opposite diagram mentioning, considering that your product is APIs, including its documentation and journeys.
As a pre-requisites, We should build the culture, which implies the following principles:
Your API is a product
Foundational design, not ad hoc retrofit
Team collaboration and impact
API-first supports microservices
The API contract
Step 1: Create API Micro-Service architecture according to DDD, which each object has the following attributes:
Who Am I? I'm Employee Class
What I know? I know my code, name, birthdate, current salary, current role.
What I do? I can Create employee, delete employee, activate employee, deactivate employee, update employee, and block employee, and unblock employee
What is my state? like : blocked employee, Unblocked employee, Active employee and Inactive employee
Step 2: Determine key domains
Step 3: Determine each domain lifecycle
Step 4: Model your architecture domains
Step 5: Model your architecture sequence diagrams
Step 6: Model your architecture state-chart diagrams
Step 7: Develop CRUD APIs according to data models characteristics
Step 8: Create the journeys according to the sequence diagrams, with validating the pre-requisites
Step 9: Create APIs to validate product states
Step 10: Create Security model for the APIs
API Improvement model
As opposite figure, We have consumer and publisher, They are shared improvement the model, due to the required improvement after design, as a nature of any product lifecycle.
Are You API First Company?
Since you have the following characteristics, you can consider yourself as API-First company:
You have APIs that operate and maintain your data models
You are providing you APIs as independent product
You make APIs available to your customers and partners as a source of your revenue stream
You know how to Manage and discover your APIs
You have standardized processes to build APIs
Your APIs is independent from any UX design
API versioning is a practice in software development that involves managing and maintaining different versions of an Application Programming Interface (API). An API is a set of rules and protocols that allows one software application to interact with and request services or data from another software component, such as a web service or library. API versioning is essential to ensure that changes and updates to an API do not break existing clients or applications that rely on it.
Compatibility
Preventing Breaking Changes
Client Isolation
Sunset enabling
Business security and governance (Like creating API banking platform using API first, and then building the application upon)
So, it meet the strategic approach for the enterprise driven from the business transformation team, according to the following perspectives:
How will you build your upcoming products
How will your external entities integrate with your product
How will you monetize your product
How APIs will cover your overall business
Different deployment
Headers
URI routing
Level 01:
•Change management
•Control integration touch points by TL
•Announcement model for integration touch points
•Application of unit testing for integration packages
Level 02:
Apply selected Versioning streategy
Enable support period for version minors (Quarter based)
Backward compatibility according to release management process, which may be monthly, or quarterly or yearly
Quarterly code refactoring process to reset minor version backward compatibility
Handle legacy Versions : Responses and stoppage
Level 03:
Application of design patterns: Aggregation pattern, to reduce the touch service, which gives the team the freedom for changing the signature
API Catalog Model completeness, which should be deployed on API management platform, like SWAGGER, including its documentation
Level 04:
Provisioning model for monetization: which will be used in the access of authentication and authorization, for the given tokens, to enable who can do what, and enrich monetization of your exposed APIs
Telemetry means automatic gathering for measurements that are used in data analysis during the application journey, in the operation platform, so it's different than logging.
At Telemetry, We have different measurements and analytics at each layer.
The objectives:
Observability
Incident Response
Performance Optimization
Security Monitoring
Tools:
Logging: ELK (Elasticsearch + Logstash + Kibana), Loki
Metrics: Prometheus, Grafana
Tracing: Jaeger, Zipkin, OpenTelemetry
All-in-One: Datadog, New Relic, Dynatrace, Azure Monitor
There are five new approaches:
Containers (Docker)
Orchestration
Microservices architecture
Serverless architecture
DevSecOps
In software engineering, containerization is operating-system–level virtualization or application-level virtualization over multiple network resources so that software applications can run in isolated user spaces called containers in any cloud or non-cloud environment, regardless of type or vendor
Kubernetes orchestration allows you to build application services that span multiple containers, schedule containers across a cluster, scale those containers, and manage their health over time. Kubernetes eliminates many of the manual processes involved in deploying and scaling containerized applications
Adding security at the pipeline level, which includes SAST (Static Application Security Testing), in order to detect potential vulnerabilities before going to production.
One of the most welknown tools is SonarCube (Sonar Cloud), which provide on-prim, and cloud-based services, that can be injected on the pipeline.
How it works?
Enable on code branch
Create a quality gate
Analyse code manaually or on build
Manage the build pipeline according to the quality gate
Dr. Ghoniem Lawaty
Technology Evangelist