跪拜 Guibai
← All articles
Flutter · Android · iOS

Why `dart analyze` Is the Real Bottleneck in AI-Assisted Flutter Development

By 法的空间 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

For AI-driven development on large Dart/Flutter codebases, the analyzer startup cost is the real bottleneck in the modify-verify-fix loop. Making the analyzer a long-lived service brings verification time down to interactive speeds and keeps parallel worktrees from exhausting memory.

Summary

AI-assisted coding turns static analysis into a high-frequency feedback step. Each time an AI edits a few lines, `dart analyze` reruns. On large Flutter projects with 15,000 files, that means paying full analyzer startup cost every iteration: spawning a process, initializing analysis context, loading plugins. The command-line tool `dana` solves this by making the analyzer a persistent daemon. First check warms the session; subsequent checks send incremental requests. In a benchmark monorepo, a single-file check dropped from about 3 minutes with a cold `dart analyze` to 0.5 seconds with a warmed session. Directory and full-project checks also fall to about a second.

The tool also addresses parallel AI tasks. When several Git worktrees each run their own analyzer, memory blows up. `dana` supports an opt-in multi-root pool: with `DANA_MULTI_ROOT=1`, compatible worktrees share one analyzer process, keeping separate analysis contexts but sharing the Dart runtime and some global caches. Windows tests with two synthetic worktrees showed analyzer job private commit drops from 2,464 MiB to 1,435 MiB, about 41.8%, with negligible latency increase (hot single-file p50 rose 0.58 ms). The pool has safeguards: a max root count (default 3), LRU eviction, memory admission checks that fail with exit code 75 rather than spawning another analyzer, and idle root reclamation under memory pressure. On macOS, only `critical` pressure triggers pool reclamation; `warn` is treated as a hint.

`dana` is part of the `dart_analysis_daemon` package on pub.dev, and a companion `dart_analysis_server_proxy` lets the command line reuse an IDE's already-running analyzer. The multi-root pool is deliberately opt-in to avoid changing behavior for users who don't need it. The author's key point: persistence beats narrowing scope. Repeatedly starting a fresh analyzer is the cost that hurts AI loops, not the per-file analysis itself.

Takeaways
— In a 15,000-file Flutter monorepo, a single-file check drops from about 3 minutes with a cold `dart analyze` to about 0.5 seconds with a warmed `dana` session.
— Persistent session reuse beats narrowing scope: `dana` keeps one analyzer alive, so repeated AI verification pays no startup cost.
— The multi-root pool, enabled via `DANA_MULTI_ROOT=1`, lets compatible Git worktrees share one analyzer process while keeping separate analysis contexts.
— Windows tests using two synthetic worktrees showed analyzer job private commit drop from 2,464.2 MiB to 1,434.9 MiB, about 41.8% savings, with hot-request p50 latency increasing by only 0.58 ms.
— The pool protects memory by capping roots (default 3), evicting LRU roots, performing admission checks that return exit code 75 on failure, and reclaiming idle roots only under memory pressure.
— `dart_analysis_server_proxy` exposes an IDE's already-running analyzer to the command line, avoiding a second analyzer instance.
— The multi-root pool is intentionally opt-in; enabling it does not change behavior for users who don't use multiple worktrees.
Conclusions

Persistence outperforms narrower scope: in the benchmark, reusing a warm analyzer saved far more time than pointing `dart analyze` at a single file, because startup dominates the cost.

The deliberate opt-in for multi-root sharing means memory benefits only appear when users set the flag, but it avoids silently changing the analyzer's process model for everyone.

Treating the analyzer as a long-lived service rather than a per-command tool reframes the economics of AI verification; cheap checks make the AI edit-verify-fix loop viable on large codebases.

Concepts & terms
Persistent analyzer session
A Dart analyzer instance that stays alive across multiple checks, preserving analysis context, loaded plugins, and diagnostic state. Instead of spawning a new process on each `dart analyze`, a daemon like `dana` reuses the warm session and sends incremental requests.
Multi-root pool
An opt-in mode in `dana` where multiple Git worktrees from the same repository share a single analyzer process. Each root keeps its own analysis context, but the process, Dart runtime, and some global caches are shared, reducing memory and startup overhead.
dart_analysis_server_proxy
A companion package that exposes the analyzer already running in an IDE (via Dart-Code's `dart.analyzerPath`) to command-line tools like `dana`, so the CLI can reuse the IDE's warm analysis state instead of starting a second analyzer.
From the discussion
Featured comments
北苑

Nice, with a slightly weaker computer analyzing a file once is very time-consuming. I'll try it out when I have time.

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗