# Bean & Bark: change data without losing it

Chapters one and two, you *read*. This one you *write* — and you write against **production**, where a wrong `WHERE` clause doesn't cost you a few RCU, it costs you data. Two things stand between you and a very bad afternoon: your prod stage is **read-only until you say otherwise**, and every bulk write **shows you what it would do before it does it**.

**You'll learn**

- Understand why a prod stage is **read-only by default**, and how you consciously unlock a write
- Use a **preview-first** bulk write to see every row that would change — before a single one does
- Take a **backup** as a one-line seatbelt before any risky change

> **10:40am — the cleanup:** **Nadia:** *"can you retire the dormant subscriptions before the board sees the churn table? there's a couple hundred of them and they're making the numbers messy."*

## In production, look before you leap

The obvious write is one line, and it reads as perfectly safe. But you're pointed at prod, and `Status = 'dormant'` may match more than the subscriptions you have in mind. DynoStudio assumes you might be wrong: the prod stage refuses writes until you deliberately unlock it, and a bulk write is **preview-first** — it runs the match, tells you exactly how many rows it *would* touch, and writes nothing until you look and confirm.

```sql
-- looks harmless. it is pointed at PRODUCTION.
UPDATE "BeanAndBark"
SET Status = 'retired'
WHERE Status = 'dormant'
```

**Try it · preview the cleanup**

_As written — too broad_

```sql
UPDATE "BeanAndBark"
SET Status = 'retired'
WHERE Status = 'dormant'
```

_Executes as:_ Bulk UPDATE · preview · BeanAndBark — "dormant" isn't only a subscription state — inactive customer accounts carry it too. The preview is the whole point: 4,012 is not the ~200 you meant, so you stop here — before any write.

_Result:_ Preview · 4,012 rows would change — 214 subscriptions + 3,798 customer accounts. Retiring accounts would lock real customers out. ⚠ Nothing has been written.

_Scoped to subscriptions_

```sql
UPDATE "BeanAndBark"
SET Status = 'retired'
WHERE begins_with(sk, 'SUB#') AND Status = 'dormant'
```

_Executes as:_ Bulk UPDATE · preview · BeanAndBark — The sk prefix pins the write to subscriptions — the set you actually meant. 214 rows, all subscriptions. Now the preview matches your intent; run it again to apply.

_Result:_ Preview · 214 rows would change — dormant subscriptions only. ✓ Review the set, then run again to apply.

_Run it live in DynoStudio:_ https://www.kanject.com/dynostudio/

That first preview is this whole chapter. `Status = 'dormant'` matched **4,012** rows — and 3,798 of them were customer accounts you'd have quietly retired, locking real people out. You wrote nothing; the preview caught it. Pin the write to subscriptions with `begins_with(sk, 'SUB#')` and the number drops to **214** — the set you actually meant. A **Scan** found them either way; the difference is that DynoStudio made you look at the blast radius first.

> **The seatbelt you clip before, not after:** Even with the correct, scoped write, you snapshot first: `BACKUP TABLE "BeanAndBark"`. It's one statement, it's cheap, and it's the whole difference between a mistake and a disaster. *Then* you apply the scoped update — 214 subscriptions retired, zero accounts touched, and a restore point if anyone ever asks. That is what "operating in production" means: the tool makes you look, and you always keep a way back.

**Recap**

- A **prod stage is read-only by default** — writes are refused until you consciously unlock it, so nothing changes production by accident.
- A **bulk write is preview-first**: it shows every row it *would* change and writes nothing until you confirm — a too-broad `WHERE` is a number on screen, not lost data.
- Take a **`BACKUP`** before a risky change. One line, cheap, and it turns any mistake into an undo.

**Before you touch prod — check yourself**

**1. You run a bulk `UPDATE`. Before anything in the table changes, what does DynoStudio show you?**

- A preview of every row that would change — and it writes nothing yet ✓ — a bulk write is preview-first: you see the full matched set (the blast radius) and confirm before a single row is written.
- Nothing — it applies immediately, that's what UPDATE means — not for a set-based write in the studio. It matches first and shows you the count, precisely so an over-broad `WHERE` can't surprise you.
- Only the rows you named in the WHERE, already updated — nothing is updated at preview time. The point is to *see* what would change while you can still stop.

**2. The preview shows **4,012** rows when you expected ~200. What's the safe move?**

- Stop, narrow the WHERE, and re-preview until the count matches your intent ✓ — the preview is a warning you can act on. A number far from what you expected means the predicate is wrong — fix it before writing.
- Apply it — it previewed, so it's been approved — a preview is a *disclosure*, not an approval. 4,012 ≠ 200 means you'd change the wrong rows; applying it is the mistake it's trying to prevent.
- Delete the extra rows afterwards — you'd be cleaning up a self-inflicted mess — and the original values are already gone unless you took a backup. Catch it in the preview instead.

**Try it yourself**

**1. Scope the cleanup**

Rewrite `UPDATE "BeanAndBark" SET Status = 'retired' WHERE Status = 'dormant'` so it can only ever touch subscriptions.

_Hint:_ In a single-table design, the `sk` prefix is the entity discriminator. Subscriptions start with `SUB#`.

_Solution:_ Add `begins_with(sk, 'SUB#')` to the `WHERE` — now the match is pinned to subscription items, and customer accounts with the same `Status` are excluded.

```sql
UPDATE "BeanAndBark"
SET Status = 'retired'
WHERE begins_with(sk, 'SUB#') AND Status = 'dormant'
```

**2. Take a restore point**

Before applying any of this to production, write the statement that gives you a way back.

_Hint:_ One statement snapshots the whole table on demand — no PITR setup required.

_Solution:_ A `BACKUP TABLE` snapshot is your undo. Run it, note the returned ARN, then apply the scoped write knowing you can restore.

```sql
BACKUP TABLE "BeanAndBark" AS 'beanandbark-pre-cleanup'
```

> **Where next?:** You changed production and kept everything you meant to keep. [Writing data](https://www.kanject.com/docs/dynostudio-partiql-writes/) is the full reference for `INSERT` / `UPDATE` / `DELETE`, conditional writes, and the preview-first set-write ritual. One Part 1 skill is left: when the base key can't answer a standing question, [add an index](https://www.kanject.com/docs/dynostudio-bean-bark-gsi/).

---
_Source: https://www.kanject.com/docs/dynostudio-bean-bark-safe-writes/ · Kanject Docs_
