1---
2title: Phases
3description: Alchemy programs run in two phases — Construction drives the deploy, Runtime handles requests. Knowing which is which is the key to writing Workers and Lambda Functions.
4---
5
6import DAG from "../../../components/DAG.astro";
7
8Every alchemy program runs in two phases — plantime builds the plan,
9runtime serves requests. [Runtime](/infrastructure-as-effects/runtime)
10resources — Workers, Lambdas, Containers — express both in a single
11program by **returning an Effect from inside an Effect**. Earlier
12pages named the two halves the Construction phase and the Runtime
13phase; this page pins down exactly when each one runs.
14
15## Construction vs Runtime
16
17```typescript
18Cloudflare.Worker(
19 "Worker",
20 { main: import.meta.url },
21 Effect.gen(function* () {
22 // ─── Construction phase ───
23 const bucket = yield* Cloudflare.R2.ReadWriteBucket(Bucket);
24
25 return {
26 // ─── Runtime phase ───
27 fetch: Effect.gen(function* () {
28 const obj = yield* bucket.get("key");
29 return HttpServerResponse.text(yield* obj.text());
30 }),
31 };
32 }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)),
33);
34```
35
36| Phase | Code | When it runs |
37| ------------ | ------ | ------------------------------------- |
38| Construction | outer | At plantime **and** at cold start |
39| Runtime | inner | Only inside a deployed handler |
40
41The `bucket` value is established once during Construction and captured
42by the runtime closure. The constructor runs at most once per cold start; the
43runtime body runs per request with everything already wired up.
44Each phase has its own `Scope` with very different lifetimes —
45[Instance scope vs request scope](/infrastructure-as-effects/runtime#instance-scope-vs-request-scope)
46covers where cleanup can (and cannot) happen.
47
48The Runtime phase is the *only* place where `Alchemy.RuntimeContext`
49is available. Any Effect whose requirements include `RuntimeContext`
50can only execute inside the runtime closure — the type system
51rejects it everywhere else.
52[Layers](/infrastructure-as-effects/layers#the-types-hold-the-boundary)
53builds on this split to keep whole services honest about where they
54can run.
55
56## What runs when
57
58<DAG
59 caption="Plantime (top) records bindings and builds the plan. Runtime (bottom) starts on cold start and runs the handler per request."
60 nodes={[
61 { id: "Plan", label: "alchemy deploy", type: "plantime", tone: "accent" },
62 { id: "ConstructP", label: "construct()", type: "records bindings" },
63 { id: "Apply", label: "apply", type: "create / update" },
64 { id: "Deploy", label: "deployed", type: "Worker / Lambda" },
65 { id: "Cold", label: "cold start", type: "runtime", tone: "accent", layer: 0 },
66 { id: "ConstructR", label: "construct()", type: "builds SDK clients", layer: 1 },
67 { id: "Runtime", label: "fetch()", type: "per request", layer: 2 },
68 ]}
69 edges={[
70 { from: "Plan", to: "ConstructP" },
71 { from: "ConstructP", to: "Apply" },
72 { from: "Apply", to: "Deploy" },
73 { from: "Cold", to: "ConstructR" },
74 { from: "ConstructR", to: "Runtime" },
75 ]}
76/>
77
78- At **plantime**, the constructor runs to discover bindings — alchemy needs
79 to know which resources the handler will use so it can wire
80 permissions, env vars, and references.
81- At **runtime cold start**, the constructor runs again — this time inside the
82 deployed Worker, where the same `bind()` calls return live SDK
83 clients backed by the deployed resource.
84- The **runtime body** only runs in the deployed handler. It never
85 executes at plantime, so you can put real per-request work there
86 without affecting deploy speed.
87
88## `ALCHEMY_PHASE`
89
90The current phase is exposed as the `ALCHEMY_PHASE` environment
91variable / config key:
92
93| Value | Context |
94| --------- | ------------------------------------------------------------------- |
95| `plan` | Default. Running `alchemy deploy`, `alchemy plan`, or `alchemy dev`. |
96| `runtime` | Running inside a deployed Worker or Lambda Function. |
97
98Most user code never reads this directly — but providers and
99[Bindings](/infrastructure-as-effects/binding) use it internally to behave
100differently across phases.
101
102## `ALCHEMY_DEV`
103
104`alchemy dev` is a plantime phase, so `ALCHEMY_PHASE` is `plan`
105during local development just like a regular deploy. To tell whether
106you're running under `alchemy dev` specifically, read the
107`ALCHEMY_DEV` config key. The CLI sets `ALCHEMY_DEV=true` on the dev
108process; every other entrypoint leaves it unset, so it defaults to
109`false`.
110
111```typescript
112import { ALCHEMY_DEV } from "alchemy";
113
114Effect.gen(function* () {
115 if (yield* ALCHEMY_DEV) {
116 // local-dev-only behavior
117 }
118});
119```
120
121## The `__ALCHEMY_RUNTIME__` guard
122
123A binding's contract is a `Binding.Service`; the guarded setup
124Effect lives in the implementation Layer you provide, not the
125contract. That setup Effect does two things — and the phase decides
126how much of it runs:
127
128| Work | Phase | Job |
129| ----------------------- | ----- | -------------------------------------------------------- |
130| typed client | both | The lightweight typed SDK that ships with the bundle. |
131| `host.bind(...)` wiring | plan | Deploy-time registration of IAM / env / native bindings. |
132
133The deploy-time wiring is fenced behind `if (!globalThis.__ALCHEMY_RUNTIME__)`.
134At plantime the guard is open, so calling `bind()` records what the
135function will need. At runtime the global flag is set, so the guard is
136skipped and `bind()` returns just the typed client. The runtime bundle
137stays small because the planning branch never executes.
138
139Which wiring the guard protects is decided by the implementation
140Layer you provided
141([Bindings](/infrastructure-as-effects/binding#a-contract-and-a-layer)).
142
143This is also how alchemy can let you write `bucket.get(...)` inside a
144Worker without bundling AWS / Cloudflare provisioning code: the
145provisioning lives behind the guard, never on the runtime path.
146
147## Why this matters
148
149The Construction/Runtime split lets you write code that:
150
1511. **Resolves infrastructure references at deploy time** — bindings
152 know which bucket ARN, queue URL, etc. to inject.
1532. **Initializes SDK clients once at cold start** — not on every
154 request.
1553. **Handles requests with a pre-configured context** — the `bucket`
156 variable in the runtime body already knows which resource to
157 talk to.
158
159## Where next
160
161- [Circular Bindings](/infrastructure-as-effects/circular-bindings) — the two-pass plan this phase split makes possible. Next page.
162- [Layers](/infrastructure-as-effects/layers) — `RuntimeContext` as a type-level boundary; infrastructure behind service interfaces.
163- [State Store](/state-store) — where deploy results persist between runs.