Performance Tuning
Performance tuning in a frontend toolchain is not one trick.
It is not "rewrite it in Rust" and then wait for graphs to go up. It is a long series of small, concrete decisions about where the time goes, how often memory moves, how much work is duplicated, and whether the architecture lets improvements compound.
This note is a knowledge-share of the things Vize keeps optimizing for.
Measure the Whole Loop
Compiler benchmarks are useful, but they are not the whole developer experience.
A Vue toolchain has several feedback loops:
one-file compile in a dev server
full production build
linting many files
formatting many files
type checking generated virtual files
editor diagnostics while the user types
CI checks across real applications
AI-generated patches being validated repeatedly
The slowest loop is not always the most obvious one.
A function that looks fast in isolation can still be harmful if it runs in every stage. A small allocation can still matter if it happens for every token, every AST node, every diagnostic, and every generated segment.
That is why Vize treats performance as a toolchain property, not only a compiler property.
Avoid Duplicate Work
The most reliable optimization is to not do the work twice.
In a fragmented setup, the same .vue file can be parsed separately by:
the compiler
the linter
the formatter
the type checker
the editor integration
the component documentation pipeline
That is expensive, but the deeper problem is architectural. If every tool builds its own understanding of the file, performance tuning becomes local and limited.
Vize is designed around shared structure:
parse once where possible
keep SFC block boundaries stable
reuse template structure across compiler and diagnostics
let semantic analysis feed multiple consumers
avoid regenerating virtual TypeScript unless inputs changed
The best optimization is often a better ownership boundary.
Allocation Is a Feature, Not a Detail
Frontend tooling processes many small objects: tokens, nodes, spans, strings, scopes, diagnostics, generated code fragments.
If those objects are allocated casually, the toolchain pays for it everywhere.
Vize puts a lot of pressure on allocation behavior:
arena-style storage for short-lived compiler data
string interning where repeated identifiers or names matter
compact spans instead of copied substrings
borrowed slices where ownership is unnecessary
stable internal IDs instead of large cloned structures
The goal is not to make the code clever for its own sake.
The goal is to make the hot path boring: fewer allocations, fewer copies, fewer cache misses, fewer reasons for the allocator to become part of the profile.
Parallelism Needs Shape
Parallelism is not "turn on threads."
It works best when the problem has clear boundaries:
many independent files
deterministic aggregation
predictable output ordering
no shared global mutation
bounded caches and sessions
Vue compilation, linting, and fixture sweeps often have a natural file-level parallel shape. But type checking and editor workflows are more subtle because they depend on project state.
So Vize separates the questions:
Can this file-level work run independently?
Does this step need a resident project session?
Is the output order user-visible?
Are diagnostics stable across thread counts?
Does parallelism increase memory pressure enough to erase the win?
Fast but unstable output is not good enough. Performance work has to preserve trust.
Source Mapping Can Become a Hot Path
Vue tooling often generates intermediate code.
That means every good diagnostic needs a path back:
generated TypeScript to original template
generated render code to SFC source
transformed style or script output to original block
virtual module IDs back to real files
If source mapping is slow or imprecise, the whole toolchain suffers. The user sees diagnostics in the wrong place. AI repair loops get poor coordinates. Tests become fragile.
So source mapping deserves the same performance attention as parsing:
store spans compactly
avoid repeated path normalization
keep generated segment metadata small
test edge cases with snapshots
profile diagnostic-heavy workloads, not only successful compile paths
Diagnostics are product surface. Their performance matters.
Real Projects Beat Synthetic Comfort
Microbenchmarks are useful when answering a focused question.
But a toolchain becomes honest when it is run against real projects.
Real projects contain:
odd dependency layouts
large SFCs
legacy patterns
auto-generated code
uncommon directives
plugin conventions
path aliases
platform-specific edge cases
That is why Vize keeps investing in real-world fixture sweeps and build snapshots. The goal is not to collect impressive test counts. The goal is to expose the performance cliffs that only appear when the code is messy in the same way production code is messy.
Performance Is a Product Feature
Speed changes behavior.
If checks are slow, people run them less often. If formatting is slow, save-on-format becomes annoying. If type-aware linting is slow, teams disable the rules. If CI is slow, maintainers batch changes and review less carefully. If AI validation is slow, agents take bigger, riskier leaps.
Fast tools make stricter workflows practical.
That is the real performance argument for Vize. The goal is not only a better benchmark number. The goal is to make the strict path feel like the default path.
When compile, lint, format, type-check, and diagnostics become fast enough to run without ceremony, quality stops being a special event.
It becomes the normal way to work.