0029: Turbo watch dev-mode
Date: 2026-04-09
Status: Accepted
Context​
The repository uses Turborepo to orchestrate build, test and dev tasks across multiple packages. Developers expect a responsive local development experience: long-running processes (dev servers, Storybook, emulators) should remain running while one-shot tasks (codegen, type checks, builds) re-run when sources change.
Turborepo provides turbo run for single-shot execution and turbo watch for persistent watch-mode workflows. turbo watch keeps persistent tasks running and restarts interruptible tasks when their inputs change; it also provides a daemon that improves responsiveness for watch workflows.
Decision​
-
Use
turbo watch devfrom the repository root as the primary local development command. This keeps persistent tasks running and re-runs interruptible tasks on file changes. -
Configure a
devtask inturbo.jsonwithpersistent: trueanddependsOn: ["^build"]. One-shot package tasks should be interruptible soturbo watchcan restart them when inputs change. -
Invoke watch with package filters in the npm script (e.g.,
turbo watch dev --filter='./apps/*' --filter='./packages/*') so common development sessions are scoped and fast by default. -
Start a local HTTP/HTTPS proxy (portless) before
turbo watchto ensure local assets, authentication, and cross-origin flows behave like production. Avoid POSIX-only script chaining in package.json; prefer safe command chaining at the npm-script level (e.g.,pnpm proxy:stop && pnpm proxy:start && turbo watch ...). -
Enable Turborepo future flags used by this workflow (e.g.,
watchUsingTaskInputs) inturbo.jsonto stabilize watch semantics and avoid cache misses or false cache hits for watch-triggered runs.
Consequences​
- Improved local developer feedback loops: persistent servers remain available while build/codegen tasks rerun on change.
- Task design must account for persistent vs. interruptible semantics. Persistent tasks cannot be depended on by other tasks; use
^buildor explicit package-level orchestration to preserve build ordering. - Package.json script chaining should avoid POSIX-only separators (
;) and instead use&&/||patterns that work on both Unix shells and Windowscmd.exein npm scripts.
Implementation notes​
- Root
turbo.jsondefinesdevandstarttasks;devispersistent: trueand depends on^build. - The root
devnpm script runspnpm proxy:stop && pnpm proxy:start && turbo watch dev --filter='./apps/*' --filter='./packages/*'. - Keep an explicit
proxy:stopscript that tolerates missing proxy (portless proxy stop || true) and aproxy:startscript that only starts the proxy (portless proxy start -p 1355 --https).
Alternatives considered​
- Shell-only chaining with
;and|| true: rejected due to cross-platform incompatibility on Windowscmd.exe. - Adding a third-party tool to normalize cross-platform shell semantics (e.g.,
npm-run-all): rejected to avoid an additional dependency; the npm scripts provide sufficient expressiveness when written with&&/||. - Using small Node wrapper scripts to abstract startup logic: considered but rejected in favor of keeping tooling simple and avoiding extra files unless startup logic becomes more complex.
Follow-ups​
- Document the developer workflow in repository README with example
pnpmcommands and recommended--filterpatterns. - Add CI checks or guidance to ensure packages that need to be interruptible are configured correctly in
turbo.json.