Local-First DX:哪些工作適合放在瀏覽器,哪些應留在 CLI
Local-First DX — What Belongs in the Browser vs a CLI
作者提出選擇前端本機工具介面的判斷方法:可重複、以磁碟路徑為輸入,或需供 CI 和 Agent 執行的工作適合 CLI;一次性、需要視覺判斷或處理暫存檔案的工作則適合瀏覽器。文章亦提醒,本機瀏覽器工具不應在未經選擇下把資料 POST 到外部服務,並建議團隊避免維護功能重疊的兩套介面。
I keep two folders of “tools I actually use.” One is a terminal aliases file. The other is a browser bookmarks bar labeled local. Both claim to be local-first. They solve different jobs, and mixing them up costs me more time than picking the slower option on purpose.
This isn’t “CLI good, browser bad.” It’s a decision rule for frontend DX: when should this live in the tab, and when should it stay in the shell?
The false dichotomy
People talk like local tooling is one pile. It isn’t.
- A CLI is scriptable, pipeable, and great at files that already live on disk. Agents love it. CI loves it. Diffs love it.
- A browser-local tool is visual, drop-zone friendly, and great at blobs you don’t want to save as
tmp-whatever-3.pngforever. No install. Nochmod +x. Close the tab and it’s gone.
Both can keep data off a third-party server. That’s the local-first part. The UX surface is what actually changes how you work.
Where I default to the CLI
I reach for the shell when most of these are true:
-
The input is already a path — repo files,
dist/, fixtures, screenshots ine2e/artifacts/ - I need to repeat it — same transform tomorrow, or in CI, or for every PR
-
I want a pipe —
rg→jq→ script → commit message - An agent will do it — coding agents are fluent with argv and stdout; they’re clumsy in a GUI
- Exit codes matter — green/red in CI beats “looks fine in the preview pane”
Examples that belong here for me:
- Lint, typecheck, format, bundle-size budgets
- Image optimization of a whole
public/folder - Converting a design-token JSON into CSS variables as part of build
- Replaying a failing Playwright run with the same flags
If I have to teach a teammate (or an agent) a click path, I’ve already lost the CLI case.
Where I default to the browser
I open a local page / side panel when most of these are true:
- The input is a blob I don’t want on disk yet — paste, drag-drop, clipboard screenshot
- I need eyes — color, layout, chart shape, crop, “does this GIF feel right”
- One-shot — I’m not putting this in CI; I’m answering a Slack “can you check this file”
-
Install friction would kill the job — non-dev collaborator, locked-down laptop, hotel Wi‑Fi where
npm iis a joke - The output is also a blob — download once, attach, done; no need for a long-lived path in the monorepo
Examples:
- Quick HEIC / WebP peek before I decide whether to commit it
- Charting a client CSV I’m not allowed to upload anywhere
- Diffing two JSON pastes from support tickets
- Checking contrast on a hex someone dropped in chat
If the “tool” requires me to invent a filename first, the browser usually wins.
The agent angle (this changed my defaults)
Coding agents flipped a few of my old habits.
Agents are excellent at CLI. They can run pnpm test, read the log, and try again. They are mediocre at “open this URL, drop a file, click Export.” So anything I want agents to own — refactors, migrations, CI gates — stays CLI-shaped.
But agents also create a lot of visual leftovers: screenshots, generated SVG, “try this CSS,” chart previews. Those leftovers are terrible as permanent repo files and fine as ephemeral browser transforms. I stopped asking the agent to curl a SaaS converter for those. I either keep a local browser tool bookmarked, or I ask the agent to write a small Node script if the job will recur.
Rule of thumb I use now:
If the agent should do it twice, make a CLI (or a script in the repo).
If a human needs to see it once, keep it in the browser.
A boring decision checklist
When I’m adding a new “helper” for the team, I ask:
| Question | Lean CLI | Lean browser |
|---|---|---|
| Will CI run it? | yes | no |
| Is the primary input a disk path? | yes | no (paste/drop) |
| Is judgment visual? | no | yes |
| Should an agent own the loop? | yes | no |
| Do non-devs need it without Node? | no | yes |
| Is the artifact temporary? | maybe | usually |
If the table splits 3–3, I build the CLI first and add a thin browser UI later only if humans keep bouncing off the flags. Flags are documentation; a drop zone is empathy. Both can exist — just don’t start with two surfaces.
Anti-patterns I’ve paid for
Electron for a one-button transform. Congrats, you shipped an OS update surface for a FileReader job.
“Just paste into this SaaS.” Local-first died the moment the paste left the machine. Browser-local and cloud-paste are not the same category.
CLI with no --stdin / no file args, only interactive prompts. Agents and CI choke. If it’s CLI, make it non-interactive by default.
Browser tool that quietly POSTs “for processing.” Read the network tab once. Local means the heavy work stays in-tab unless I opt in.
How I keep the split sane on a team
-
Repo scripts for anything that gates merges (
package.jsonscripts,just,make— pick one). - A short internal doc titled “browser-local helpers” with 5–10 bookmarks, not 40. If it isn’t in the doc, it isn’t a team tool.
- No duplicate truth — don’t maintain a CLI and a web UI that drift. Prefer one engine (shared WASM / shared Node package) and two thin fronts only when both audiences are real.
- Name the constraint in the PR — “this is agent-facing → CLI” or “this is designer-facing → browser” stops bikesheds.
Takeaway
Local-first isn’t a single product category. It’s a privacy/ownership promise that still needs a surface.
Put repeatable, path-based, agent-friendly work in the CLI. Put visual, ephemeral, drop-zone work in the browser. Optimize for who (or what) will run the loop next — you once, CI forever, or an agent at 2 a.m.
The wrong surface doesn’t just feel awkward. It quietly pushes people back to uploading files to whoever’s landing page loaded first. Pick deliberately, and the local habit sticks.
來源:DEV Community · dev.to