Skip to main content

Versioning - TypeScript SDK

Since Workflow Executions in Temporal can run for long periods — sometimes months or even years — it's common to need to make changes to a Workflow Definition, even while a particular Workflow Execution is in progress.

The Temporal Platform requires that Workflow code is deterministic. If you make a change to your Workflow code that would cause non-deterministic behavior on Replay, you'll need to use one of our Versioning methods to gracefully update your running Workflows. With Versioning, you can modify your Workflow Definition so that new executions use the updated code, while existing ones continue running the original version. There are two primary Versioning methods that you can use:

  • Versioning with Patching. This method works by adding branches to your code tied to specific revisions. It can be used to revise in-progress Workflows.
  • Worker Versioning. The Worker Versioning feature allows you to tag your Workers and programmatically roll them out in deployment versions, so that old Workers can run old code paths and new Workers can run new code paths. If you were using this method experimentally prior to summer 2025, refer to the Worker Versioning Legacy docs.

Versioning with Patching

To understand why Workflow Branching is useful, it's helpful to first demonstrate cutting over an entire Workflow.

Workflow cutovers

Since incompatible changes only affect open Workflow Executions of the same type, you can avoid determinism errors by creating a whole new Workflow when making changes. To do this, you can copy the Workflow Definition function, giving it a different name, and make sure that both names were registered with your Workers.

For example, you would duplicate PizzaWorkflow as PizzaWorkflowV2:

function pizzaWorkflow(order: PizzaOrder): Promise<OrderConfirmation> {
// this function contains the original code
}

function pizzaWorkflowV2(order: PizzaOrder): Promise<OrderConfirmation> {
// this function contains the updated code
}

You would then need to update the Worker configuration, and any other identifier strings, to register both Workflow Types:

const worker = await Worker.create({
workflowsPath: require.resolve('./workflows'),
// other configurations
});

The downside of this method is that it does not use any Temporal platform features. It requires you to duplicate code and to update any code and commands used to start the Workflow. This can become impractical over time, depending on how you are providing configuration strings to your deployment. This method also does not provide a way to introduce versioning to any still-running Workflows -- it is essentially just a cutover, unlike the Patching method.

Adding a patch

Patching essentially defines a logical branch for a specific change in the Workflow. If your Workflow is not pinned to a specific deployment or you need to fix a bug in a running workflow, you can patch it.

Suppose you have an initial Workflow that runs activityA:

// v1
export async function myWorkflow(): Promise<void> {
await activityA();
await sleep('1 days'); // arbitrary long sleep to simulate a long running workflow we need to patch
await activityThatMustRunAfterA();
}

Now, you want to update your code to run activityB instead. This represents your desired end state.

// vFinal
export async function myWorkflow(): Promise<void> {
await activityB();
await sleep('1 days');
}

The problem is that you cannot deploy this vFinal revision directly until you're certain there are no more running Workflows created using the v1 code, otherwise you are likely to cause a nondeterminism error. Instead, you'll need to use the patched function to check which version of the code should be executed.

Patching is a three-step process:

  1. Patch in any new, updated code using the patched() function. Run the new patched code alongside old code.
  2. Remove old code and use deprecatePatch() to mark a particular patch as deprecated.
  3. Once there are no longer any open Worklow Executions of the previous version of the code, remove deprecatePatch(). Let's walk through this process in sequence.

Patching in new code

Using patched inserts a marker into the Workflow History. During Replay, if a Worker encounters a history with that marker, it will fail the Workflow task when the Workflow code doesn't produce the same patch marker (in this case your-change-id). This ensures you can safely deploy code from v2 as a "feature flag" alongside the original version (v1).

// v2
import { patched } from '@temporalio/workflow';
export async function myWorkflow(): Promise<void> {
if (patched('my-change-id')) {
await activityB();
await sleep('1 days');
} else {
await activityA();
await sleep('1 days');
await activityThatMustRunAfterA();
}
}

Deprecating patches

After ensuring that all Workflows started with v1 code have finished, you can deprecate the patch.

Once you're confident that your Workflows are no longer running the pre-patch code paths, you can deploy your code with deprecatePatch(). These Workers will be running the most up-to-date version of the Workflow code, which no longer requires the patch. The deprecatePatch() function works similarly to the patched() function by recording a marker in the Workflow history. This marker does not fail replay when Workflow code does not emit it. Deprecated patches serve as a bridge between the pre-patch code paths and the post-patch code paths, and are useful for avoiding errors resulting from patched code paths in your Workflow history.

// v3
import { deprecatePatch } from '@temporalio/workflow';

export async function myWorkflow(): Promise<void> {
deprecatePatch('my-change-id');
await activityB();
await sleep('1 days');
}

Removing a patch

Once you're sure that you will no longer need to Query or Replay any of your pre-patch Workflows, you can then safely deploy Workers that no longer use either the patched() or deprecatePatch() calls:

Patching allows you to make changes to currently running Workflows. It is a powerful method for introducing compatible changes without introducing non-determinism errors.

Worker Versioning

Temporal's Worker Versioning feature allows you to tag your Workers and programmatically roll them out in deployment versions, so that old Workers can run old code paths and new Workers can run new code paths. This way, you can pin your deployments to specific revisions, often avoiding the need for patching.

Changing the order of any commands in your Workflow code that interact directly with the Temporal Service -- such as calling an Activity or creating a Sleep timer -- will cause a non-determinism error unless you've implemented a versioning solution. To test whether a new revision will require versioning, you should incorporate Replay Testing.