Release night, again
The developer connects to the server, pulls the latest code, runs the migration, clears the cache, restarts the service and checks the site. Most of the time it works. Sometimes a step is missed, or a file was edited directly on the server last month and gets overwritten, or the migration runs against the wrong database. Then the site is down, it is late, and the fix is being typed live into a terminal.
Releases have become something the team avoids. Changes pile up and go out in large batches, which makes each release even riskier.
Why hand deployments keep failing
- The steps live in someone's head or in an out-of-date wiki page.
- The server has drifted: hand fixes, installed packages and edited config that the code does not know about.
- What was tested on a laptop is not what runs on the server, because versions differ.
- Database changes are run by hand, in an order someone has to remember.
- There is no quick way back, so a bad release means fixing forward under pressure.
None of this is carelessness. Any process that relies on a person doing a dozen steps perfectly, often late at night, will fail sometimes.
What fragile releases cost
| Effect | What you see |
|---|---|
| Downtime | Customers hit errors during and after releases |
| Slower delivery | Changes are batched because releasing is painful |
| Bigger failures | Large batches make faults harder to find |
| Key-person risk | Only one person dares to deploy |
| Late nights | Releases scheduled out of hours, with tired people doing them |
The slower delivery row compounds. When releases are painful, the team batches changes. Bigger batches fail more often and are harder to diagnose, which makes releases more painful again. Teams in this loop often release less and less frequently, and each release becomes a small project of its own.
It also affects what the business asks for. If every change carries a risk of downtime, people stop asking for small improvements, and the product stays as it is for longer than it should.
How we make deployments boring
Boring is the goal. A release should be a routine event that anyone on the team can trigger.
- Write down how it is deployed today, including every manual step and server tweak. This alone often explains past failures.
- Put configuration and server setup into code, using containers (Docker) or infrastructure as code, so environments are built the same way every time.
- Build a pipeline with GitHub Actions, GitLab CI, Azure DevOps or a similar tool. Every change runs automated tests and produces one build.
- Deploy that same build to a staging environment first, then promote it to production. Nothing is rebuilt between the two.
- Script database migrations so they run automatically, in order, with a plan for reversing them.
- Make rollback one action: redeploy the previous build. Where the setup allows, use blue-green or rolling deployments so users do not see downtime during a release.
- Add checks after each deployment that confirm the key pages and APIs respond, and alert if not.
We fit the pipeline to your team. A small business with one app does not need a platform built for hundreds of services, and we will not build one.
What release day looks like afterwards
A developer merges a change, the tests run, it appears on staging, someone checks it, and it goes to production with a click. If something is wrong, the previous version is back just as easily. Releases can happen during the working day, in small pieces, and more than one person can do them.
Is this your release process?
- Deployments involve logging into a server and running commands by hand.
- Something goes wrong on a noticeable share of releases.
- Only one person knows how to deploy.
- You batch changes to avoid releasing often.
- Rolling back a bad release is hard or impossible.