SST v4 for AWS
Overview
SST v4 (the "Ion" engine) is a Pulumi-backed IaC framework: you describe AWS resources in TypeScript and SST/Pulumi reconciles them into your account. It gives you high-level sst.aws.* components (Function, Bucket, Dynamo, Cron, Service, …) that expand into many underlying resources, plus an escape hatch to any raw Pulumi aws.* resource for the long tail. This skill encodes a production-proven way to author, link, test, deploy, and troubleshoot SST stacks on AWS — distilled from real multi-stack projects that have paid for each lesson with a prod incident.
SST and Pulumi are third-party — verify current syntax with Context7 (resolve-library-id → query-docs for sst or pulumi-aws) when you're unsure about a component's options. Verify AWS-side facts (service limits, model IDs, IAM action names, region availability) with the AWS docs MCP, never from memory. The patterns here are the how; the docs are the what.
When to Use
Use this skill when you need to:
- Write or edit
sst.config.ts — app name, home, providers/region, defaultTags, global $transform, run() import order.
- Build
infra/ modules using sst.aws.Function, sst.aws.Bucket, sst.aws.Dynamo, sst.aws.Cron, sst.aws.Service, sst.aws.Router, sst.Secret, sst.Linkable, or raw aws.* Pulumi resources.
- Wire resource links between modules (SST
link:, SSM Parameter Store, IAM scope).
- Write source-level Vitest tests for infra modules.
- Run a deploy, diagnose a deploy failure, or migrate a resource between Pulumi types.
- Troubleshoot SST stack issues on AWS.
Do not use this skill for SST v2/v3 ("SST Classic", CDK-based) projects. Those are a different framework — the patterns here do not apply. Run npx sst version to confirm you're on v4/Ion (look for the $config + .sst/platform/ signature).
Prerequisites
- Node.js matching the repo's
.nvmrc — check before editing.
- SST v4 (Ion) installed — verify with
npx sst version.
- AWS CLI configured with credentials for the target account.
- Package manager identified from
package.json (npm vs pnpm).
- Context7 MCP available for SST/Pulumi syntax verification.
- AWS docs MCP available for AWS-side fact verification.
- Windows host (PowerShell) is the primary environment. Adjust path separators and shell syntax accordingly.
Procedure
Step 1 — Orient: read the repo before you touch it
SST projects are conventional but not identical. Before editing, build a quick map so your change matches the house style instead of fighting it:
- Read
sst.config.ts — the app name, home, providers/region, defaultTags, any global $transform (Node runtime pin, bundle fixups), and the order in which run() imports infra/ modules. The import order is the dependency order; respect it.
- Read
infra/ — one file per domain (storage, functions, api, observability…). This is where resources are declared. Check for an infra/CLAUDE.md — these projects keep IaC-specific rules there, and it's the single most valuable file to read first.
- Read
infra/tests/ — source-level Vitest assertions that pin resource invariants. If they exist, your change must keep them green and probably needs a new assertion.
- Read
package.json / .nvmrc — package manager (npm vs pnpm), Node version, and the sst/pulumi versions actually installed.
npx sst version
Confirm you're on v4/Ion. If you see v2/v3 ("SST Classic", CDK-based), stop — these patterns don't apply.
Step 2 — Determine your mode
| Situation |
Follow |
| New project, or adding a resource/module to an existing SST app |
Authoring conventions in Step 4 |
| Wiring one module's output into another (links, SSM, IAM scope) |
Sharing notes in Step 4 (link: vs SSM) |
| Writing tests for infra so changes don't silently break |
Step 5 — Test |
| Running a deploy, or a deploy just failed |
Step 6 — Deploy/operate |
| Migrating a resource between Pulumi types, renaming a physical name |
Step 6 migration notes |
Read the matching procedure step before editing — each step carries the why behind the rule, which matters more than the rule itself.
Step 3 — Verify syntax
Verify current syntax with Context7 (resolve-library-id → query-docs for sst or pulumi-aws) when you're unsure about a component's options. Verify AWS-side facts (service limits, model IDs, IAM action names, region availability) with the AWS docs MCP, never from memory. Don't guess at a component's option name.
Step 4 — Author the resource/module
Follow the authoring conventions below. Match the surrounding file's commenting density and naming — these projects comment the why heavily, and a terse one-liner in a heavily-annotated file reads as a regression.
Universal conventions (apply everywhere):
- Control the Node runtime deliberately, in one place. Don't leave it to whatever the installed SST happens to default to. The idiom is a single global
$transform(sst.aws.Function, (args) => { args.runtime ??= "nodejs24.x" }) in run() — ??= is correct here (the transform runs before the component applies its own default, so it fills in only when the user didn't set one). Recent SST already defaults to a current Node runtime, so check the installed default first (Context7); the transform is then version-independence insurance so a future SST downgrade can't silently move your fleet.
- Never interpolate a Pulumi
Output<T> into a plain JS template literal. Use $interpolate (or pulumi.interpolate). A bare top-level `${bucket.arn}/*` stringifies the Output to a [Output<T>] placeholder and produces a broken ARN that only fails at deploy time (it type-checks and sst dev runs fine). The fix is $interpolate`${bucket.arn}/*`. This has caused prod deploy outages.
- Prefer typed
sst.aws.* / aws.* resources over the aws.cloudcontrol.Resource escape hatch. CloudControl outputs are stringly-typed and oneOf fields don't patch cleanly. Use it only when no typed resource exists yet, and migrate off it when one ships.
Project-specific defaults (adopt for consistency, but confirm per repo):
- Region
ap-northeast-1, home: "aws", and defaultTags carrying Project / Stage / ManagedBy: "sst".
- Stage-gated lifecycle:
removal: stage === "prod" ? "retain" : "remove" and protect: stage === "prod" so prod resources survive a stack tear-down and non-prod previews clean up.
- SSM Parameter Store as the out-of-graph contract under a
/{app}/{stage}/{domain}/... prefix — for consumers that aren't in the Pulumi graph (CI scripts, sibling apps, operators). For same-app Lambdas, prefer SST link: (it wires a real dependency edge and grants IAM); don't route same-app sharing through SSM.
- Lazy
await import("./infra/<module>") inside run() so sst dev hot-reload stays light. (For testing, a module export still runs its top-level new sst.aws.* unless it's wrapped in a factory function — see Step 5 for how to test infra.)
- Source-level Vitest tests on every infra module — a lightweight, house-style regression net asserting on the source text (resource names, index shapes, IAM scopes). It's a deliberate choice, not an SST limit: Pulumi does support runtime mocks (
@pulumi/pulumi/runtime) for behavioral graph tests when a module has real logic. Source assertions don't replace a preview-deploy + smoke test.
- An observability gate: every new Lambda/queue/schedule gets an alarm and structured logging before merge. Whether you enforce this depends on the project, but it's cheap insurance.
When you introduce a convention, say which bucket it's in ("this is universal" vs "matching this repo's house style") so the user can override the project-specific ones deliberately.
Step 5 — Test
Add or update source-level assertions as described in this step and run:
npx vitest
Or use the repo's test script:
npm test
Run npx sst diff and/or tsc --noEmit to catch type and plan errors before deploying:
npx sst diff
npx tsc --noEmit
Step 6 — Deploy/operate
Follow the deploy steps below. Confirm the target account before any deploy:
aws sts get-caller-identity
Then deploy:
npx sst deploy
For migrations (resource between Pulumi types, renaming a physical name), default to two sequential PRs — Pulumi creates-before-destroys, so for a uniqueness-constrained AWS name (bucket, IAM role, gateway) the old resource still owns it and the create fails with ConflictException. Two sequential deploys (teardown, then recreate) is the conservative default; aliases: / pulumi import / state surgery can bridge identity in some cases but only with a reviewed plan. See the migration notes in this step.
Step 7 — Clean up
Clean up any exported state files — they contain account IDs and ARNs and must not linger in /tmp or chat history.
Pitfalls
Output<T> in template literals causes silent prod outages. A bare `${bucket.arn}/*` stringifies the Output to [Output<T>] and produces a broken ARN. It type-checks and sst dev runs fine — it only fails at deploy time. Always use $interpolate`${bucket.arn}/*` or pulumi.interpolate. This has caused real prod deploy outages.
Resource-type migrations fail with ConflictException. Pulumi creates-before-destroys. For uniqueness-constrained AWS names (buckets, IAM roles, gateways), the old resource still owns the name when the new one tries to create. Default to two sequential deploys (teardown, then recreate). aliases: / pulumi import / state surgery can bridge identity but only with a reviewed plan.
aws.cloudcontrol.Resource is a trap door. Outputs are stringly-typed and oneOf fields don't patch cleanly. Use it only when no typed resource exists yet, and migrate off it when one ships.
Don't route same-app sharing through SSM. For same-app Lambdas, use SST link: — it wires a real dependency edge and grants IAM. SSM is for out-of-graph consumers (CI scripts, sibling apps, operators) only.
Don't hand-set runtime on individual functions. Use the global $transform with ??= so you fill in only when the user didn't set one. Hand-setting diverges from the fleet unless intentionally diverging (e.g., a Python function).
SST v2/v3 ("SST Classic") is a different framework. These patterns don't apply. Always verify with npx sst version first.
Import order in run() is dependency order. Respect it. Reordering imports can break resource references.
Source-level tests don't replace preview-deploy + smoke test. They assert on source text, not runtime behavior. For modules with real logic, consider Pulumi runtime mocks (@pulumi/pulumi/runtime).
State files contain account IDs and ARNs. They must not linger in /tmp or chat history. Clean them up after any export.
Don't guess at component option names. Verify with Context7 (resolve-library-id → query-docs for sst or pulumi-aws). Verify AWS-side facts with the AWS docs MCP, never from memory.
Verification
Confirm SST version (must be v4/Ion):
npx sst version
Expected: v4.x with $config + .sst/platform/ signature.
Confirm target AWS account before deploy:
aws sts get-caller-identity
Verify the account ID matches your expectation before proceeding.
Type-check the project:
npx tsc --noEmit
No errors expected.
Preview the deploy plan:
npx sst diff
Review the diff for unexpected creates/deletes/updates.
Run the test suite:
npx vitest
All source-level assertions must pass. Add new assertions for new resources.
Post-deploy smoke test: Verify the deployed resource is reachable and behaves as expected. Source-level tests don't replace this.
What good looks like
- The change is the smallest diff that satisfies the requirement, in the right
infra/ module, wired into run() in dependency order.
- Every Lambda gets the right runtime via the global transform (you didn't hand-set
runtime unless intentionally diverging — e.g. a Python function).
- Cross-resource references use
link: (in-graph) and/or $interpolate-scoped IAM; outputs other tools consume are published to SSM under the stage prefix.
- New infra has a matching source-level test, and the existing suite stays green.
- You confirmed AWS-side facts via the docs MCP and SST/Pulumi syntax via Context7 rather than relying on recall.
- Anything irreversible (deploy,
sst remove, a resource-type migration) was flagged to the user with the account it targets, and migrations were planned as two PRs, not one.
Related skills
- aws-cdk-development — for CDK-based AWS IaC projects (SST v2/v3 Classic).
- aws-lambda-development — for Lambda function authoring patterns.
- aws-iam-least-privilege — for IAM policy scoping within SST resources.
Limitations
- Use this skill only when the task clearly matches its upstream source and local project context.
- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.
1---2name: aws-sst-development3description: Authors SST v4 (Ion) AWS infra in TypeScript: sst.config.ts, infra/ modules (sst.aws.Function/Bucket/Dynamo/Cron/Service/Router, sst.Secret, sst.Linkable, raw aws.* Pulumi), resource links, sst deploy, and stack troubleshooting. Use when writing Ion stacks or diagnosing Pulumi-backed SST deploys. Not for SST Classic v2/v3 CDK projects; never interpolate a Pulumi Output into a bare JS template literal.4license: MIT5---6
7# SST v4 for AWS
8
9## Overview
10
11SST v4 (the "Ion" engine) is a Pulumi-backed IaC framework: you describe AWS resources in TypeScript and SST/Pulumi reconciles them into your account. It gives you high-level `sst.aws.*` components (Function, Bucket, Dynamo, Cron, Service, …) that expand into many underlying resources, plus an escape hatch to *any* raw Pulumi `aws.*` resource for the long tail. This skill encodes a production-proven way to author, link, test, deploy, and troubleshoot SST stacks on AWS — distilled from real multi-stack projects that have paid for each lesson with a prod incident.
12
13SST and Pulumi are third-party — verify current syntax with Context7 (`resolve-library-id` → `query-docs` for `sst` or `pulumi-aws`) when you're unsure about a component's options. Verify AWS-side facts (service limits, model IDs, IAM action names, region availability) with the AWS docs MCP, never from memory. The patterns here are the *how*; the docs are the *what*.
14
15## When to Use
16
17Use this skill when you need to:
18
19- Write or edit `sst.config.ts` — app name, `home`, providers/region, `defaultTags`, global `$transform`, `run()` import order.
20- Build `infra/` modules using `sst.aws.Function`, `sst.aws.Bucket`, `sst.aws.Dynamo`, `sst.aws.Cron`, `sst.aws.Service`, `sst.aws.Router`, `sst.Secret`, `sst.Linkable`, or raw `aws.*` Pulumi resources.
21- Wire resource links between modules (SST `link:`, SSM Parameter Store, IAM scope).
22- Write source-level Vitest tests for infra modules.
23- Run a deploy, diagnose a deploy failure, or migrate a resource between Pulumi types.
24- Troubleshoot SST stack issues on AWS.
25
26**Do not use this skill for SST v2/v3 ("SST Classic", CDK-based) projects.** Those are a different framework — the patterns here do not apply. Run `npx sst version` to confirm you're on v4/Ion (look for the `$config` + `.sst/platform/` signature).
27
28## Prerequisites
29
30- **Node.js** matching the repo's `.nvmrc` — check before editing.
31- **SST v4 (Ion)** installed — verify with `npx sst version`.
32- **AWS CLI** configured with credentials for the target account.
33- **Package manager** identified from `package.json` (npm vs pnpm).
34- **Context7 MCP** available for SST/Pulumi syntax verification.
35- **AWS docs MCP** available for AWS-side fact verification.
36- **Windows host (PowerShell)** is the primary environment. Adjust path separators and shell syntax accordingly.
37
38## Procedure
39
40### Step 1 — Orient: read the repo before you touch it
41
42SST projects are conventional but not identical. Before editing, build a quick map so your change matches the house style instead of fighting it:
43
441. **Read `sst.config.ts`** — the app name, `home`, providers/region, `defaultTags`, any global `$transform` (Node runtime pin, bundle fixups), and the order in which `run()` imports `infra/` modules. The import order *is* the dependency order; respect it.
452. **Read `infra/`** — one file per domain (storage, functions, api, observability…). This is where resources are declared. Check for an `infra/CLAUDE.md` — these projects keep IaC-specific rules there, and it's the single most valuable file to read first.
463. **Read `infra/tests/`** — source-level Vitest assertions that pin resource invariants. If they exist, your change must keep them green and probably needs a new assertion.
474. **Read `package.json` / `.nvmrc`** — package manager (npm vs pnpm), Node version, and the `sst`/`pulumi` versions actually installed.
48
49```powershell
50npx sst version
51```
52
53Confirm you're on v4/Ion. If you see v2/v3 ("SST Classic", CDK-based), stop — these patterns don't apply.
54
55### Step 2 — Determine your mode
56
57| Situation | Follow |
58|-----------|--------|
59| New project, or adding a resource/module to an existing SST app | Authoring conventions in Step 4 |
60| Wiring one module's output into another (links, SSM, IAM scope) | Sharing notes in Step 4 (`link:` vs SSM) |
61| Writing tests for infra so changes don't silently break | Step 5 — Test |
62| Running a deploy, or a deploy just failed | Step 6 — Deploy/operate |
63| Migrating a resource between Pulumi types, renaming a physical name | Step 6 migration notes |
64
65**Read the matching procedure step before editing** — each step carries the *why* behind the rule, which matters more than the rule itself.
66
67### Step 3 — Verify syntax
68
69Verify current syntax with Context7 (`resolve-library-id` → `query-docs` for `sst` or `pulumi-aws`) when you're unsure about a component's options. Verify AWS-side facts (service limits, model IDs, IAM action names, region availability) with the AWS docs MCP, never from memory. Don't guess at a component's option name.
70
71### Step 4 — Author the resource/module
72
73Follow the authoring conventions below. Match the surrounding file's commenting density and naming — these projects comment the *why* heavily, and a terse one-liner in a heavily-annotated file reads as a regression.
74
75**Universal conventions (apply everywhere):**
76
77- **Control the Node runtime deliberately, in one place.** Don't leave it to whatever the installed SST happens to default to. The idiom is a single global `$transform(sst.aws.Function, (args) => { args.runtime ??= "nodejs24.x" })` in `run()` — `??=` is correct here (the transform runs before the component applies its own default, so it fills in only when the user didn't set one). Recent SST already defaults to a current Node runtime, so check the installed default first (Context7); the transform is then version-independence insurance so a future SST downgrade can't silently move your fleet.
78- **Never interpolate a Pulumi `Output<T>` into a plain JS template literal.** Use `$interpolate` (or `pulumi.interpolate`). A bare top-level `` `${bucket.arn}/*` `` stringifies the `Output` to a `[Output<T>]` placeholder and produces a broken ARN that only fails at deploy time (it type-checks and `sst dev` runs fine). The fix is `$interpolate``` `${bucket.arn}/*` ``. This has caused prod deploy outages.
79- **Prefer typed `sst.aws.*` / `aws.*` resources over the `aws.cloudcontrol.Resource` escape hatch.** CloudControl outputs are stringly-typed and `oneOf` fields don't patch cleanly. Use it only when no typed resource exists yet, and migrate off it when one ships.
80
81**Project-specific defaults (adopt for consistency, but confirm per repo):**
82
83- **Region `ap-northeast-1`**, `home: "aws"`, and `defaultTags` carrying `Project` / `Stage` / `ManagedBy: "sst"`.
84- **Stage-gated lifecycle**: `removal: stage === "prod" ? "retain" : "remove"` and `protect: stage === "prod"` so prod resources survive a stack tear-down and non-prod previews clean up.
85- **SSM Parameter Store as the out-of-graph contract** under a `/{app}/{stage}/{domain}/...` prefix — for consumers that aren't in the Pulumi graph (CI scripts, sibling apps, operators). For *same-app* Lambdas, prefer SST `link:` (it wires a real dependency edge and grants IAM); don't route same-app sharing through SSM.
86- **Lazy `await import("./infra/<module>")` inside `run()`** so `sst dev` hot-reload stays light. (For testing, a module export still runs its top-level `new sst.aws.*` unless it's wrapped in a factory function — see Step 5 for how to test infra.)
87- **Source-level Vitest tests** on every infra module — a lightweight, house-style regression net asserting on the *source text* (resource names, index shapes, IAM scopes). It's a deliberate choice, not an SST limit: Pulumi *does* support runtime mocks (`@pulumi/pulumi/runtime`) for behavioral graph tests when a module has real logic. Source assertions don't replace a preview-deploy + smoke test.
88- **An observability gate**: every new Lambda/queue/schedule gets an alarm and structured logging before merge. Whether you enforce this depends on the project, but it's cheap insurance.
89
90When you introduce a convention, say which bucket it's in ("this is universal" vs "matching this repo's house style") so the user can override the project-specific ones deliberately.
91
92### Step 5 — Test
93
94Add or update source-level assertions as described in this step and run:
95
96```powershell
97npx vitest
98```
99
100Or use the repo's `test` script:
101
102```powershell
103npm test
104```
105
106Run `npx sst diff` and/or `tsc --noEmit` to catch type and plan errors before deploying:
107
108```powershell
109npx sst diff
110npx tsc --noEmit
111```
112
113### Step 6 — Deploy/operate
114
115Follow the deploy steps below. **Confirm the target account before any deploy:**
116
117```powershell
118aws sts get-caller-identity
119```
120
121Then deploy:
122
123```powershell
124npx sst deploy
125```
126
127For migrations (resource between Pulumi types, renaming a physical name), default to **two sequential PRs** — Pulumi creates-before-destroys, so for a uniqueness-constrained AWS name (bucket, IAM role, gateway) the old resource still owns it and the create fails with `ConflictException`. Two sequential deploys (teardown, then recreate) is the conservative default; `aliases:` / `pulumi import` / state surgery can bridge identity in some cases but only with a reviewed plan. See the migration notes in this step.
128
129### Step 7 — Clean up
130
131Clean up any exported state files — they contain account IDs and ARNs and must not linger in `/tmp` or chat history.
132
133## Pitfalls
134
1351. **`Output<T>` in template literals causes silent prod outages.** A bare `` `${bucket.arn}/*` `` stringifies the `Output` to `[Output<T>]` and produces a broken ARN. It type-checks and `sst dev` runs fine — it only fails at deploy time. Always use `$interpolate``` `${bucket.arn}/*` `` or `pulumi.interpolate`. This has caused real prod deploy outages.
136
1372. **Resource-type migrations fail with `ConflictException`.** Pulumi creates-before-destroys. For uniqueness-constrained AWS names (buckets, IAM roles, gateways), the old resource still owns the name when the new one tries to create. Default to two sequential deploys (teardown, then recreate). `aliases:` / `pulumi import` / state surgery can bridge identity but only with a reviewed plan.
138
1393. **`aws.cloudcontrol.Resource` is a trap door.** Outputs are stringly-typed and `oneOf` fields don't patch cleanly. Use it only when no typed resource exists yet, and migrate off it when one ships.
140
1414. **Don't route same-app sharing through SSM.** For same-app Lambdas, use SST `link:` — it wires a real dependency edge and grants IAM. SSM is for out-of-graph consumers (CI scripts, sibling apps, operators) only.
142
1435. **Don't hand-set `runtime` on individual functions.** Use the global `$transform` with `??=` so you fill in only when the user didn't set one. Hand-setting diverges from the fleet unless intentionally diverging (e.g., a Python function).
144
1456. **SST v2/v3 ("SST Classic") is a different framework.** These patterns don't apply. Always verify with `npx sst version` first.
146
1477. **Import order in `run()` is dependency order.** Respect it. Reordering imports can break resource references.
148
1498. **Source-level tests don't replace preview-deploy + smoke test.** They assert on source text, not runtime behavior. For modules with real logic, consider Pulumi runtime mocks (`@pulumi/pulumi/runtime`).
150
1519. **State files contain account IDs and ARNs.** They must not linger in `/tmp` or chat history. Clean them up after any export.
152
15310. **Don't guess at component option names.** Verify with Context7 (`resolve-library-id` → `query-docs` for `sst` or `pulumi-aws`). Verify AWS-side facts with the AWS docs MCP, never from memory.
154
155## Verification
156
1571. **Confirm SST version (must be v4/Ion):**
158 ```powershell
159 npx sst version
160 ```
161 Expected: v4.x with `$config` + `.sst/platform/` signature.
162
1632. **Confirm target AWS account before deploy:**
164 ```powershell
165 aws sts get-caller-identity
166 ```
167 Verify the account ID matches your expectation before proceeding.
168
1693. **Type-check the project:**
170 ```powershell
171 npx tsc --noEmit
172 ```
173 No errors expected.
174
1754. **Preview the deploy plan:**
176 ```powershell
177 npx sst diff
178 ```
179 Review the diff for unexpected creates/deletes/updates.
180
1815. **Run the test suite:**
182 ```powershell
183 npx vitest
184 ```
185 All source-level assertions must pass. Add new assertions for new resources.
186
1876. **Post-deploy smoke test:** Verify the deployed resource is reachable and behaves as expected. Source-level tests don't replace this.
188
189## What good looks like
190
191- The change is the smallest diff that satisfies the requirement, in the right `infra/` module, wired into `run()` in dependency order.
192- Every Lambda gets the right runtime via the global transform (you didn't hand-set `runtime` unless intentionally diverging — e.g. a Python function).
193- Cross-resource references use `link:` (in-graph) and/or `$interpolate`-scoped IAM; outputs other tools consume are published to SSM under the stage prefix.
194- New infra has a matching source-level test, and the existing suite stays green.
195- You confirmed AWS-side facts via the docs MCP and SST/Pulumi syntax via Context7 rather than relying on recall.
196- Anything irreversible (deploy, `sst remove`, a resource-type migration) was flagged to the user with the account it targets, and migrations were planned as two PRs, not one.
197
198## Related skills
199
200- **aws-cdk-development** — for CDK-based AWS IaC projects (SST v2/v3 Classic).
201- **aws-lambda-development** — for Lambda function authoring patterns.
202- **aws-iam-least-privilege** — for IAM policy scoping within SST resources.
203
204## Limitations
205
206- Use this skill only when the task clearly matches its upstream source and local project context.
207- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
208- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.