Deployment vs Release: Why Your New Code Isn't Reaching Users Yet

You've just finished a deployment. The pipeline is green, the artifact is on the production server, and your team is ready to call it done. But when you check the application, users are still seeing the old version. Nothing changed on their end.

This moment confuses a lot of teams. You did everything right. The code is there. The server is running the new binary. So why aren't users getting the update?

The answer is simple: deployment and release are two different things. And understanding that difference changes how you think about shipping software.

Deployment Means the Code Is on the Server

Deployment is the act of placing a new version of your application onto a server. The artifact comes out of your registry, gets copied to the target environment, and the server starts running it. If you deployed to production, the new version is physically present on production servers.

That's it. Deployment is a technical action. It says nothing about whether users can actually use the new version.

Think of it like this: you've loaded a new movie onto the projector in a cinema. The film is threaded, the reels are ready, and the projector is powered on. But the movie hasn't started playing yet. The audience is still watching the previous film.

The following diagram shows the timeline and traffic flow between old and new versions during deployment, release, and canary release:

sequenceDiagram participant Dev as Dev Team participant LB as Load Balancer participant Old as Old Version participant New as New Version participant User as Users Dev->>LB: Deploy new version LB->>New: Start new version Note over New: Running but idle User->>LB: Request LB->>Old: Route to old version Note over LB: Release moment Dev->>LB: Switch traffic to new LB->>New: Route all users Note over LB: Canary release Dev->>LB: Route 5% to new User->>LB: Request LB->>New: Route some users LB->>Old: Route most users Dev->>LB: Increase to 50% Dev->>LB: Route 100%

Release Means Users Get the New Version

Release is the moment when the new version starts serving real user traffic. Traffic means the requests coming from users to your application. As long as traffic is still directed to the old version, users won't feel any effect from the new code sitting on the server.

The new version is there. It's running. But it's idle. Nobody is hitting it.

Release is when you switch the sign from "coming soon" to "now showing." The projector starts rolling, and the audience sees the new movie.

Why Bother Separating Them?

If you can deploy and release at the same time, why would you want to separate them? Because separation gives you control.

When deployment and release are the same action, every deployment becomes a gamble. You push code, users immediately see it, and if something breaks, everyone experiences the problem. There's no buffer between "we put it there" and "users see it."

When you separate them, you get a window. You can:

  • Deploy the new version and let it sit.
  • Monitor its behavior without user impact.
  • Verify that it starts correctly, connects to databases, and handles internal requests.
  • Fix issues before any user notices.

That window is your safety net. It turns deployment from a high-stakes event into a routine operation.

How to Separate Deployment from Release

The simplest method uses a load balancer or reverse proxy. Here's how it works:

  1. Deploy the new version to the server alongside the old version.
  2. Configure the load balancer to send all user traffic to the old version.
  3. The new version runs but receives zero external requests.
  4. When you're ready, update the load balancer configuration to route traffic to the new version.

That configuration change is your release. It can happen seconds after deployment, or hours later. The timing is up to you.

Here is a practical example using a hypothetical load balancer CLI to shift traffic:

# Deploy new version alongside old version
# (assumes both are already running on the server)

# Check current traffic distribution
trafficctl get-weight myapp
# Output: myapp-v1: 100%, myapp-v2: 0%

# Shift 10% of traffic to the new version (canary)
trafficctl set-weight myapp-v2 10%

# After monitoring, shift all traffic to the new version
trafficctl set-weight myapp-v2 100%

# Rollback if needed: instantly redirect all traffic back to old version
trafficctl set-weight myapp-v1 100%

Canary Releases: Rolling Out Slowly

A more refined approach is the canary release. Instead of switching all traffic at once, you send a small percentage of users to the new version first.

Say you have a thousand users. You start by routing fifty of them to the new version. If everything looks good after five minutes, you increase to two hundred. Then five hundred. Then all of them.

This approach limits blast radius. If the new version has a bug, only fifty people experience it instead of a thousand. You catch problems early, with minimal damage.

Canary releases work well with feature flags too. You can deploy code that's hidden behind a flag, enable it for a small group, observe the results, and gradually expand the audience.

Rollback Without Redeployment

Separation also makes rollback easier. If you release a bad version, you don't need to redeploy the old one. The old version is still on the server, still running, and still capable of handling traffic.

You just change the load balancer configuration back. Traffic shifts to the old version. Users are back on stable ground within seconds.

Compare that to a combined deploy-and-release approach. There, a rollback means:

  • Finding the old artifact.
  • Redeploying it.
  • Waiting for the server to restart.
  • Hoping the rollback itself doesn't introduce issues.

That process takes minutes at best, and often longer. During that time, users are hitting a broken application.

Who Decides When to Release?

Deployment is a technical decision. Your CI/CD pipeline can handle it automatically. But release often involves product or business stakeholders.

The product team knows whether the feature is ready for users. They might want to test it internally first, run an A/B test, or delay the release for marketing reasons. They understand the user impact in ways the deployment pipeline cannot.

This doesn't mean every release needs a meeting. For routine updates, you can automate the release after a brief observation period. But for significant changes, the decision to release should involve people who understand the business context.

A Practical Checklist for Your Next Release

Before you release a new version to users, run through these checks:

  • Has the new version been running in production for at least a few minutes without errors?
  • Are logs showing normal behavior?
  • Do you have a way to redirect traffic back to the old version if needed?
  • Has someone confirmed that the feature is ready from a product perspective?
  • Is the release window chosen to minimize user impact?
  • Do you know who to contact if something goes wrong after release?

This list is short by design. Overcomplicating the release process leads to skipped steps. Keep it simple, and actually run through it each time.

The Real Takeaway

Deployment and release are not the same thing. Deployment is putting code on a server. Release is letting users touch it. Treating them as separate actions gives you control over risk, rollback speed, and user experience.

Next time your pipeline turns green, ask yourself: did you just deploy, or did you actually release? The answer determines whether your users are getting the update or still waiting for the movie to start.