agentleFS
MicrosoftKnown publishermicrosoft/webui/.github/skills/perf/SKILL.md

perf

Skill96 starsChanged 36 days agoMITView on GitHub

Speed and memory performance rules for Rust crates, the Node addon, webui-framework, and webui-router.

We split this skill into the rules it gives an agent

18 rules. 0 are common, 3 are distinctive, and 15 are very specific to this project.

  • 0

    Common

    Most of the 14 edited versions we read keep these.

  • 3

    Distinctive

    This author's own choices. Worth a look.

  • 15

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use this skill when modifying any performance-sensitive code across the stack.

    line 12
  2. Very specific:

    No format!() in writer output: Use sequential writer.write() calls.

    line 18
  3. Very specific:

    No .to_string() on Cow: Write Cow<str> directly to avoid defeating zero-copy.

    line 19
  4. Very specific:

    No redundant scans: Use first_part.len() == path.len() instead of path.contains('.').

    line 21
  5. Very specific:

    No String::from(ch) in escape loops: Use ch.encode_utf8(&mut buf) with a [u8.

    line 22
  6. Very specific:

    No format! in hex parsing: Use direct arithmetic ((hi_nibble << 4) | lo_nibble) instead of u8::from_str_radix(&format!(...)).

    line 23
  7. Very specific:

    No silent unwrap_or defaults: If binding_stack.pop() returns None, that's a protocol error - propagate it, don't mask it.

    line 25
  8. Very specific:

    No cloning large state trees: Use evaluate_with_resolver with a closure.

    line 30
  9. Very specific:

    No cloning String values for read-only access: Use s.as_str() for Value::String branches.

    line 32
  10. Very specific:

    Pre-allocate buffers: Use Vec::with_capacity / String::with_capacity when size is known or estimable.

    line 33
  11. Very specific:

    No deep-cloning protocol or state per request: Use Arc<T> with clone-on-write or snapshot swapping.

    line 36
  12. Very specific:

    Path-indexed targeted updates: When an @observable changes, only bindings referencing that property are visited - not the entire template.

    line 46
  13. Very specific:

    DOM cloning over innerHTML: Use cloneNode(true) from cached template fragments.

    line 47
  14. Very specific:

    No for..in on objects: Use Object.keys() with an indexed for loop — faster and prototype-safe without needing Object.hasOwn.

    line 51
  15. Very specific:

    No per-update array allocations: Avoid .filter(), .map(), .slice() in the update hot path.

    line 57
  16. Very specific:

    No for..in on objects: Use Object.keys() with an indexed for loop.

    line 70
  17. Distinctive:

    When making a performance-related change, report:

    line 121
  18. Distinctive:

    Allocation count delta: if measurable

    line 123

“Common” is measured: kept by at least 6 in 10 of the 14 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Performance

WebUI's value proposition is speed and low memory usage. Every change to core Rust crates, @microsoft/webui-framework, or @microsoft/webui-router must be evaluated through two lenses: throughput (how fast) and memory (how little).

Server memory is not cheap. Client memory is not unlimited. Every allocation that can be avoided is a win on both sides.

Use this skill when modifying any performance-sensitive code across the stack.

Rust - speed rules

These apply to webui-handler, webui-state, webui-expressions, webui-parser, webui-protocol, webui-ffi, and webui-node.

  1. No format!() in writer output. Use sequential writer.write() calls. format! allocates a temporary String every invocation.
  2. No .to_string() on Cow. Write Cow<str> directly to avoid defeating zero-copy.
  3. No collect::<Vec<_>>() on splits. Iterate path.split('.') directly. Collecting allocates a Vec for sequential access.
  4. No redundant scans. Use first_part.len() == path.len() instead of path.contains('.').
  5. No String::from(ch) in escape loops. Use ch.encode_utf8(&mut buf) with a [u8; 4] stack buffer, or batch contiguous safe chars into a single write.
  6. No format! in hex parsing. Use direct arithmetic ((hi_nibble << 4) | lo_nibble) instead of u8::from_str_radix(&format!(...)).
  7. No per-request template re-parsing. Pre-parse at protocol load time and reuse.
  8. No silent unwrap_or defaults. If binding_stack.pop() returns None, that's a protocol error - propagate it, don't mask it.
  9. Keep cold error/diagnostic construction off the hot path. Mark error builders #[cold]/#[inline(never)] so they don't inline into hot functions and perturb their code layout. Adding cold error code with no hot-path work has regressed parse benches ~4-5% purely via layout - keep per-element fast-paths inlinable and push the cold fallback out-of-line. See skills/diagnostics/SKILL.md §7.

Rust - memory rules

  1. No cloning large state trees. Use evaluate_with_resolver with a closure. Cloning duplicates the entire JSON tree in memory.
  2. No cloning HashMaps for scope. Save/restore only the overwritten key on loop iteration. A HashMap clone copies every entry.
  3. No cloning String values for read-only access. Use s.as_str() for Value::String branches; only create owned strings for Number/Bool via a scratch buffer or Cow.
  4. Pre-allocate buffers. Use Vec::with_capacity / String::with_capacity when size is known or estimable. For HTML output, 4096 bytes is a reasonable starting point.
  5. Prefer &str and slices over owned types. Pass by reference when the callee only reads. Move clone decisions to the caller.
  6. Use Cow<'_, str> when a value is sometimes borrowed, sometimes owned. Avoids unconditional allocation.
  7. No deep-cloning protocol or state per request. Use Arc<T> with clone-on-write or snapshot swapping.
  8. Cap memory for untrusted inputs. File reads during discovery must have size limits. A 100MB HTML file should not cause OOM.

TypeScript - @microsoft/webui-framework rules

These apply to packages/webui-framework (the client-side Web Component runtime).

Speed

  1. Single-pass hydration. The framework walks the DOM once to connect all bindings. No multi-pass scanning.
  2. Path-indexed targeted updates. When an @observable changes, only bindings referencing that property are visited - not the entire template.
  3. DOM cloning over innerHTML. Use cloneNode(true) from cached template fragments. Never use innerHTML for component creation.
  4. Direct event listeners. Attach @event bindings to the bound element, never a shared render root. $wireEvents runs once per block instance, so delegating stacks one listener per block on the same node and fires all of them per dispatch — O(N) for no reduction in listener count.
  5. Microtask coalescing. Multiple property changes within the same synchronous block batch into a single DOM update via queueMicrotask.
  6. Cursor-based repeat reconciliation. <for> block updates use a diff algorithm that only calls insertBefore on nodes that actually moved. Append/prepend/remove are O(1).
  7. No for..in on objects. Use Object.keys() with an indexed for loop — faster and prototype-safe without needing Object.hasOwn. Applies to setState, setInitialState, and any code iterating user-provided objects.

Memory

  1. No framework in the GC. Minimize object allocations during reactive updates. Reuse binding objects, don't recreate them.
  2. Template cache is WeakMap-keyed. Parsed template DOMs are cached per metadata object. When metadata is released (e.g., via Router.gc()), the cache entry becomes GC-eligible.
  3. No per-update array allocations. Avoid .filter(), .map(), .slice() in the update hot path. Use index-based iteration.
  4. Strip SSR markers after hydration. Comment nodes used as markers are removed from the DOM once wiring is complete - they don't persist as memory overhead.
  5. Scope frames are stack-allocated. <for> loop item variables use a linked-list scope chain, not cloned Maps or Objects.

TypeScript - @microsoft/webui-router rules

These apply to packages/webui-router (the client-side SPA router).

Speed

  1. Server does route matching. The client does not re-match routes. The server returns the matched chain array; the client diffs old vs new and mounts only changed components.
  2. Lazy loading via dynamic import. Route component JS is fetched only on first navigation to that route.
  3. Chain diffing, not full remount. Navigating between sibling routes preserves parent components. Only the changed level is remounted.
  4. No for..in on objects. Use Object.keys() with an indexed for loop. for..in walks the prototype chain (slow) and requires an Object.hasOwn guard to be safe — Object.keys is both faster and prototype-safe in one call:
    // ✗ Bad: slow, prototype-unsafe without guard
    for (const key in obj) { ... }
    
    // ✗ Still bad: correct but slower than Object.keys
    for (const key in obj) {
      if (Object.hasOwn(obj, key)) { ... }
    }
    
    // ✓ Good: fast, prototype-safe, no guard needed
    const keys = Object.keys(obj);
    for (let i = 0; i < keys.length; i++) { ... }

Memory

  1. Release unused templates. Router.gc() clears cached component templates for routes the user hasn't visited recently. Active route components are never released.
  2. Inventory bitmask prevents duplicate downloads. The server tracks which component templates the client already has via a bitmask. Re-navigation never re-sends templates.
  3. Minimal state per navigation. Route-scoped state means the JSON partial contains only what the active route needs, not the full app state.

Measuring

Server benchmarks

cargo bench -p microsoft-webui --bench contact_book_bench          # full run
cargo bench -p microsoft-webui --bench contact_book_bench -- --test # quick validation
cargo bench -p microsoft-webui --bench server_request_bench         # high-level request helper
cargo xtask bench all                                               # all Rust crates
cargo xtask bench node-addon                                        # Node/V8/N-API boundary

Compare Render/1000 P50 before and after. For Node changes, save with --save-baseline before and compare with --baseline before. Verify output Bytes is unchanged (same HTML = correct behavior).

Client performance

window.addEventListener('webui:hydration-complete', () => {
  for (const entry of performance.getEntriesByType('measure')) {
    if (entry.name.startsWith('webui:hydrate:')) {
      console.log(`${entry.name}: ${entry.duration.toFixed(1)}ms`);
    }
  }
});

What to report

When making a performance-related change, report:

  • Before/after benchmark numbers (P50 latency, throughput)
  • Allocation count delta if measurable
  • Output size unchanged (proves correctness)
  • Memory profile for memory-related changes (heap snapshots, RSS delta)

Download now (ZIP)

  • Other versions20 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 20

    1. alamenai/terrae★ 245same name
    2. OutThisLife/brooklyn-skills★ 207same name
    3. zeuikli/claude-code-workspace★ 155same name
    4. epicsagas/epic-harness★ 19same name
    5. jmylchreest/aide★ 19same name
    6. QAInsights/perf-skills★ 16same name
    7. fullymiddleaged/Clawness★ 4same name
    8. umararshad4/honey-stack★ 4same name
    9. JustinK33/foreman★ 3same name
    10. wangqiqi/cursor-ai★ 3same name

    10 more not listed

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • Used alongsideProjects with it also use scaffold, test and 10 more.

    Other files people use alongside this one.

    Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
    • MicrosoftSkill

      scaffold

      Also in 3 projects that use this

    • Microsoft AzureSkill

      test

      Also in 6 projects that use this

    • 0xSeroSkill

      security

      Also in 4 projects that use this

    • GitHubSkill

      refactor

      Surgical code refactoring to improve maintainability without changing behavior. Covers extracting functions, renaming variables, breaking down god functions, improving type safety, eliminating code smells, and applying design patterns. Less drastic than repo-rebuilder; use for gradual improvements.

      Also in 6 projects that use this

    • AnthropicSkill

      debug

      Structured debugging session — reproduce, isolate, diagnose, and fix. Trigger with an error message or stack trace, "this works in staging but not prod", "something broke after the deploy", or when behavior diverges from expected and the cause isn't obvious.

      Also in 7 projects that use this

    • Microsoft AzureSkill

      design

      Design rules and conventions for the API Center Portal. Use when editing detail pages, card components, badges, layout, navigation, sidebars, or headers. Covers badge styling, metadata placement, page navigation patterns, and layout alignment.

      Also in 5 projects that use this

    • +1 more from Azure
  • In this repository11 other files it gives its agents.

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.