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.