A cloud-native application is not simply an application that runs in the cloud: it is an application designed to take advantage of the cloud, that is, conceived from the start to scale horizontally, tolerate failures, be deployed frequently and automatically, and run in dynamic environments (containers, orchestrators, managed platforms). To achieve this, the community has consolidated a series of proven patterns and principles that avoid reinventing the wheel and the typical mistakes. This lesson covers the most important ones: the 12-factor methodology, the sidecar and ambassador container patterns, the strangler fig migration pattern, auto-scaling, and externalized configuration. Mastering them lets you design resilient, portable, and operable systems, which are exactly the qualities the cloud rewards.

Contents

  1. The 12-factor methodology (summary).
  2. Sidecar pattern.
  3. Ambassador pattern.
  4. Strangler Fig pattern.
  5. Auto-scaling (horizontal and vertical).
  6. Externalized configuration.
  7. Common mistakes and tips.
  8. Exercises and solutions.

  1. The 12-factor methodology

The Twelve-Factor App is a set of best practices for building portable, scalable SaaS applications. Here is a grouped summary:

# Factor In one sentence
1 Codebase A single versioned repository per application
2 Dependencies Explicitly declared and isolated
3 Config In the environment, never in the code
4 Backing services Databases, queues, etc. as swappable attached resources
5 Build, release, run Separate and strict stages
6 Processes The app runs as stateless processes
7 Port binding The service is exposed through a port
8 Concurrency Scale via the process model (horizontally)
9 Disposability Fast startup and graceful shutdown
10 Dev/prod parity Development, testing, and production as similar as possible
11 Logs Treated as event streams to standard output
12 Admin processes One-off tasks as ephemeral processes

The three that have the most impact on cloud-native architecture: (3) config in the environment (we will look at it in detail), (6) stateless processes (allows freely scaling and replacing instances), and (9) disposability (instances must be able to die and be born without drama, the basis of resilience and auto-scaling).

  1. Sidecar pattern

The sidecar pattern (literally, the "sidecar" of a motorcycle) consists of deploying an auxiliary container alongside the application's main container, in the same Pod, sharing lifecycle, network, and volumes. The sidecar adds cross-cutting functionality (logging, proxy, security, synchronization) without modifying the main application.

apiVersion: v1
kind: Pod
metadata:
  name: app-with-sidecar
spec:
  containers:
    - name: application         # main container
      image: myapp:1.0
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
    - name: log-shipper         # sidecar: sends logs to a central system
      image: fluentbit:latest
      volumeMounts:
        - name: logs
          mountPath: /var/log/app   # shares the same volume
  volumes:
    - name: logs
      emptyDir: {}              # volume shared between the two

Explanation: the Pod has two containers. application writes its logs to /var/log/app. The log-shipper sidecar (Fluent Bit) reads from that same shared volume (emptyDir) and sends them to a central system. The application knows nothing about how its logs are exported: that cross-cutting responsibility lives in the sidecar. It is the basis of service meshes like Istio, where a sidecar proxy manages all network communication.

  1. Ambassador pattern

The ambassador pattern is a special type of sidecar that acts as an outbound proxy: the application talks to the ambassador as if it were the remote service, and the ambassador takes care of the details of the external connection (retries, circuit breaker, TLS encryption, sharding, monitoring). This way, the application is simplified and the network logic is centralized.

graph LR
    APP[Application] -->|localhost:6379| AMB[Ambassador / Proxy]
    AMB -->|retries, TLS, routing| EXT[(Remote service: DB / Cache)]

Explanation of the diagram: the application always connects to localhost (to the ambassador), ignoring where the remote service actually is. The ambassador resolves the actual connection and adds retries, encryption, and routing. Key difference from the generic sidecar: the sidecar usually adds functions that accompany the app (logging, metrics); the ambassador specializes in managing outbound connections to external services.

  1. Strangler Fig pattern

The strangler fig pattern (named after the plant that wraps around and replaces the tree that supports it) is the recommended strategy for migrating a legacy monolithic system to microservices incrementally, without rewriting everything at once (which would be extremely risky). The idea: you place a proxy/router in front of the monolith and gradually redirect functionalities, one by one, to new services, until the monolith is empty and is retired.

graph TB
    USER[User] --> ROUTER[Router / Facade]
    ROUTER -->|/payments new| MS[Payments Microservice]
    ROUTER -->|rest, legacy| MONO[Legacy monolith]

Explanation: the router receives all traffic. The "payments" functionality has already been extracted to a new microservice, so the router sends /payments to the microservice and everything else still goes to the monolith. Over time, more routes migrate to the new side until the monolith is empty. Advantage: you migrate with controlled risk, being able to roll back each step, and you deliver value continuously instead of in a risky "big bang" migration.

  1. Auto-scaling

Auto-scaling automatically adjusts capacity according to demand. There are two dimensions:

Type What it does When to use it
Horizontal (scale out/in) Adds or removes instances/replicas Preferred in cloud-native; requires stateless apps
Vertical (scale up/down) Gives more/less CPU and RAM to an instance When you cannot scale horizontally

In Kubernetes, the Horizontal Pod Autoscaler (HPA) adjusts the number of Pods according to metrics:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp-deployment      # which Deployment it scales
  minReplicas: 2                # never fewer than 2
  maxReplicas: 10               # never more than 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70   # target: 70% CPU usage

Explanation: the HPA watches the myapp-deployment Deployment. It keeps between 2 and 10 replicas and its goal is for average CPU usage to hover around 70%. If CPU goes above that, it creates more Pods; if it drops, it reduces them (without going below 2). For this to work, the application must be stateless (factors 6 and 9 of the 12 factors): any replica must be able to serve any request.

  1. Externalized configuration

Factor 3 of the 12 factors says: configuration does not go in the code. Credentials, URLs, and parameters that change between environments (development, testing, production) must be injected from outside, usually as environment variables or mounted files. This way, the same immutable image is promoted across environments by changing only the configuration, and there are no hardcoded secrets in the repository.

In Kubernetes this is done with ConfigMap (non-sensitive configuration) and Secret (sensitive data):

apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
data:
  LOG_LEVEL: "info"
  FEATURE_NEW_DASHBOARD: "true"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  template:
    spec:
      containers:
        - name: myapp
          image: myapp:1.0
          envFrom:
            - configMapRef:
                name: myapp-config     # injects all keys as env vars
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:           # secret, not plain text in the manifest
                  name: myapp-secret
                  key: db-password

Explanation: the ConfigMap stores non-sensitive parameters (LOG_LEVEL, feature flags). In the Deployment, envFrom.configMapRef injects all its keys as container environment variables. The database password is obtained from a Secret via secretKeyRef, so it does not appear in plain text in the deployment manifest. The myapp:1.0 image does not change between environments; only the ConfigMap and the Secret change. Remember that Kubernetes Secrets are only base64-encoded by default: for truly sensitive data, a dedicated secrets manager is advisable, and the approach should be validated with the security team.

Common Mistakes and Tips

  • Storing state on the instance. Sessions or files in local memory break horizontal scaling: use external storage (DB, cache, object storage). This is factor 6.
  • Hardcoded configuration or secrets in the repository. Always externalize configuration. A secret pushed to Git is a security incident.
  • Big bang migration. Rewriting a monolith from scratch usually ends badly. Use the strangler pattern to migrate incrementally and reversibly.
  • Auto-scaling a stateful app. If the app is not stateless, adding replicas does not solve the load and can corrupt data.
  • Overusing sidecars. Each sidecar consumes resources in the Pod; add them when they provide clear cross-cutting value, not because it is trendy.
  • Not designing for disposability. Implement graceful shutdown (handle SIGTERM, close connections) so that scaling and deployments do not cut requests off midway.

Exercises

  1. Why is the "stateless processes" principle (factor 6) a prerequisite for horizontal auto-scaling to work well?
  2. Your team must modernize a large insurance monolith without being able to stop the service. Describe how you would apply the strangler fig pattern in the first steps.
  3. Differentiate the sidecar pattern from the ambassador pattern with an example.

Solutions

  1. Because when scaling horizontally the traffic is distributed among many interchangeable replicas, and any request can land on any replica. If a replica stored local state (the user's session, in-memory data), the same user's subsequent requests could go to another replica that does not have that state, causing errors. With stateless processes, all replicas are equivalent and can be freely added, removed, or replaced.
  2. Initial steps: (1) place a router/facade in front of the monolith that receives all traffic without changing behavior; (2) choose a bounded, low-risk functionality (for example, querying a catalog) and extract it to a new service; (3) configure the router so that that route goes to the new service and the rest stays in the monolith; (4) verify, measure, and, if all goes well, repeat with the next functionality. Each step is reversible.
  3. Sidecar: an auxiliary container that accompanies the app, providing a cross-cutting function; for example, a log-shipper that collects and sends the application's logs. Ambassador: a specialized sidecar that acts as an outbound proxy to external services; for example, a proxy the app connects to via localhost that manages retries and TLS to a remote database.

Conclusion

You have gone through the patterns that define a cloud-native architecture: the discipline of the 12 factors (stateless, external configuration, disposability), sidecars and ambassadors to add capabilities without touching the app, the strangler fig to migrate monoliths safely, auto-scaling to adapt to demand, and externalized configuration with ConfigMaps and Secrets. All of them share one goal: resilient, portable, and operable applications. In the last lesson of the module we will see how to create and manage all this infrastructure in a reproducible and versioned way with Infrastructure as Code (IaC).

Application Architecture Course

Module 1: Fundamentals of Application Architecture

Module 2: Design Principles and Tactics

Module 3: Architectural Styles and Patterns

Module 4: Distributed Architectures and Microservices

Module 5: Event-Driven Architectures and Messaging

Module 6: Domain-Driven Design (DDD)

Module 7: Data and Persistence

Module 8: Cloud Architecture and Deployment

Module 9: Quality, Security and Observability

Module 10: Evolution, Governance and Case Studies

© Copyright 2026. All rights reserved