> ## Documentation Index
> Fetch the complete documentation index at: https://launchdarkly-preview.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Migration configuration

> This topic explains how to configure LaunchDarkly SDKs to manage migrations or modernizations.

> For the complete documentation index, see [llms.txt](/llms.txt). For the full documentation snapshot, see [llms-full.txt](/llms-full.txt).

## Overview

This topic explains how to configure LaunchDarkly SDKs to manage migrations or modernizations. You might use this feature if you are optimizing queries, upgrading to new tech stacks, migrating from one database to another, or other similar technology changes. This feature is available for server-side and edge SDKs only.

## Prerequisites

Before you configure your SDK to manage a migration, you must complete the following prerequisites:

* Create a migration feature flag. This is a temporary flag used to migrate data or systems while keeping your application available and disruption free. Migration flags break up the switch from an old to a new implementation into a series of recommended stages where movement from one stage to the next is done in incremental steps.
* Determine how many stages your migration will have. You can select from the following options as part of creating a migration feature flag:
  * Two stages: For migrations where you cannot run the new system and old system at the same time
  * Four stages: For migrations that can run both the new and old systems at the same time
  * Six stages: For migrations where you need to migrate `READS` and `WRITES` separatelyTo learn more, read [Migration flags](/home/flags/migration).

## Migration configuration options

There are two categories of migration options that you can configure for each LaunchDarkly SDK:

* Options for reading and writing data: You can define how to read from and write to both the old system and the new system. You can also define a method to check whether the two reads are a match, and whether the migration should execute serially or concurrently. To learn how these options apply to each migration stage, read [Use SDKs to manage a migration](/sdk/features/migrations#use-sdks-to-manage-a-migration).

* Options for tracking metrics: You can configure whether the SDK should track latency and errors, so that you can monitor the performance of your application during the migration. Details about each SDK's configuration are available in the SDK-specific sections below:

* [Server-side SDKs](#server-side-sdks)

* [Edge SDKs](#edge-sdks)

## Server-side SDKs

This feature is available in the following server-side SDKs:

* [.NET (server-side)](#net-server-side)
* [Go](#go)
* [Java](#java)
* [Node.js (server-side)](#nodejs-server-side)
* [PHP](#php)
* [Python](#python)
* [Ruby](#ruby)
* [Rust](#rust)

### .NET (server-side)

<Accordion title="Expand .NET (server-side) code sample">
  To define how to read from the old and new systems, define the `Read` function. You can also define how to check whether the two reads are a match, and whether the reads should take place concurrently or serially.

  To define how to write to the old and new systems, define the `Write` function.

  To configure the metrics for your migration, set the `TrackLatency` and `TrackErrors` options.

  Here's how:

  <CodeGroup>
    ```csharp title=".NET SDK v8.0 (C#)" theme={null}

          // define how to compare the two read values
          bool Checker(string a, string b) => a.Equals(b);

          var migration = new MigrationBuilder<string, string, string, string>(_client)
              .Read(
                  (payload) => MigrationMethod.Success("read old"),
                  (payload) => MigrationMethod.Success("read new"),
                  Checker)
              .Write(
                  (payload) => MigrationMethod.Success("write old"),
                  (payload) => MigrationMethod.Success("write new"))
              .ReadExecution(MigrationExecution.Parallel()) // or MigrationExecution.Serial(MigrationSerialOrder.Fixed)
              .TrackErrors(true)    // true by default
              .TrackLatency(true)   // true by default
              .Build();

    ```
  </CodeGroup>

  To learn more, read [`MigrationBuilder`](https://launchdarkly.github.io/dotnet-core/pkgs/sdk/server/api/LaunchDarkly.Sdk.Server.Migrations.MigrationBuilder-4.html).
</Accordion>

### Go

<Accordion title="Expand Go code sample">
  You can use the `Migration` configuration to define the options for your migration.

  To define how to read from the old and new systems, define the `Read` function. You can also define how to check whether the two reads are a match, and whether the reads should take place concurrently or serially.

  To define how to write to the old and new systems, define the `Write` function.

  To configure the metrics for your migration, set the `TrackLatency` and `TrackErrors` options.

  Here's how:

  <CodeGroup>
    ```go title="Go SDK v7.0" theme={null}

          client, _ := ld.MakeClient("YOUR_SDK_KEY", 5*time.Second)

          var comparison ld.MigrationComparisonFn
          comparison = func(interface{}, interface{}) bool {
          	// compare the two read values
          	return true
          }

          migrator, _ := ld.Migration(client).
          	Read(
          		func(interface{}) (interface{}, error) {
          			return "old read", nil
          		},
          		func(interface{}) (interface{}, error) {
          			return "new read", nil
          		},
          		&comparison,
          	).
          	ReadExecutionOrder(ldmigration.Random).
          	Write(
          		func(interface{}) (interface{}, error) {
          			return "old write result", nil
          		},
          		func(interface{}) (interface{}, error) {
          			return "new write result", nil
          		},
          	).
          	TrackLatency(true).
          	TrackErrors(true).
          	Build()
    ```
  </CodeGroup>

  To learn more, read [`Migration`](https://pkg.go.dev/github.com/launchdarkly/go-server-sdk/v7#Migration).
</Accordion>

### Java

<Accordion title="Expand Java code sample">
  You can use the `MigrationBuilder` to define the options for your migration.

  To define how to read from the old and new systems, define the `read` function, including how to check whether the two reads are a match. You can also define whether the reads should take place concurrently or serially.

  To define how to write to the old and new systems, define the `write` function.

  To configure the metrics for your migration, set the `trackLatency` and `trackErrors` options.

  Here's how:

  <CodeGroup>
    ```java title="Java SDK v7.0" theme={null}

          LDClient client = new LDClient("YOUR_SDK_KEY");

          MigrationBuilder<String, String, String, String> migrationBuilder = new MigrationBuilder<>(client)
            .read(
              (payload) -> MigrationMethodResult.Success("read old"),
              (payload) -> MigrationMethodResult.Success("read new"),
              (a, b) -> a.equals(b)
            )
            .readExecution(MigrationExecution.Serial(MigrationSerialOrder.RANDOM)) // default is .Parallel
            .write(
              (payload) -> MigrationMethodResult.Success("write old"),
              (payload) -> MigrationMethodResult.Success("write new")
            )
            .trackLatency(true)
            .trackErrors(true);

          Migration<String, String, String, String> migration = migrationBuilder.build();

    ```
  </CodeGroup>

  To learn more, read [`MigrationBuilder`](https://launchdarkly.github.io/java-core/lib/sdk/server/com/launchdarkly/sdk/server/migrations/MigrationBuilder.html).
</Accordion>

### Node.js (server-side)

<Accordion title="Expand Node.js (server-side) code sample">
  To define how to read from the old and new systems, define the `readOld` and `readNew` functions. You can also define how to check whether the two reads are a match, and whether the reads should take place concurrently or serially.

  To define how to write to the old and new systems, define the `writeOld` and `writeNew` functions.

  Each of the `readOld`, `readNew`, `writeOld`, and `writeNew` functions accept an optional argument, which is typically used to define what to read or write. They should return `LDMigrationSuccess` or `LDMigrationError`, or can throw an exception. The code sample below uses a mix to illustrate these possibilities.

  To configure the metrics for your migration, set the `latencyTracking` and `errorTracking` options.

  Here's how to configure each of these options:

  <CodeGroup>
    ```ts title="Node.js SDK v9.0" theme={null}
          import { LDMigrationOptions, init, createMigration } from '@launchdarkly/node-server-sdk';
          const options: LDMigrationOptions = {
            readNew: async(key?: string) => {
              console.log("Reading from new: ", key);
              return LDMigrationSuccess(true);
            },
            readOld: async(key?: string) => {
              console.log("Reading from old: ", key);
              return LDMigrationSuccess(true);
            },
            writeNew: async(params?: {key: string, value: string}) => {
              console.log("Writing to new: ", params);
              // if failure
              return LDMigrationError(new Error('example error'));
            },
            writeOld: async(params?: {key: string, value: string}) => {
              console.log("Writing to old: ", params);
              // if failure
              return LDMigrationError(new Error('example error'));
            },

            check: (old, new) => {
              // Define your consistency check for read operations
              // and return a boolean. Depending on your migration,
              // this may be as simple as 'return a === b;'
            },

            execution: new LDConcurrentExecution(),
              // or new LDSerialExecution(LDExecutionOrdering.Random),
              // or new LDSerialExecution(LDExecutionOrdering.Fixed),

            latencyTracking: true, // defaults to true
            errorTracking: true, // defaults to true
          }

          const client = init('YOUR_SDK_KEY');
          const migration = createMigration(client, options);

    ```
  </CodeGroup>

  To learn more, read [`LDMigrationOptions`](https://launchdarkly.github.io/js-core/packages/sdk/server-node/docs/interfaces/LDMigrationOptions.html).
</Accordion>

### PHP

<Accordion title="Expand PHP code sample">
  To define how to read from the old and new systems, call the `read` method. You can also define how to check whether the two reads are a match, and whether the reads should take place serially or in a randomized execution order.

  To define how to write to the old and new systems, call the `write` method.

  Each of the `read` and `write` method accept `new` and `old` methods for how to read from or write to both the new and old systems. These `new` and `old` methods accept an optional payload parameter, which is typically used to define what to read or write. They should return an `LaunchDarkly\Types\Result` instance.

  To configure the metrics for your migration, set the `trackLatency` and `trackErrors` options.

  Here's how to configure each of these options:

  <CodeGroup>
    ```php title="PHP SDK v6" theme={null}

          use LaunchDarkly\Migrations;
          use LaunchDarkly\Types;

          $builder = new Migrations\MigratorBuilder($client);

          $builder->read(
              fn (?string $payload) => Types\Result::success("old read"),
              fn (?string $payload) => Types\Result::success("new read"),
              fn(string $old, string $new) => $old == $new,
          );

          $builder->write(
              fn (?string $payload) => Types\Result::success("old write"),
              fn (?string $payload) => Types\Result::success("new write")
          );
          $builder->readExecutionOrder(Migrations\ExecutionOrder::SERIAL);
          // could also use ExecutionOrder::RANDOM

          $builder->trackLatency(true); // defaults to true
          $builder->trackErrors(true);  // defaults to true

          $result = $builder->build();

    ```
  </CodeGroup>

  To learn more, read [`MigratorBuilder`](https://launchdarkly.github.io/php-server-sdk/classes/LaunchDarkly-Migrations-MigratorBuilder.html).
</Accordion>

### Python

<Accordion title="Expand Python code sample">
  To define how to read from the old and new systems, call the `read` method. You can also define how to check whether the two reads are a match, and whether the reads should take place concurrently or serially.

  To define how to write to the old and new systems, call the `write` method.

  Each of the `read` and `write` method accept `new` and `old` methods for how to read from or write to both the new and old systems. These `new` and `old` methods accept an optional payload parameter, which is typically used to define what to read or write. They should return an `ldclient.Result` instance.

  To configure the metrics for your migration, set the `track_latency` and `track_errors` options.

  Here's how to configure each of these options:

  <CodeGroup>
    ```python title="Python SDK v9" theme={null}
          from ldclient import Result, MigratorBuilder, ExecutionOrder

          builder = MigratorBuilder(ldclient.get())

          builder.read(lambda _: Result.success("read old"), lambda _: Result.success("read new"), lambda lhs, rhs: lhs == rhs)
          builder.write(lambda _: Result.success("write old"), lambda _: Result.success("write new"))

          builder.read_execution_order(ExecutionOrder.PARALLEL)
          # could also use ExecutionOrder.SERIAL, ExecutionOrder.RANDOM

          builder.track_latency(True) # defaults to True
          builder.track_errors(True)  # defaults to True

          result = builder.build()

    ```
  </CodeGroup>

  To learn more, read [`ldclient.migrations`](https://launchdarkly-python-sdk.readthedocs.io/en/latest/api-extending.html#module-ldclient.migrations).
</Accordion>

### Ruby

<Accordion title="Expand Ruby code sample">
  To define how to read from the old and new systems, define the `read` function. You can also define how to check whether the two reads are a match, and whether the reads should take place in serial or in parallel.

  To define how to write to the old and new systems, define the `write` function.

  To configure the metrics for your migration, set the `track_latency` and `track_errors` options.

  Here's how:

  <CodeGroup>
    ```ruby title="Ruby SDK v8.0" theme={null}
          builder = LaunchDarkly::Migrations::MigratorBuilder.new(@client)
          builder.read(
            ->(_payload) { LaunchDarkly::Result.success('old value') },
            ->(_payload) { LaunchDarkly::Result.success('new value') },
            ->(lhs, rhs) { lhs == rhs }
          )

          builder.write(
            ->(_payload) { LaunchDarkly::Result.success('old value') },
            ->(_payload) { LaunchDarkly::Result.success('new value') }
          )
          builder.read_execution_order(builder.EXECUTION_PARALLEL) # or .EXECUTION_SERIAL, or .EXECUTION_RANDOM
          builder.track_latency(true) # defaults to true
          builder.track_errors(true)  # defaults to true
          migrator = builder.build
    ```
  </CodeGroup>

  To learn more, read [`MigratorBuilder`](https://launchdarkly.github.io/ruby-server-sdk/LaunchDarkly/Migrations/MigratorBuilder.html).
</Accordion>

### Rust

<Accordion title="Expand Rust code sample">
  To define how to read from the old and new systems, define the `read` function. You can also define how to check whether the two reads are a match, and whether the reads should take place in serial or concurrently.

  To define how to write to the old and new systems, define the `write` function.

  To configure the metrics for your migration, set the `track_latency` and `track_errors` options.

  Here's how:

  <CodeGroup>
    ```rust title="Rust SDK v2.2" theme={null}
          let client = Arc::new(client);
          let mut builder = MigratorBuilder::new(client.clone())
              .read(
                  |_payload: &String| async move { Ok(()) }.boxed(),
                  |_payload: &String| async move { Ok(()) }.boxed(),
                  Some(|lhs, rhs| lhs == rhs),
              )
              .write(
                  |_payload: &String| async move { Ok(()) }.boxed(),
                  |_payload: &String| async move { Ok(()) }.boxed(),
              );

          builder = builder
              .read_execution_order(ExecutionOrder::Concurrent) // Or ExecutionOrder::Serial or ExecutionOrder::Random
              .track_latency(true) // defaults to true
              .track_errors(true); // defaults to true

          let migrator = builder.build().expect("build migrator");
    ```
  </CodeGroup>

  To learn more, read [`MigratorBuilder`](https://docs.rs/launchdarkly-server-sdk/latest/launchdarkly_server_sdk/struct.MigratorBuilder.html).
</Accordion>

## Edge SDKs

This feature is available in the following edge SDKs:

* [Akamai](#akamai)
* [Cloudflare](#cloudflare)
* [Vercel](#vercel)

### Akamai

<Accordion title="Expand Akamai code sample">
  To define how to read from the old and new systems, define the `readOld` and `readNew` functions. You can also define how to check whether the two reads are a match, and whether the reads should take place concurrently or serially.

  To define how to write to the old and new systems, define the `writeOld` and `writeNew` functions.

  Each of the `readOld`, `readNew`, `writeOld`, and `writeNew` functions accept an optional argument, which is typically used to define what to read or write. They should return `LDMigrationSuccess` or `LDMigrationError`, or can throw an exception. The code sample below uses a mix to illustrate these possibilities.

  Although the migration options `latencyTracking` and `errorTracking` default to `true`, the Akamai SDK does not track metrics for migrations, because the Akamai SDK does not send events back to LaunchDarkly.

  Here's how to configure each of these options:

  <CodeGroup>
    ```ts title="Akamai SDK v1.0.9+" theme={null}
          import {
            createMigration,
            init,
            LDConcurrentExecution,
            LDMigrationError,
            LDMigrationOptions,
            LDMigrationSuccess,
          } from '@launchdarkly/akamai-server-edgekv-sdk';

          const options: LDMigrationOptions = {
            readNew: async(key?: string) => {
              console.log("Reading from new: ", key);
              return LDMigrationSuccess(true);
            },
            readOld: async(key?: string) => {
              console.log("Reading from new: ", key);
              return LDMigrationSuccess(true);
            },
            writeNew: async(params?: {key: string, value: string}) => {
              console.log("Writing to new: ", params);
              // if failure - can throw an exception
              throw new Error("example exception")
            },
            writeOld: async(params?: {key: string, value: string}) => {
              console.log("Writing to old: ", params);
              // if failure - can return the error
              return LDMigrationError(new Error('example error'));
            },

            check: (old, new) => {
              // Define your consistency check for read operations
              // and return a boolean. Depending on your migration,
              // this may be as simple as 'return a === b;'
            },

            execution: new LDConcurrentExecution(),
              // or new LDSerialExecution(LDExecutionOrdering.Random),
              // or new LDSerialExecution(LDExecutionOrdering.Fixed),

          }

          const client = init({
            sdkKey: 'example-client-side-id',
            namespace: 'your-edgekv-namespace',
            group: 'your-edgekv-group-id'
          });
          const migration = createMigration(client, options);

    ```
  </CodeGroup>

  To learn more, read [`LDMigrationOptions`](https://launchdarkly.github.io/js-core/packages/sdk/akamai-edgekv/docs/interfaces/LDMigrationOptions.html).
</Accordion>

### Cloudflare

<Accordion title="Expand Cloudflare code sample">
  To define how to read from the old and new systems, define the `readOld` and `readNew` functions. You can also define how to check whether the two reads are a match, and whether the reads should take place concurrently or serially.

  To define how to write to the old and new systems, define the `writeOld` and `writeNew` functions.

  Each of the `readOld`, `readNew`, `writeOld`, and `writeNew` functions accept an optional argument, which is typically used to define what to read or write. They should return `LDMigrationSuccess` or `LDMigrationError`, or can throw an exception. The code sample below uses a mix to illustrate these possibilities.

  To configure the metrics for your migration, set the `latencyTracking` and `errorTracking` options.

  Here's how to configure each of these options:

  <CodeGroup>
    ```ts title="Cloudflare SDK v2.2.2+" theme={null}
          import {
            createMigration,
            init,
            LDConcurrentExecution,
            LDMigrationError,
            LDMigrationOptions,
            LDMigrationSuccess,
          } from '@launchdarkly/cloudflare-server-sdk';

          const options: LDMigrationOptions = {
            readNew: async(key?: string) => {
              console.log("Reading from new: ", key);
              return LDMigrationSuccess(true);
            },
            readOld: async(key?: string) => {
              console.log("Reading from new: ", key);
              return LDMigrationSuccess(true);
            },
            writeNew: async(params?: {key: string, value: string}) => {
              console.log("Writing to new: ", params);
              // if failure - can throw an exception
              throw new Error('example exception')
            },
            writeOld: async(params?: {key: string, value: string}) => {
              console.log("Writing to old: ", params);
              // if failure - can return the failure
              return LDMigrationError(new Error('example error'));
            },

            check: (old, new) => {
              // Define your consistency check for read operations
              // and return a boolean. Depending on your migration,
              // this may be as simple as 'return a === b;'
            },

            execution: new LDConcurrentExecution(),
              // or new LDSerialExecution(LDExecutionOrdering.Random),
              // or new LDSerialExecution(LDExecutionOrdering.Fixed),

            latencyTracking: true, // defaults to true
            errorTracking: true, // defaults to true
          }

          const client = init('YOUR_SDK_KEY', env.LD_KV, { sendEvents: true });
          const migration = createMigration(client, options);

    ```
  </CodeGroup>

  To learn more, read [`LDMigrationOptions`](https://launchdarkly.github.io/js-core/packages/sdk/cloudflare/docs/interfaces/LDMigrationOptions.html).
</Accordion>

### Vercel

<Accordion title="Expand Vercel code sample">
  To define how to read from the old and new systems, define the `readOld` and `readNew` functions. You can also define how to check whether the two reads are a match, and whether the reads should take place concurrently or serially.

  To define how to write to the old and new systems, define the `writeOld` and `writeNew` functions.

  Each of the `readOld`, `readNew`, `writeOld`, and `writeNew` functions accept an optional argument, which is typically used to define what to read or write. They should return `LDMigrationSuccess` or `LDMigrationError`, or can throw an exception. The code sample below uses a mix to illustrate these possibilities.

  To configure the metrics for your migration, set the `latencyTracking` and `errorTracking` options.

  Here's how to configure each of these options:

  <CodeGroup>
    ```ts title="Vercel SDK v1.1.6+" theme={null}
          import {
            createMigration,
            init,
            LDConcurrentExecution,
            LDMigrationError,
            LDMigrationOptions,
            LDMigrationSuccess,
          } from '@launchdarkly/vercel-server-sdk';

          const options: LDMigrationOptions = {
            readNew: async(key?: string) => {
              console.log("Reading from new: ", key);
              return LDMigrationSuccess(true);
            },
            readOld: async(key?: string) => {
              console.log("Reading from new: ", key);
              return LDMigrationSuccess(true);
            },
            writeNew: async(params?: {key: string, value: string}) => {
              console.log("Writing to new: ", params);
              // if failure - can throw an exception
              throw new Error("example exception")
            },
            writeOld: async(params?: {key: string, value: string}) => {
              console.log("Writing to old: ", params);
              // if failure - can return the failure
              return LDMigrationError(new Error('example error'));
            },

            check: (old, new) => {
              // Define your consistency check for read operations
              // and return a boolean. Depending on your migration,
              // this may be as simple as 'return a === b;'
            },

            execution: new LDConcurrentExecution(),
              // or new LDSerialExecution(LDExecutionOrdering.Random),
              // or new LDSerialExecution(LDExecutionOrdering.Fixed),

            latencyTracking: true, // defaults to true
            errorTracking: true, // defaults to true
          }

          const client = init('example-client-side-id', edgeConfigClient, { sendEvents: true });
          const migration = createMigration(client, options);

    ```
  </CodeGroup>

  To learn more, read [`LDMigrationOptions`](https://launchdarkly.github.io/js-core/packages/sdk/vercel/docs/interfaces/LDMigrationOptions.html).
</Accordion>
