Shared-Database Blue-Green Deployments in Practice
Shared-database Blue-Green deployment lets teams update a database without users noticing, because the database temporarily supports both the existing 'Blue' application contract and the new 'Green' contract simultaneously.
This article examines Expand-Contract techniques for shared-database Blue-Green deployments that preserve backward compatibility during a phased transition window, as application instances are updated or traffic is redirected from Blue to Green. It covers abstraction and encapsulation, application dual-writes, database synchronization, and versioned parallel structures, explaining how they work and addressing practical questions about when to use each one and what can go wrong.
The role of Flyway Enterprise in shared-database Blue-Green deployments is to manage and automate the Expand and Contract phases of the database deployment. It allows backward-compatible changes to be introduced, tested, verified, and removed, with control and traceability, rather than relying on manual scripting.
The Basics of Shared-Database Blue-Green Deployments
The first article in this series introduced shared-database Blue-Green in the context of other techniques for supporting online database changes, explained the Expand-Contract pattern, and how Flyway Enterprise makes the technique safer and more reliable. This second article is about ways to put this into practice.
Implementing Expand-Contract
Let's say a new application feature needs a cleaner or more efficient database model and requires us to change the way customer data is stored and accessed.
During a shared-database Blue-Green deployment, the Expand phase will create a temporary "Blue + Green" database state that supports Green's new contract while still honoring Blue's old one. Depending on the nature of the changes required, there are two ways we could make this change in a backward-compatible manner:
- Use abstraction/encapsulation – update the underlying database structure as required but preserve the old database contract through 'proxy' or 'transition' objects in a database abstraction layer, so that any Blue clients or applications continue working during the transition period.
- Maintain old and new structures side by side – add the new database structure side by side with the old. During the transition window, we keep both versions of the schema consistent, either by using dual application writes, or synchronization logic in the database, such as triggers.
Abstraction/encapsulation is usually most attractive when applications already access the database through a stable interface layer like an API or CRUD stored procedures, and the old contract can be preserved without too much proxy logic.
Certain types of changes are simpler when the old and new structures co-exist physically for a while, but it introduces the harder problem of keeping data synchronized.
In both cases, most of the complexity is around the transition window when Blue and Green applications may both be reading and writing data. Our compatibility layer or synchronization logic must be robust enough to prevent data loss, data discrepancies, and integrity problems.
Preserve the 'blue' contract through abstraction/encapsulation
To isolate the application from structural database changes, we encapsulate CRUD operations in stored procedures. In this simple illustration, the application does not access the base Customer table directly. It calls stored procedure C in an application-owned interface layer, and C handles the access to the underlying base table schema.

In this starting state, all user traffic is through the blue app stack.
However, now a new (Green) feature requires customer status data to be handled in a more structured way, and we need to refactor the underlying Customer table.
Expand: backward-compatible schema changes
In the Expand phase, Flyway versioned migrations make the base-table changes and introduce the new stored procedure contract (C2) needed by Green.
To preserve Blue's existing contract, we keep its current stored procedure interface in place as a proxy, or transition procedure. Internally, we update it using a Flyway migration so that it reads from the new underlying structure but returns data in the shape Blue expects. The database is now in a temporary "Blue + Green" state:

At this point, all production traffic still goes through the Blue application stack. Before we send users to Green, we first prove that the expanded database state supports Blue correctly. If the interface layer is wrong, we fix that before the traffic shift begins.
Once the Expand phase is working safely, we have an application fallback path.
Phased rollout of changes
We can now start moving traffic from the Blue to the Green stack, by whichever phased rollout technique we choose, knowing that Blue can still operate against the expanded database if the Green app must be rolled back.

Contract: schema cleanup
When all applications are updated to the Green version and verified to be operating correctly, we run Flyway versioned migrations as controlled teardown scripts to remove the obsolete procedure C. In Refactoring Databases, this is the formal "deprecation period": every schema change must have a removal plan and date attached.

Pros and cons of the abstraction technique
This abstraction technique is often used when a legacy application cannot be changed but still needs to be supported, sometimes for an indeterminate period, against a newer schema. It is best suited to teams that already use stored procedures as a database interface, and to changes where the proxy logic needed to filter, reshape, or translate data back into Blue's expected format remains relatively simple.
The main advantage is that it retains a single source of truth for the data. We are not relying on data synchronization or dual application writes to keep two physical structures in step, which avoids a common source of subtle, hard-to-spot bugs.
Success depends, instead, on the design of the abstraction layer. As well as testing the new Green version, we also need to test the expanded Blue + Green database state, checking that Blue still behaves correctly and that there are no regressions or performance deteriorations before any traffic is moved to Green.
The Expand and Contract phases can be designed, implemented, and tracked using Flyway versioned migrations. Flyway Enterprise can generate the forward migration, such as V3, that creates the required interface objects. The team can then recreate and test the Blue + Green database state across environments, using the same versioned migrations that will later be deployed to production. If the interface objects are managed in a separate schema from the base tables, or by different teams, you might choose to manage them independently using separate Flyway projects.
Flyway can also generate the corresponding backward, or undo, migration (U3) that removes those objects again. Normally, an undo migration is used as a rollback script for its matching V migration. In this case, the Contract phase is a planned 'roll forward' from the intermediate Blue + Green state to Green, so the generated undo script can be reviewed, tested and used as the basis for this versioned cleanup migration (V4).
Maintain old and new database structures side by side
Sometimes the nature of the schema change means it is better or easier to create the new database structures alongside the old ones, rather than hide the change behind an abstraction layer.
In the Expand phase, we use Flyway to deploy the new database structures before deploying the Green application code that will use them. Backward compatibility is preserved by keeping the old structures in place for Blue, while ensuring that the old and new representations of the data remain consistent throughout the transition window.
In the Contract phase, once all traffic has moved to Green and the Blue fallback path is no longer needed, we remove the legacy structures.
The hard part is the transition window. While Blue and Green are both active, writes through one version must be reflected correctly in the other. There are two main ways to handle this synchronization, depending on the nature of the change: in the application layer, or in the database layer. In either case, this approach is best suited to short, tightly controlled transition periods.
Application layer dual-writes
For some column-level changes, such as renaming a column or introducing a replacement column with a new data type, the common approach is to add a new column alongside the old one, temporarily. In these cases, the simplest way to preserve backward compatibility during the transition window may be application-layer dual-writes.
This PlanetScale article explains the strategy well for a single application stack. For shared-database Blue-Green, it might look something like this:
- Expand
- Database: a Flyway migration adds the new column to the table alongside the existing one.
- Application:
- Green is expanded to write to both columns. It reads from the new column but falls back to the old column for any row written by Blue during the transition window (where the new column will be
NULL). - Blue reads and writes the old column only.
- Green is expanded to write to both columns. It reads from the new column but falls back to the old column for any row written by Blue during the transition window (where the new column will be
- Migrate data – data is backfilled from the old column to the new as a background job, while Blue still handles all traffic.
- Phased transition – once data consistency is verified, Green opens to a targeted subset of users; traffic progressively shifts across.
- Contract – once all traffic is on Green and rollback is no longer needed, Green stops writing to the old column, and a Flyway versioned teardown migration removes legacy columns.
Flyway manages the database side of this sequence: adding the new column as a controlled Expand migration and removing it in the Contract step once the old column is no longer needed. Flyway checks and drift detection help confirm that each environment is in the expected state before traffic moves to Green.
The main advantage of shared-database Blue-Green over the single-stack approach is that Green can be exposed gradually. If the new structure has missing data, a schema error, or a slow query, you find out against a small slice of users rather than everyone. If Green must be withdrawn, traffic can be routed back to Blue, provided the old column is still being maintained correctly.
Database layer: trigger-based data synchronization
If the new version of the application requires, for example, an entirely new table structure that will temporarily exist alongside the old table, then we probably need a database-level synchronization mechanism to keep the two structures aligned during the transition phase.
One possible option, hate them or love them, is to use database triggers. You'll need to use bidirectional triggers: one mirrors Blue's writes from the old table to the new, applying any required transformation; the other mirrors Green's writes from the new table to the old. You'll need an anti-recursion guard to prevent each trigger from firing the other in a loop.
Another option would be to use change data capture, but we'll focus on triggers here.
Expand
- Database: Flyway migration creates the new table alongside the existing one and installs the bidirectional triggers (T) to keep the two structures in sync.
- Application: Green is deployed to read from and write to the new table only. Blue reads and writes the old table only. Green receives no user traffic yet.

Data migration
Data is bulk-copied from the old table to the new as a background job, while Blue handles all traffic. With the triggers already in place, any writes arriving during the backfill are captured automatically.
Phased transition of user traffic
Once data consistency between old and new tables is verified, Green opens to a targeted subset of users; traffic progressively shifts across.

Contract
With all traffic on Green and the Blue fallback path no longer needed, we execute scheduled Flyway migrations to remove the triggers and drop the legacy table. Triggers left behind after the transition create long-term confusion, especially because they are less visible than tables, columns, or stored procedures.

Pros and cons of trigger approach
The main attraction of trigger-based data synchronization is that the old and new structures can exist side by side during the transition window. This gives us a direct way to test both versions: we can run equivalent business queries against the old Blue structure and the new Green structure, compare the results, and check that writes through either path are reflected correctly in the other. Once everything is working correctly, we also have a relatively simple rollback route.
The trade-off is complexity and risk. The transition window should be short, and the trigger logic should be narrow and well tested. A useful test pattern is to write data through the Blue path, query both the Blue and Green structures, and compare the JSON result sets (then test in reverse, writing through Green). See, for example, Basic Functional Testing for Databases Using Flyway. The triggers also need to be tested under concurrent writes, updates and deletes, with realistic data volumes and edge cases.
Database layer: versioned parallel data
This advanced technique uses versioned parallel schemas with role-based synonyms. It is perhaps the most literal expression of the idea of maintaining 'multiple schema versions' side by side, during the transition phase, each behind its own role in this case.
It's an approach you might consider if you already use versioned schemas, synonyms, or table switching to avoid downtime in ETL operations or to keep reports running when doing data refreshes. However, it does not seem to be widely used for Blue-Green online database deployments, so we won't cover it in detail.
How it works
We introduce a separate, versioned schema inside the same database. Blue runs against one schema version (e.g., v12), while the candidate release (Green) is deployed into a new schema (e.g., v13). Role-based synonyms (prod.Orders, next.Orders) abstract the version from applications and integrations; promotion to the Green version is then just a synonym switch rather than a code change.
In a Blue-Green deployment, Blue continues to use the current versioned structure, Green uses the new one, and promotion becomes a controlled switch from one version to the other. However, once both versions may accept writes, we also need a reliable way to keep data in the two versions consistent during the transition window.
Pros and cons of versioned parallel data
The main attraction is the clean cutover mechanism. If applications and integrations access objects through stable synonyms or role-based interfaces, the new structure can be built and tested while the current production structure remains in place.
It is also well-supported by Flyway's versioned migration model. If preferred, each of the versioned schemas could be managed using separate Flyway projects. This would allow each version to have its own controlled migration history, but it also adds project and lifecycle management overhead.
The big downsides are the added design and operational complexity. Synonyms add a layer of indirection that makes the database harder to understand. In SSMS, for example, Object Explorer may no longer show the full dependency picture. This will confuse less experienced team members, and even AI assistants, when they are trying to trace how the database is really structured.
In a shared-database Blue-Green deployment, old and new structures may both need to handle writes, which means reliable synchronization, careful testing, and disciplined cleanup are required. In many relational databases, complex relationships, cross-schema dependencies, and ownership boundaries can make this a very difficult option.
Scheduling Cleanup and Deprecation
Regardless of which approach we choose, temporary coexistence must eventually end. Refactoring Databases is explicit about this: every schema refactoring needs a deprecation strategy. If old columns, proxy procedures, triggers, synchronization logic or parallel structures are left in place indefinitely, the database accumulates confusion and risk. This is what the book calls database rot.
The Contract phase should therefore be planned from the start, not treated as a later tidy-up task. Once all applications have moved to Green, and the Blue fallback path is no longer needed, the deprecated objects should be removed through scheduled, tested Flyway migrations. Cleanup is a critical final stage of the refactoring process: it is what turns a temporary Blue + Green compatibility state into the final Green database design.
With Flyway, the cleanup migrations are created as part of the original change, so you can't miss them: they remain in Flyway's migration list until they are deployed.
Choosing the right technique
There is no single best way to preserve backward compatibility during a shared-database Blue-Green deployment. The right choice depends on questions around the nature of the schema change, how the application accesses the database, how much data must be moved or transformed, and so on.
| Approach | Complexity | Best used when | Main risk |
|---|---|---|---|
| Abstraction/encapsulation | Low to medium | Applications already access data through stored procedures or another stable interface (API) | Proxy logic becomes too complex or changes query behavior |
| Application-layer dual-writes | Medium | The change is relatively contained, such as introducing a replacement column alongside the old one | Application write logic becomes harder to test and retire |
| Trigger-based synchronization | Medium to high | Old and new structures must exist side by side, and synchronization can be narrow and short-lived | Trigger logic fails under edge cases, concurrency or load |
| Versioned parallel structures | High | Teams already use versioned schemas, synonyms or table-switching patterns, often around reporting or ETL workloads | Synchronization, ownership, cleanup become difficult to manage and the database logic is harder to understand |
Whichever approach we choose, the principle is the same: the database must temporarily support both the old Blue contract and the new Green contract, and that temporary state must be tested, tracked and removed once it is no longer needed.
Flyway helps by making those Expand and Contract steps explicit, versioned, and repeatable, so that compatibility objects are introduced, tested, and removed as part of a controlled migration process.
This document contains proprietary information and is protected by copyright law.
Copyright © 2026 Red Gate Software Limited. All rights reserved





Loading comments...