Three Levers for Safer Releases: Traffic, Cohorts, and Feature Flags
You have a new version of your application ready. Tests pass in staging. The team feels confident. But you still hesitate before hitting deploy to production. That hesitation is healthy. Every deployment carries risk, and the safest way to reduce that risk is to not send the change to everyone at once.
The idea is simple: release gradually. But gradual means different things depending on what you control. When you decide to not ship a change to all users simultaneously, you have three independent levers to pull. Each controls a different aspect of the release. Understanding when to use each one, and how they combine, is what separates a controlled rollout from a gamble.
Traffic: Controlling How Much Load Hits the New Version
The most straightforward way to release gradually is to control the proportion of requests that reach the new version. Instead of routing all traffic to the updated application, you send only a fraction of it. Five percent of all requests go to the new version. The remaining ninety-five percent continue hitting the old version.
This approach is called traffic splitting. You configure it at the infrastructure level: your load balancer, service mesh, or API gateway. The configuration is simple. You specify a percentage, and the infrastructure distributes requests accordingly. No user identity is involved. No targeting logic. Just a raw proportion of network traffic.
Traffic splitting is ideal for the earliest validation stage. You want to know whether the new version crashes on startup, throws errors under real load, or consumes more memory than expected. Because the sample is random, you get a quick signal about basic health. If error rates spike, you stop the rollout before most users are affected.
Here is a realistic example using Istio's VirtualService to split traffic 5% to the new version and 95% to the old version:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp
spec:
hosts:
- myapp.example.com
http:
- match:
- uri:
prefix: /
route:
- destination:
host: myapp
subset: v2
weight: 5
- destination:
host: myapp
subset: v1
weight: 95
The limitation is obvious: you cannot control who gets the new version. The same user might hit the new version on one request and the old version on the next. That inconsistency is fine for infrastructure validation, but it becomes a problem when you need to observe behavior over time or collect feedback from specific users.
Cohorts: Controlling Who Gets the New Version
When you need to target specific users, traffic splitting is not enough. You want internal testers to see the new version first. Or beta users. Or users in a specific region. Or accounts with a particular subscription tier. This is where cohorts come in.
A cohort is a group of users defined by a criteria: user ID range, geographic location, account type, business segment, or any attribute your system knows about. Instead of routing a random percentage of traffic, you route all traffic from a specific cohort to the new version. This is called a staged rollout.
The advantage over traffic splitting is traceability. When you release to a cohort of internal users, you know exactly who is seeing the new version. If they report issues, you can correlate the problem with the specific change. If the cohort is small and controlled, the blast radius is contained. You can observe real usage patterns, not just error rates.
Cohorts also enable progressive expansion. You start with internal users, then expand to beta users, then to five percent of production users in a specific region, then to twenty-five percent, and so on. At each stage, you have a known group of users providing signal. The rollout becomes a series of deliberate expansions rather than a single binary decision.
The trade-off is complexity. You need the infrastructure to identify and route users based on attributes. Your load balancer or gateway must understand user identity, not just request paths. This often requires integration with authentication systems, user databases, or session management. It is more work to set up than traffic splitting, but it gives you much richer control.
Features: Controlling What Functionality Is Active
Traffic and cohorts control which version of the application a user runs. But sometimes you want to deploy the new version everywhere and selectively activate specific features within it. Maybe the new version is already running on all servers, but a particular feature should not be visible to everyone yet. Or you want to enable a feature for a subset of users without redeploying.
This is where feature flags come in. A feature flag is a conditional switch in your code that determines whether a feature is active for a given user. The flag can be toggled at runtime, without a new deployment. You can enable the feature for ten percent of users, for internal accounts, for users with even-numbered IDs, or based on any logic you define.
Feature flags decouple deployment from activation. You can deploy the code that contains a new feature days or weeks before the feature is actually turned on. This reduces the risk of deployment itself, because you are not changing behavior at the same time you are changing code. If something goes wrong with the feature, you disable the flag. No rollback needed. No redeployment. Just a configuration change.
The power of feature flags is granularity. You can target individual users, run A/B tests, gradually ramp up exposure, or instantly kill a feature that is causing problems. But that power comes with responsibility. Feature flags add conditional logic to your codebase. Too many flags, or flags that are never cleaned up, create technical debt. Every flag that stays in the code after its feature is fully rolled out becomes dead weight.
Combining the Three Levers
These three components are not mutually exclusive. In practice, a mature progressive delivery strategy uses all of them together.
The diagram below shows how the three levers work together in a typical progressive delivery sequence.
A typical sequence might look like this:
- Deploy the new version to a small percentage of traffic using traffic splitting. Validate that the application does not crash or leak memory.
- Expand to a cohort of internal users using staged rollout. Collect feedback and observe behavior patterns.
- Once confident, deploy the new version to all servers but keep a new feature behind a feature flag. Enable the flag for ten percent of users.
- Gradually increase the feature flag percentage while monitoring metrics. If something goes wrong, disable the flag instantly.
Each lever gives you a different kind of control. Traffic controls how widely the new version spreads. Cohorts controls who is affected. Features controls what functionality is active. When you understand all three, you can design a release strategy that matches your risk tolerance and your validation needs.
A Practical Checklist
Before your next release, ask these questions:
- Do I need to validate basic infrastructure health first? Use traffic splitting.
- Do I need feedback from specific users? Use cohorts and staged rollout.
- Do I want to deploy code now but activate features later? Use feature flags.
- Do I have a way to measure whether the new version or feature is safe? If not, do not start the rollout yet.
The Takeaway
Gradual release is not one technique. It is three independent controls that solve different problems. Traffic splitting validates infrastructure. Cohorts validate user experience. Feature flags separate deployment from activation. The teams that release safely are the ones that know which lever to pull, and when.