Skip to content

Commit adfb042

Browse files
add multi env deployments (#956)
1 parent 17c12eb commit adfb042

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
title: Database Multi-Environment Deployments - The Challenges and Patterns
3+
author: Adela
4+
updated_at: 2025/12/03 18:00:00
5+
feature_image: /content/blog/database-multi-environment-deployments/banner.webp
6+
tags: Explanation
7+
description: A guide to multi-environment deployments in databases.
8+
---
9+
10+
Most development teams work with several environments — Dev->Test->Stage->Prod, some also manage multiple tenants in production. Application code moves through this workflow fairly smoothly. Databases are a different story. They hold state, evolve over time, and can’t simply be redeployed or rolled back. As a result, promoting database changes across multiple environments often turns into a recurring source of friction.
11+
12+
## What does Database Multi-Environment Deployment Mean?
13+
14+
For databases, **multi-environment deployment** means taking the same change and rolling it out in a controlled way across:
15+
16+
- the environment pipeline (Dev → Test → Stage → Prod), and
17+
18+
- all relevant production databases (tenants, regions, clusters).
19+
20+
A typical flow looks like this:
21+
22+
1. Apply the change in Dev.
23+
24+
1. If it looks good, promote to Test and Stage.
25+
26+
1. Finally, promote to Prod.
27+
28+
1. In Prod, repeat the same change for each tenant/region that uses its own database.
29+
30+
Every step has to respect existing data, constraints, and workload. That mix of multiple environments plus multiple Prod instances is what makes database deployment very different from shipping a stateless service.
31+
32+
## Challenges of Database Change Deployment
33+
34+
In practice, database deployments hit the same problems again and again:
35+
36+
- **Environment drift**
37+
Someone runs a quick fix in Prod, or a migration is skipped in Test. Schemas no longer match.
38+
39+
- **Unclear migration state**
40+
Nobody is 100% sure which changes have run in which environment or tenant.
41+
42+
- **Out-of-order and conflicting changes**
43+
Feature branches introduce overlapping migrations; a hotfix lands in Prod before it exists in Dev.
44+
45+
- **Prod behaves differently**
46+
A trivial change in Dev can lock big tables or blow up queries in Prod because of larger data volume.
47+
48+
- **Different deployment paths**
49+
Not every service uses the same Dev→Test→Stage path, and some have extra staging steps.
50+
51+
- **Rollback is hard**
52+
Rolling back schema and data safely is much harder than rolling back a container image.
53+
54+
These are the reasons "just run this SQL everywhere" stops working beyond a certain scale.
55+
56+
## Patterns for Handling Multi-Environment Database Changes
57+
58+
### 1. Manual copy/paste scripts
59+
60+
The simplest pattern looks like this:
61+
62+
1. A developer writes SQL and runs it in Dev.
63+
64+
1. When it’s ready, they copy the same script to Test, Stage, Prod (and each tenant) — either running it themselves or raising a ticket so an admin/DBA can run it.
65+
66+
Even with admin approval in the loop, the failure modes are obvious:
67+
68+
- scripts get edited between environments
69+
70+
- someone forgets one step or one tenant
71+
72+
- too many tickets to approve, DBAs are overwhelmed
73+
74+
- different environments or tenants quietly end up with slightly different schemas
75+
76+
It’s fine for a small team and a handful of databases. It doesn’t age well.
77+
78+
### 2. GitOps: single source of truth
79+
80+
Another common pattern is to manage database changes the same way as application code:
81+
82+
- **Create once** – write a migration script for each change.
83+
- **Keep it in Git** – store migrations in a repo (either the app repo or a dedicated DB repo).
84+
- **Review it** – use pull requests for SQL review and approval.
85+
- **Promote it** – automation applies the same migration, in order, to Dev, Test, Stage, Prod, and then any tenant databases.
86+
87+
Teams tend to choose this model when they:
88+
89+
- want Git as the single source of truth
90+
- prefer PR-based review
91+
- already have CI/CD pipelines in place
92+
- need a clear versioned change history
93+
94+
Typical tools in this setup: Flyway, Liquibase, Alembic, Atlas, Bytebase.
95+
96+
Actually, **Bytebase** can fit into both worlds:
97+
98+
- as a **GitOps engine** that reads migrations from Git, maps them to environments/instances, runs SQL review rules, and deploys in order, and
99+
- as a **GUI-driven workflow** where changes are proposed, reviewed, and deployed through the UI for teams that don’t want to go full GitOps yet.
100+
101+
Manual copy/paste plus tickets, and GitOps with pipelines, are just different patterns. Which one works better depends on team size, existing tooling, and how much process you actually want.
102+
103+
## Process Enhancements
104+
105+
Once a team has picked a basic deployment pattern—manual scripts with tickets, GitOps with pipelines, or a mix—there are a few simple add-ons that make life much easier. These don’t replace your process; they sit beside it.
106+
107+
### 1. Diff tools (schema sync)
108+
109+
Instead of writing every change from scratch, you can let a diff tool compare two schemas and generate the SQL:
110+
111+
- "Make Stage look like Test"
112+
- "Bring this tenant up to the reference schema"
113+
114+
Examples: Atlas, Liquibase diff, pgdiff, or platforms that include schema diff features such as Bytebase.
115+
116+
### 2. Review process (SQL review and approval)
117+
118+
Before a change reaches Stage or Prod, it’s worth running it through:
119+
120+
- automated checks (linting, basic safety rules)
121+
- a human approval step for higher-risk changes
122+
123+
Examples: SQLFluff, sqlcheck, Git pull request review, or built-in SQL review/approval flows in tools like Bytebase (which can enforce rules in both GUI- and GitOps-based workflows).
124+
125+
### 3. Drift detection
126+
127+
Drift is when the actual database no longer matches what you think it should be:
128+
129+
- a hotfix was applied directly in Prod
130+
- a tenant missed a deployment
131+
- a migration failed halfway
132+
133+
Drift detection compares schemas and flags differences so you can fix them before the next rollout.
134+
Examples: Atlas, Liquibase diff, or drift detection features in platforms like Bytebase.
135+
136+
### 4. Rollback strategy
137+
138+
Because databases are stateful, rollback usually isn’t a clean "revert" button. Teams typically choose one or more of:
139+
140+
- forward-fix migrations
141+
- restore points or backups
142+
- reversible migrations where safely possible
143+
144+
The important part is agreeing on the strategy up front and wiring it into whatever deployment pattern you use.
145+
146+
### 5. Change history
147+
148+
When something breaks, you need to know **what changed, where, and when**.
149+
150+
Common sources:
151+
152+
- Git history for migration files
153+
- Change history with change tickets
154+
- audit logs
155+
156+
It doesn’t matter whether the trigger was "merge PR" or "DBA clicked deploy" — having a single place to look back is what saves time. This is essential for debugging and for compliance.
157+
158+
## Closing Thoughts
159+
160+
Multi-environment database deployment is hard not because the concepts are complicated, but because the system is stateful, long-lived, and spread across many environments and tenants.
161+
162+
The patterns that work share a few traits:
163+
164+
- there's a single source of truth for changes
165+
166+
- sql linting and review process are required
167+
168+
- ad-hoc SQL is minimized
169+
170+
- diff, review, and drift detection keep schemas aligned
171+
172+
- there’s a clear history and a realistic rollback story
173+
174+
Once those basics are in place, database deployment starts to feel much closer to application deployment—quiet, repeatable, and mostly boring, which is exactly what you want.
25.4 KB
Loading

0 commit comments

Comments
 (0)