Course contentsShow
← Databases & SQL
Lesson 1249 of 1,95311. NoSQL and Document ModelsFree lesson

Handling Schema Evolution

Use dynamic application code to handle multiple versions of documents simultaneously.

Handling Schema Evolution: Multi-Version Application Logic

In this lesson, you will learn how to maintain application uptime and data integrity by using dynamic code to handle multiple document versions simultaneously within your database.

The Challenge of "Schema-Less" Data

In document models, while the database doesn't force a strict table structure, your application code expects specific fields to exist. When business requirements change—such as renaming a user_name field to full_name—you cannot instantly update millions of existing documents. Performing a massive "all-at-once" migration often locks your database or causes downtime, which contradicts the goal of a flexible schema.

The Strategy: Defensive Reads

Instead of forcing a migration, we treat the database as a repository of historical schemas. We update the application code to be "version-aware," handling multiple document formats simultaneously. When the application fetches a document, it checks for the presence of the data it needs and applies transformations on the fly.

Consider this example using a simple conditional check:

function formatUser(doc) {
  // Handle older schema version
  if (doc.user_name && !doc.full_name) {
    return {
      id: doc.id,
      name: doc.user_name, // Map legacy field
      version: 1
    };
  }
  
  // Handle current schema version
  return {
    id: doc.id,
    name: doc.full_name,
    version: 2
  };
}

Why This Works

By moving the logic into the application layer, you avoid expensive write operations across the entire collection.

  1. Zero Downtime: You don’t need to stop the database to run migration scripts.
  2. Lazy Migration: You can choose to update documents to the new format only when they are accessed or updated (a "write-back" pattern), gradually normalizing the data over time without a global lock.
  3. Robustness: If a deployment fails, the application remains compatible with the data already stored in the database because it still recognizes the "old" structure.

This approach acknowledges that data is rarely uniform in a distributed, evolving system. Your code becomes the bridge between past and future storage formats.

Key Takeaway: Rather than forcing a global database migration, use dynamic application logic to transform legacy document structures into current requirements at the moment of read.