Skip to main content
Back to Blog
IT StrategyDevSecOpsDatabaseMigrationChangeManagementDataIntegrityDevOpsCloudMigrationDataSecurityITConsulting

How to Evolve Database and API Schemas in Production Without Downtime

Eldar Aydayev· CEO, Aydahwa Enterprise September 3, 2026 14 min read
How to Evolve Database and API Schemas in Production Without Downtime

The change that looks trivial in review and fails at 2am

A column gets renamed. A field gets added to an event. A response payload drops an attribute nobody appeared to be using. In the pull request it is a green diff, a few lines, approved in minutes. The migration runs clean in staging. Then it ships to production, and within the hour a service that had nothing to do with the change starts throwing errors and paging the on-call engineer.

When the team digs in, the migration itself turns out to be fine. The problem is timing. The schema changed while two versions of the application were still running against the same database, and only one of those versions knew about the new shape of the data. That is the pattern behind a large share of the production incidents we get called into after the fact. The code was correct. The data was correct. The assumption that only one version of the contract was live at any given moment was wrong.

Schema changes are among the highest-risk changes an operations team makes, and they rarely get treated that way. We have spent 25 years running these migrations across banking, telecom, and critical national infrastructure environments, and the discipline that keeps them safe is the same whether the store is Oracle, PostgreSQL, a Kafka topic, or a public REST API. This article lays out that discipline: why more than one schema is always in play, which changes break consumers and which do not, the expand-and-contract pattern that makes risky changes routine, and where all of this crosses from an engineering concern into a security and compliance one.

More than one schema version is always live at once

The mental model that causes outages is the one where a schema change is a single atomic event: the old shape exists, you run the migration, the new shape exists. Real systems never behave that way. At the moment a change lands, several versions of the contract are reading and writing the same data.

During a rolling deployment, the old application version and the new one run side by side for minutes or hours while instances are replaced one at a time. Both talk to the same database. If the new version has already dropped a column the old version still selects, the old instances fail every time they touch that row. That is the deployment-window case, and it is the easiest one to spot.

The harder cases sit outside the deployment window entirely. Rows written years ago are read by code that replaced the code that wrote them. Messages sitting in a queue were serialized by a producer that has since been rewritten, and the consumer that eventually picks them up expects a newer shape. Mobile app builds from eighteen months ago are still installed on real devices and still calling the API with the field names they were compiled against. A partner's integration was written once, in 2022, and no one on their side has looked at it since. Every one of these is a case where data written under one schema version is read under another, and the reader has to cope.

Once you accept that the system holds several concurrent versions of the contract, the design question stops being "how do I change the schema" and becomes "how do I change it so every version currently in play keeps working." That reframing is the whole game.

Backward and forward compatibility, in plain terms

Two properties decide whether a schema change is safe, and they are worth stating without jargon because teams routinely conflate them.

A change is backward compatible when new code can read data that was written by old code. You add a field, and the new consumer treats its absence in old records as an acceptable default. Nothing that already exists in the store becomes unreadable.

A change is forward compatible when old code can read data written by new code. The old consumer encounters a field it has never heard of and ignores it instead of crashing. This is the property people forget, and it is exactly the property the deployment-window incident violates.

Systems that survive constant change tend to have both. New readers tolerate old data, old readers tolerate new data, and that mutual tolerance is what lets you deploy producers and consumers independently instead of in a tightly choreographed lockstep that has to be right the first time. The engineering term for the receiving side of this is a tolerant reader: parse what you need, ignore what you do not recognize, and never assume the payload contains exactly the fields you expect and no others.

Which changes break consumers and which do not

Not every schema change carries the same risk. The distinction that matters is whether the change is additive or destructive, and a handful of qualifiers decide the edge cases. Additive changes tend to be safe because existing readers can ignore what they do not use. Destructive and mutating changes tend to break something, because a reader somewhere still depends on the thing you removed or altered.

ChangeDefault riskWhat makes it safe

Add a new nullable column or optional field

Safe

Old readers ignore it; new readers treat absence as null or a default.

Add a column with a NOT NULL constraint and no default

Breaking

Give it a default, or backfill existing rows before enforcing the constraint.

Rename a column or field

Breaking

Add the new name, write to both, migrate readers, then retire the old name.

Drop a column or field

Breaking

Confirm no live consumer reads it, deprecate it, then remove it in a later release.

Change a data type (for example, integer to string)

Breaking

Add a new field of the new type, dual-write, migrate, then remove the old one.

Widen a type (for example, 32-bit to 64-bit integer)

Usually safe

Verify every consumer and serialization format actually accepts the wider range.

Make an optional field required

Breaking

Old producers may omit it; keep it optional or enforce only after all producers send it.

Add or tighten a uniqueness or check constraint

Breaking

Validate existing data first; the constraint fails the instant one legacy row violates it.

The pattern in that table is consistent. Every genuinely safe change is one where the reader is free to ignore new information. Every breaking change is one where you have taken something away or redefined it under a reader that still expects the old meaning. The way to make a breaking change safe is to turn it into a sequence of additive steps, which is what expand and contract does.

Expand and contract: making risky changes routine

Expand and contract, sometimes called the parallel-change pattern, splits a breaking change into stages where the old and new schema coexist until it is safe to remove the old one. Take the common case of renaming a column from user_name to full_name in a table that a dozen services read.

  1. Expand. Add the new full_name column alongside the existing user_name. The schema now carries both. Nothing reads the new column yet, so nothing has changed for any consumer.
  2. Dual-write. Update the application so every write populates both columns. New rows are correct under both names. The old column stays authoritative for reads for now.
  3. Backfill. Run a batched migration that copies user_name into full_name for existing rows. Batch it, throttle it, and run it off-peak so you do not lock a hot table or saturate replication.
  4. Migrate reads. Move consumers over to read full_name, one service at a time, verifying each in production before moving to the next. This is where independent deployability earns its keep.
  5. Stop writing the old column. Once every reader is on the new column and you are confident there is no rollback need, drop the write to user_name.
  6. Contract. After a deprecation window long enough to cover queued messages, cached data, and stragglers, remove user_name from the schema.

Every step in that sequence is individually reversible and individually safe. At no point are two versions of the code disagreeing about what a column means. The cost is that a change which looked like one line in review is now six deployments spread over days or weeks. That cost is the point. You are trading a short, invisible window of high risk for a long, boring sequence of low-risk steps, and boring is what you want in production. The same shape applies to APIs and event streams: add the new field, populate it, move readers, then retire the old field on a published timeline.

Schema registries and enforced contracts

On event-driven systems, the contract lives in the messages, and the safest way to keep producers and consumers honest is to make the contract explicit and machine-checked. A schema registry does this. Producers register the schema for a topic, the registry enforces a compatibility mode on every new version, and an incompatible change is rejected at publish time rather than discovered when a consumer falls over.

Confluent Schema Registry with Avro, Protobuf, or JSON Schema is the common implementation in the Kafka world. Compatibility modes such as BACKWARD, FORWARD, and FULL let you declare which direction of compatibility the registry must guarantee, and the registry refuses to register a schema that would break it. For relational databases, versioned migration tools such as Flyway and Liquibase give you the same auditability: every change is a numbered, reviewed, version-controlled script rather than an ad-hoc statement someone typed into a production console. For online changes to large MySQL tables, tools like gh-ost and pt-online-schema-change apply the change without the long exclusive locks a naive ALTER would take. For HTTP APIs, an OpenAPI specification under version control, checked in CI, plays the registry role, catching a breaking change in the pipeline instead of in a partner's integration.

The common thread is that the contract stops being tribal knowledge and becomes an artifact the system can check on your behalf. That is worth a great deal when the person making the change has never met the team whose consumer will break.

The same problem wears different clothes across databases, APIs, and event streams

The underlying issue is identical everywhere, but the surface changes how you handle it. In a relational database, multiple application versions share one authoritative store, so the risk concentrates in the deployment window and in long-lived rows; expand-and-contract plus online migration tooling is the answer. In an API, you do not control the clients, so you cannot force an upgrade; you version the interface, keep old versions running, and publish deprecation timelines that give integrators time to move. In an event stream, messages are immutable once written and may be replayed months later, so forward compatibility and a registry matter more than anywhere else, because a consumer will eventually read a message serialized by a producer that no longer exists.

Teams that treat these as three unrelated problems end up with three inconsistent approaches and gaps between them. Treating them as one problem with three surfaces is what lets a platform team set a single policy and apply it everywhere.

Where schema changes become a security and compliance concern

It is tempting to file all of this under engineering hygiene, but in a regulated environment it is squarely a security and compliance matter, and auditors treat it that way. The reason is the CIA triad. A botched schema change is a direct hit to availability when unrelated services fall over, and a hit to integrity when a backfill runs wrong or a dual-write path silently diverges and the two copies of the data stop agreeing. In sectors where we work, an integrity failure in customer or transaction data is not an inconvenience; it is a reportable event.

This is why change management is a named control in every framework a serious IT function answers to. ISO/IEC 27001 requires managed change to information-processing facilities, covering documentation, testing, approval, and the ability to reverse a change that goes wrong. The PCI-DSS change-management requirements expect schema and code changes to systems in scope to be reviewed, tested in a separate environment, and backed by a documented rollback plan before they touch production. The NIST Cybersecurity Framework and CIS Controls both put configuration and change control among their core practices. A schema migration executed straight against production, with no review, no tested rollback, and no record of who approved it, fails those controls whether or not it happens to work that day.

The practices in this article map onto those controls almost one to one. Versioned migration scripts in Flyway or Liquibase give you the audit trail and the reviewed, repeatable change an assessor asks for. Expand-and-contract gives you the tested rollback path at every step. A schema registry enforcing a compatibility mode is a preventive control that stops a non-compliant change before it ships. Separation of duties between the engineer who writes the migration and the person who approves it is the same separation PCI-DSS expects around production changes. Done properly, safe schema evolution and audit-ready change management are the same work described in two vocabularies, and building the discipline once satisfies both the operations goal and the compliance obligation.

A checklist for changing a live schema

  1. Assume more than one version of every consumer is live, including old rows, queued messages, mobile builds, and partner integrations you do not control.
  2. Classify the change as additive or breaking before writing any code. If it removes or redefines anything, plan it as expand-and-contract.
  3. Make the schema change backward and forward compatible: new readers tolerate old data, old readers tolerate new data.
  4. Keep every migration as a numbered, reviewed, version-controlled script. No manual statements against production.
  5. Backfill in throttled batches, off-peak, with an eye on locks and replication lag.
  6. Migrate readers one service at a time and verify each in production before the next.
  7. Enforce contracts automatically where you can: a schema registry for events, an OpenAPI check in CI for APIs.
  8. Publish a deprecation timeline before removing anything, long enough to cover replayed messages and caches.
  9. Have a tested rollback for every stage, and record who reviewed and approved the change.
  10. Map the whole flow to your change-management control so the audit evidence is a by-product, not a scramble later.

How Aydahwa Enterprise Can Help

Most of the schema-change outages we are asked to investigate were preventable, and the fix is rarely a new tool. It is a change-management discipline that treats production data as the high-value, high-risk asset it is, applied consistently across databases, APIs, and event streams, and evidenced well enough to satisfy an auditor. That is the intersection of engineering and compliance where Aydahwa Enterprise works.

Our team brings 25-plus years across UNIX, Linux, cloud, and network architecture, together with ISO 27001, PCI-DSS, SOC 2, NIST CSF, and CIS-aligned practice and a Microsoft Cybersecurity Architect Expert background, into engagements in banking, telecom, and critical national infrastructure. We design change-control and DevSecOps pipelines that make risky migrations routine, review architectures before a cloud migration turns a schema change into an incident, and build the audit trail into the process rather than reconstructing it afterward.

If you are planning a migration, hardening your deployment process, or preparing for an assessment, start with our free cybersecurity self-assessment and the cybersecurity readiness checklist to find the gaps first. You can read more about how we work across cloud security and migration, cybersecurity, and managed IT support, or get in touch to talk through a specific change your team is nervous about shipping.

Share

Need expert guidance?

Our cybersecurity and IT consultants can help you implement the strategies discussed in this article.

Call UsWhatsAppBook