Lesson 0004 · ~12 minutes

Boundaries & integration deal-breakers

One skill: run a design-review risk scan — what a dedicated worker can do, what it can’t, and which integration edges kill the plan before code ships.

Win for this lesson

You can reject a bad worker proposal in 60 seconds (DOM coupling, wrong tool, tooling/lifecycle missing) and green-light a good one with eyes open.

1. The hard boundary: no page DOM

MDN’s core rule still drives architecture: you can run almost any code in a worker, with important exceptions — notably you cannot directly manipulate the DOM or treat the page window as your global (Web Workers API).

System design split that works:

That is the same “UI thread vs everything else” split native platforms push — web.dev’s OMT guide leans on it hard.

2. Capability map (dedicated worker)

Authoritative inventory: Functions and classes available to workers. High-signal FE facts:

Usually yes Usually no / different
fetch, timers, console, IndexedDB, WebSockets, structured clone helpers, many binary/media helpers DOM APIs, page window, localStorage/sessionStorage (prefer IDB), React/Vue render
Classic: importScripts; module workers: import Assuming every main-thread API exists — always check the list
Design review question

“What does this code touch that is DOM or storage-API specific?” If the answer is “half the module,” either split a pure core or keep it on main.

3. Integration edges that burn teams

A. Bundler & script URL

new Worker("worker.js") is easy in demos and fragile in apps. Modern guidance (MDN + webpack/Vite docs): resolve the worker as a module asset:

// Portable pattern (webpack 5+, Vite, etc.)
const worker = new Worker(
  new URL("./worker.js", import.meta.url),
  { type: "module" } // if the worker is ESM
);

// Vite also supports:
// import Worker from "./worker.js?worker"

Risk: wrong relative path, CDN/publicPath mismatches, classic vs module mismatch. Budget “make the worker load in prod” as a real task.

B. Origin

Worker scripts are subject to origin rules. Cross-origin worker scripts are a common footgun; modern browsers surface failure via the worker error event rather than a helpful constructor throw (see MDN Worker). Prefer shipping the worker with your app origin/bundle.

C. Lifecycle ownership

From lesson 2: create → message → terminate on unmount. Deal-breaker is organizational: if no component “owns” the worker, you leak threads across SPA navigations. Spec note: workers are relatively heavy — reuse, don’t spawn casually (WHATWG).

D. Protocol & errors

Multi-job workers without type + correlation id become racey soup. Missing onerror / messageerror means silent production failure. This is integration, not trivia.

E. Wrong tool

Offline cache, push, request interception → service worker. Multi-tab shared session bus → maybe shared worker (later) or BroadcastChannel. Don’t force dedicated workers into those shapes.

4. When not to use a worker

Too small

Work already fits budgets on target devices — measure first; worker overhead can dominate.

DOM-bound

The bottleneck is layout/paint/React commit, not pure JS — workers can’t paint your tree for you.

Yield enough

Chunking on main (lesson 1 spectrum) already keeps input responsive — simpler ops story.

Team cost

Protocol + bundler + debug + clone design exceeds the jank risk you actually have.

5. Practice — green light or kill?

Equal-length options. Design-review mode.

Review A

Proposal: move the React list virtualizer (measures DOM nodes, writes styles) fully into a dedicated worker.

Review B

Proposal: parse large JSON reports in a long-lived module worker; main only renders table rows from results; terminate on leave.

Review C

Proposal: “Add a dedicated worker so the SPA works offline and caches the app shell.”

Review D

Proposal: for email format checks (~0.1ms), spawn a worker on every keystroke and post the full form state object.

6. Steal this 60-second scan

  1. Is the bottleneck pure work or DOM/layout?
  2. Can results be messages (DTOs), not live objects?
  3. Who owns create/terminate? Same-origin worker URL?
  4. Bundler story clear? Error handlers wired?
  5. Payload size/frequency OK — or patch/transfer plan?
  6. Is this actually a service worker problem?

Full table: integration checklist.

7. What to remember

Ask your teacher Vite/webpack worker setup for your stack, OffscreenCanvas, or a real PR proposal — ask in chat.

Primary source (read next)

MDN — Functions and classes available to workers (capability inventory). Optional: your bundler’s worker docs (webpack / Vite).