You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/blog/directives-and-the-platform-boundary.md
+63-5Lines changed: 63 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,7 +16,7 @@ But now we are watching a new trend emerge. Frameworks are inventing their own t
16
16
17
17
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.
18
18
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.
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.
20
20
21
21
---
22
22
@@ -77,7 +77,7 @@ export const action = server(
77
77
)
78
78
```
79
79
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.
81
81
82
82
---
83
83
@@ -104,7 +104,7 @@ An example of where we've seen these struggles before is with decorators. TypeSc
104
104
105
105
### “Isn’t this just a Babel plugin/macro with different syntax?”
106
106
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.
108
108
109
109
- Directives look like the platform. No import, no owner, no explicit source. They signal “this is JavaScript.”
110
110
- 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
192
192
193
193
---
194
194
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:
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
+
declareglobal {
234
+
var workflow:typeofimport('@workflows/workflow').workflow
235
+
}
236
+
```
237
+
238
+
Usage stays API‑shaped, without directives:
239
+
240
+
```ts
241
+
exportconst 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
+
195
253
### Subtle forms of lock‑in can emerge
196
254
197
255
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:
222
280
- Propose primitives to TC39 when appropriate
223
281
- Keep non standard features clearly scoped to API space, not language space
224
282
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.
226
284
227
285
---
228
286
@@ -234,7 +292,7 @@ It’s tempting to compare criticism of directives to the early skepticism aroun
234
292
235
293
### The bottom line
236
294
237
-
Framework directives might feel like DX magic today, but the current trend risks a more fragmented future—dialects 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.
0 commit comments