Skip to content

Commit 16df8f0

Browse files
authored
Merge branch 'main' into villAsh-fix/copy-md-btn
2 parents 126224c + 03b312b commit 16df8f0

File tree

1 file changed

+63
-5
lines changed

1 file changed

+63
-5
lines changed

src/blog/directives-and-the-platform-boundary.md

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ But now we are watching a new trend emerge. Frameworks are inventing their own t
1616

1717
There is an important distinction: these are not standardized JavaScript features. Runtimes don't understand them, there is no governing specification, and each framework is free to define its own meaning, rules, and edge cases.
1818

19-
This can feel ergonomic today, but it also increases confusion, complicates debugging, and imposes costs on tooling and portabilitypatterns we’ve seen before.
19+
This can feel ergonomic today, but it also increases confusion, complicates debugging, and imposes costs on tooling and portability, patterns we’ve seen before.
2020

2121
---
2222

@@ -77,7 +77,7 @@ export const action = server(
7777
)
7878
```
7979

80-
APIs carry provenance (imports), versioning (packages), composition (functions), and testability. Directives typically don’t and trying to encode options into them can quickly become a design smell.
80+
APIs carry provenance (imports), versioning (packages), composition (functions), and testability. Directives typically don’t, and trying to encode options into them can quickly become a design smell.
8181

8282
---
8383

@@ -104,7 +104,7 @@ An example of where we've seen these struggles before is with decorators. TypeSc
104104

105105
### “Isn’t this just a Babel plugin/macro with different syntax?”
106106

107-
Functionally, yes — both directives and custom transforms can change behavior at compile time. The issue isn’t capability; it’s surface and optics.
107+
Functionally, yes. Both directives and custom transforms can change behavior at compile time. The issue isn’t capability; it’s surface and optics.
108108

109109
- Directives look like the platform. No import, no owner, no explicit source. They signal “this is JavaScript.”
110110
- APIs/macros point to an owner. Imports provide provenance, versioning, and discoverability.
@@ -192,6 +192,64 @@ Even durable tasks, caching strategies, and execution locations are now being en
192192

193193
---
194194

195+
### Considering APIs instead of directives for option‑rich features
196+
197+
Durable execution is a good example (e.g., `'use workflow'`, `'use step'`), but the point is general: directives can collapse behavior to a boolean, while many features benefit from options and room to evolve. Compilers and transforms can support either surface; this is about choosing the right one for longevity and clarity.
198+
199+
```js
200+
'use workflow'
201+
'use step'
202+
```
203+
204+
One option: an explicit API with provenance and options:
205+
206+
```js
207+
import { workflow, step } from '@workflows/workflow'
208+
209+
export const sendEmail = workflow(
210+
async (input) => {
211+
/* ... */
212+
},
213+
{ retries: 3, timeout: '1m' }
214+
)
215+
216+
export const handle = step(
217+
'fetchUser',
218+
async () => {
219+
/* ... */
220+
},
221+
{ cache: 60 }
222+
)
223+
```
224+
225+
Function forms can be just as AST/transform‑friendly as directives, and they carry provenance (imports) and type‑safety.
226+
227+
Another option is to inject a global once and type it:
228+
229+
```ts
230+
// bootstrap once
231+
globalThis.workflow = createWorkflow()
232+
// global types (e.g., global.d.ts)
233+
declare global {
234+
var workflow: typeof import('@workflows/workflow').workflow
235+
}
236+
```
237+
238+
Usage stays API‑shaped, without directives:
239+
240+
```ts
241+
export const task = workflow(
242+
async () => {
243+
/* ... */
244+
},
245+
{ retries: 5 }
246+
)
247+
```
248+
249+
Compilers that extend ergonomics are great. Just look at JSX is a useful precedent! We just need to do it carefully and responsibly: extend via APIs with clear provenance and types, not top‑level strings that look like the language. These are options, not prescriptions.
250+
251+
---
252+
195253
### Subtle forms of lock‑in can emerge
196254

197255
Even when there is no bad intent, directives create lock in by design:
@@ -222,7 +280,7 @@ If multiple frameworks truly want shared primitives, a responsible path is:
222280
- Propose primitives to TC39 when appropriate
223281
- Keep non standard features clearly scoped to API space, not language space
224282

225-
Directives should be rare, stable, and standardized—used judiciously rather than proliferating across vendors.
283+
Directives should be rare, stable, standardized and especially used judiciously rather than proliferating across vendors.
226284

227285
---
228286

@@ -234,7 +292,7 @@ It’s tempting to compare criticism of directives to the early skepticism aroun
234292

235293
### The bottom line
236294

237-
Framework directives might feel like DX magic today, but the current trend risks a more fragmented futuredialects defined not by standards, but by tools.
295+
Framework directives might feel like DX magic today, but the current trend risks a more fragmented future consisting of dialects defined not by standards, but by tools.
238296

239297
We can aim for clearer boundaries.
240298

0 commit comments

Comments
 (0)