Course contentsShow
← Node.js
Lesson 892 of 1,9808. Data Persistence and ORM IntegrationFree lesson

Database Refactoring Strategies

You will perform breaking changes to your schema while maintaining service compatibility.

In this lesson, you will learn how to evolve your database schema while keeping your Node.js services running without downtime.

The Challenge of Evolving Schemas

When working with production databases, you rarely have the luxury of "pausing" your application to restructure data. Database refactoring involves changing the shape of your data—such as splitting a large table into two or renaming a field—while your Node.js application continues to read from and write to it.

To achieve this, we use the Expand and Contract pattern (often called Parallel Change). Instead of running a destructive ALTER TABLE or a direct schema overwrite, we perform the migration in three distinct phases:

1. The Expand Phase

Add the new schema structure alongside the old one. If you are renaming a user’s name field to fullName, update your Node.js code to write to both fields simultaneously.

// Service logic during the transition
async function updateUser(id, data) {
  const payload = {
    name: data.fullName, // Legacy field
    fullName: data.fullName, // New field
    updatedAt: new Date()
  };
  await db.collection('users').updateOne({ _id: id }, { $set: payload });
}

2. The Migration Phase

Once your application is reliably writing to both fields, run a background script to backfill the fullName data for existing records that lack it. Because your application is already keeping new writes in sync, this background process only needs to handle the "legacy" subset of your data.

3. The Contract Phase

Once all records are updated and your services are successfully reading from the fullName field, you can safely remove the legacy name field from your database and your application code. This "contracting" of the schema is the final cleanup step.

By treating refactoring as a multi-step rollout rather than a single destructive event, you avoid the risks of service outages and data inconsistency. You effectively decouple the database structure from the application lifecycle, allowing you to iterate on your persistence layer with confidence.

Key Takeaway: Never perform a breaking schema change in a single step; use the Expand and Contract pattern to synchronize data across versions before removing legacy structures.