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

# Migration configuration

<View title="Developer" />

<View title="Federal docs">
  <Danger>
    **Migration flags are not available in federal environments**

    To learn more, read <a href="/docs/home/infrastructure/federal">LaunchDarkly in federal environments</a>.
  </Danger>
</View>

<View title="EU docs">
  <Danger>
    **Migration flags are not available in LaunchDarkly's European Union (EU) instance**

    To learn more, read <a href="/docs/home/infrastructure/eu">LaunchDarkly in the European Union (EU)</a>.
  </Danger>
</View>

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` separately

To learn more, read [Migration flags](/docs/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](/docs/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)](#node-js-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#)" expandable lines wrap 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" maxLines=0 expandable lines wrap 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" maxLines=0 expandable lines wrap theme={null}

    LDClient client = new LDClient("YOUR_SDK_KEY");

    MigrationBuilder<String, String, String, String> migrationBuilder = new MigrationBuilder<String, String, String, String>(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().orElseThrow();

    ```
  </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" maxLines=0 expandable lines wrap theme={null}
    import {
      LDMigrationOptions,
      init,
      createMigration,
      LDMigrationSuccess,
      LDMigrationError,
      LDConcurrentExecution,
      LDSerialExecution,
      LDExecutionOrdering,
    } 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, newVal) => {
        // 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" maxLines=0 expandable lines wrap 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" lines wrap 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" lines wrap 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" expandable lines wrap 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+" maxLines=0 expandable lines wrap 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 old: ", 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, newVal) => {
        // 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+" maxLines=0 expandable lines wrap 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 old: ", 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, newVal) => {
        // 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+" maxLines=0 expandable lines wrap 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 old: ", 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, newVal) => {
        // 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>
