diff --git a/.changeset/tough-wasps-notice.md b/.changeset/tough-wasps-notice.md new file mode 100644 index 000000000..a845151cc --- /dev/null +++ b/.changeset/tough-wasps-notice.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/docs/content/docs/foundations/serialization.mdx b/docs/content/docs/foundations/serialization.mdx index f9423edd6..6a7d6f56b 100644 --- a/docs/content/docs/foundations/serialization.mdx +++ b/docs/content/docs/foundations/serialization.mdx @@ -152,3 +152,45 @@ export async function handleWebhookWorkflow() { // … } ``` + +## Pass-by-Value Semantics + +**Parameters are passed by value, not by reference.** Steps receive deserialized copies of data. Mutations inside a step won't affect the original in the workflow. + +**Incorrect:** + +```typescript +export async function updateUserWorkflow(userId: string) { + "use workflow"; + + let user = { id: userId, name: "John", email: "john@example.com" }; + await updateUserStep(user); + + // user.email is still "john@example.com" + console.log(user.email); +} + +async function updateUserStep(user: { id: string; name: string; email: string }) { + "use step"; + user.email = "newemail@example.com"; // Changes are lost +} +``` + +**Correct - return the modified data:** + +```typescript +export async function updateUserWorkflow(userId: string) { + "use workflow"; + + let user = { id: userId, name: "John", email: "john@example.com" }; + user = await updateUserStep(user); // Reassign the return value + + console.log(user.email); // "newemail@example.com" +} + +async function updateUserStep(user: { id: string; name: string; email: string }) { + "use step"; + user.email = "newemail@example.com"; + return user; +} +``` diff --git a/docs/content/docs/foundations/workflows-and-steps.mdx b/docs/content/docs/foundations/workflows-and-steps.mdx index f1d1fd278..c0b3352e1 100644 --- a/docs/content/docs/foundations/workflows-and-steps.mdx +++ b/docs/content/docs/foundations/workflows-and-steps.mdx @@ -79,6 +79,10 @@ async function chargePayment(order: Order) { By default, steps have a maximum of 3 retry attempts before they fail and propagate the error over to the workflow. Learn more about errors and retrying in the [Errors & Retrying](/docs/foundations/errors-and-retries) page. + +**Important:** Due to serialization, parameters are passed by **value, not by reference**. If you pass an object or array to a step and mutate it, those changes will **not** be visible in the workflow context. Always return modified data from your step functions instead. See [Pass-by-Value Semantics](/docs/foundations/serialization#pass-by-value-semantics) for details and examples. + + Step functions are primarily meant to be used inside a workflow.