Source profileQuality 88/100Review permissions

vercel/next.js/.agents/skills/v8-jit/SKILL.md

v8-jit

V8 JIT optimization patterns for writing high-performance JavaScript in Next.js server internals. Use when writing or reviewing hot-path code in app-render, stream-utils, routing, caching, or any per-request code path. Covers hidden classes / shapes, monomorphic call sites, inline caches, megamorphic deopt, closure allocation, array packing, and profiling with --trace-opt / --trace-deopt.

Source repository stars
141,364
Declared platforms
0
Static risk flags
1
Last source update
2026-08-05
Source checked
2026-08-05

Decision brief

What it does—and where it fits

Use this skill when writing or optimizing performance-critical code paths in Next.js server internals — especially per-request hot paths like rendering, streaming, routing, and caching.

Best for

  • Use when writing or reviewing hot-path code in app-render, stream-utils, routing, caching, or any per-request code path.

Not for

  • Tasks that require unconfirmed production actions or broad system permissions.
  • Environments where the pinned source and install steps cannot be inspected.

Compatibility matrix

Platform support, with evidence labels

PlatformStatusEvidenceWhat to check
CodexNot declaredNo explicit evidencePortability before use
Claude CodeNot declaredNo explicit evidencePortability before use
CursorNot declaredNo explicit evidencePortability before use
Gemini CLINot declaredNo explicit evidencePortability before use
Open the compatibility checker

Installation

Inspect first. Install second.

The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

Source-detected install commandSource
npx skills add https://github.com/vercel/next.js --skill ".agents/skills/v8-jit"
Safe inspection promptEditorial

Inspect the Agent Skill "v8-jit" from https://github.com/vercel/next.js/blob/bcea67d4e96fbaf8c51f50951e497724d2a02cfb/.agents/skills/v8-jit/SKILL.md at commit bcea67d4e96fbaf8c51f50951e497724d2a02cfb. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

Workflow

What the source asks the agent to do

  1. 01

    Profiling and Verification

    Review the “Profiling and Verification” section in the pinned source before continuing.

    Review and apply the “Profiling and Verification” source section.
  2. 02

    Background: V8's Tiered Compilation

    V8 compiles JavaScript through multiple tiers:

    Ignition (interpreter) — executes bytecode immediately.Sparkplug — fast baseline compiler (no optimization).Maglev — mid-tier optimizing compiler.
  3. 03

    Hidden Classes (Shapes / Maps)

    Every JavaScript object has an internal "hidden class" (V8 calls it a Map, the spec calls it a Shape). Objects that share the same property names, added in the same order, share the same hidden class. This enables fast property access via inline caches.

    Assign every property in the constructor, in the same order, for everyPrefer factory functions when constructing hot-path objects. A single factoryNever delete a property on a hot object — it forces a transition to
  4. 04

    Initialize All Properties in Constructors

    Assign every property in the constructor, in the same order, for every

    Assign every property in the constructor, in the same order, for everyPrefer factory functions when constructing hot-path objects. A single factoryNever delete a property on a hot object — it forces a transition to
  5. 05

    Real Codebase Example

    Span in src/trace/trace.ts initializes all fields in the constructor in a fixed order — name, parentId, attrs, status, id, start, now. This ensures all Span instances share one hidden class.

    Span in src/trace/trace.ts initializes all fields in the constructor in a fixed order — name, parentId, attrs, status, id, start, now. This ensures all Span instances share one hidden class.

Permission review

Static risk signals and limitations

Runs scripts

medium · line 357

The documentation asks the agent to run terminal commands or scripts.

node --trace-opt server.js 2>&1 | grep "my-function-name"

Runs scripts

medium · line 360

The documentation asks the agent to run terminal commands or scripts.

node --trace-deopt server.js 2>&1 | grep "my-function-name"

Evidence record

Why each signal appears

EvidenceSourceComputedTestedEditorial
SignalValueEvidence typeMeaning
Quality score88/100ComputedDocumentation, specificity, maintenance, and trust rules
Repository stars141,364SourceRepository attention, not individual Skill quality
Compatibility0 platformsSourceDeclared in the catalog source record
Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

Pinned source

Provenance and original SKILL.md

Repository
vercel/next.js
Skill path
.agents/skills/v8-jit/SKILL.md
Commit
bcea67d4e96fbaf8c51f50951e497724d2a02cfb
License
MIT
Collected
2026-08-05
Default branch
canary
View the original SKILL.md

V8 JIT Optimization

Use this skill when writing or optimizing performance-critical code paths in Next.js server internals — especially per-request hot paths like rendering, streaming, routing, and caching.

Background: V8's Tiered Compilation

V8 compiles JavaScript through multiple tiers:

  1. Ignition (interpreter) — executes bytecode immediately.
  2. Sparkplug — fast baseline compiler (no optimization).
  3. Maglev — mid-tier optimizing compiler.
  4. Turbofan — full optimizing compiler (speculative, type-feedback-driven).

Code starts in Ignition and is promoted to higher tiers based on execution frequency and collected type feedback. Turbofan produces the fastest machine code but bails out (deopts) when assumptions are violated at runtime.

The key principle: help V8 make correct speculative assumptions by keeping types, shapes, and control flow predictable.

Hidden Classes (Shapes / Maps)

Every JavaScript object has an internal "hidden class" (V8 calls it a Map, the spec calls it a Shape). Objects that share the same property names, added in the same order, share the same hidden class. This enables fast property access via inline caches.

Initialize All Properties in Constructors

// GOOD — consistent shape, single hidden class transition chain
class RequestContext {
  url: string
  method: string
  headers: Record<string, string>
  startTime: number
  cached: boolean

  constructor(url: string, method: string, headers: Record<string, string>) {
    this.url = url
    this.method = method
    this.headers = headers
    this.startTime = performance.now()
    this.cached = false // always initialize, even defaults
  }
}
// BAD — conditional property addition creates multiple hidden classes
class RequestContext {
  constructor(url, method, headers, options) {
    this.url = url
    this.method = method
    if (options.timing) {
      this.startTime = performance.now() // shape fork!
    }
    if (options.cache) {
      this.cached = false // another shape fork!
    }
    this.headers = headers
  }
}

Rules:

  • Assign every property in the constructor, in the same order, for every instance. Use null / undefined / false as default values rather than omitting the property.
  • Prefer factory functions when constructing hot-path objects. A single factory makes it harder to accidentally fork shapes in different call sites.
  • Never delete a property on a hot object — it forces a transition to dictionary mode (slow properties).
  • Avoid adding properties after construction (obj.newProp = x) on objects used in hot paths.
  • Object literals that flow into the same function should have keys in the same order:
  • Use tuples for very small fixed-size records when names are not needed. Tuples avoid key-order pitfalls entirely.
// GOOD — same key order, shares hidden class
const a = { type: 'static', value: 1 }
const b = { type: 'dynamic', value: 2 }

// BAD — different key order, different hidden classes
const a = { type: 'static', value: 1 }
const b = { value: 2, type: 'dynamic' }

Real Codebase Example

Span in src/trace/trace.ts initializes all fields in the constructor in a fixed order — name, parentId, attrs, status, id, _start, now. This ensures all Span instances share one hidden class.

Monomorphic vs Polymorphic vs Megamorphic

V8's inline caches (ICs) track the types/shapes seen at each call site or property access:

IC StateShapes SeenSpeed
Monomorphic1Fastest — single direct check
Polymorphic2–4Fast — linear search through cases
Megamorphic5+Slow — hash-table lookup, no inlining

Once an IC goes megamorphic it does NOT recover (until the function is re-compiled). Megamorphic ICs also prevent Turbofan from inlining the function.

Keep Hot Call Sites Monomorphic

// GOOD — always called with the same argument shape
function processChunk(chunk: Uint8Array): void {
  // chunk is always Uint8Array → monomorphic
}

// BAD — called with different types at the same call site
function processChunk(chunk: Uint8Array | Buffer | string): void {
  // IC becomes polymorphic/megamorphic
}

Practical strategies:

  • Normalize inputs at the boundary (e.g. convert BufferUint8Array once) and keep internal functions monomorphic.
  • Avoid passing both null and undefined for the same parameter — pick one sentinel value.
  • When a function must handle multiple types, split into separate specialized functions and dispatch once at the entry point:
// Entry point dispatches once
function handleStream(stream: ReadableStream | Readable) {
  if (stream instanceof ReadableStream) {
    return handleWebStream(stream) // monomorphic call
  }
  return handleNodeStream(stream) // monomorphic call
}

This is the pattern used in stream-ops.ts and throughout the stream-utils code (Node.js vs Web stream split via compile-time switcher).

Closure and Allocation Pressure

Every closure captures its enclosing scope. Creating closures in hot loops or per-request paths generates GC pressure and can prevent escape analysis.

Hoist Closures Out of Hot Paths

// BAD — closure allocated for every request
function handleRequest(req) {
  stream.on('data', (chunk) => processChunk(chunk, req.id))
}

// GOOD — shared listener, request context looked up by stream
const requestIdByStream = new WeakMap()
function onData(chunk) {
  const id = requestIdByStream.get(this)
  if (id !== undefined) processChunk(chunk, id)
}

function processChunk(chunk, id) {
  /* ... */
}

function handleRequest(req) {
  requestIdByStream.set(stream, req.id)
  stream.on('data', onData)
}
// BEST — pre-allocate the callback as a method on a context object
class StreamProcessor {
  id: string
  constructor(id: string) {
    this.id = id
  }
  handleChunk(chunk: Uint8Array) {
    processChunk(chunk, this.id)
  }
}

Avoid Allocations in Tight Loops

// BAD — allocates a new object per iteration
for (const item of items) {
  doSomething({ key: item.key, value: item.value })
}

// GOOD — reuse a mutable scratch object
const scratch = { key: '', value: '' }
for (const item of items) {
  scratch.key = item.key
  scratch.value = item.value
  doSomething(scratch)
}

Real Codebase Example

node-stream-helpers.ts hoists encoder, BUFFER_TAGS, and tag constants to module scope to avoid re-creating them on every request. The bufferIndexOf helper uses Buffer.indexOf (C++ native) instead of a per-call JS loop, eliminating per-chunk allocation.

Array Optimizations

V8 tracks array "element kinds" — an internal type tag that determines how elements are stored in memory:

Element KindDescriptionSpeed
PACKED_SMISmall integers only, no holesFastest
PACKED_DOUBLENumbers only, no holesFast
PACKED_ELEMENTSMixed/objects, no holesModerate
HOLEY_*Any of above with holesSlower (extra bounds check)

Transitions are one-way — once an array becomes HOLEY or PACKED_ELEMENTS, it never goes back.

Rules

  • Pre-allocate arrays with known size: new Array(n) creates a holey array. Prefer [] and push(), or use Array.from({ length: n }, initFn).
  • Don't create holes: arr[100] = x on an empty array creates 100 holes.
  • Don't mix types: [1, 'two', {}] immediately becomes PACKED_ELEMENTS.
  • Prefer typed arrays only when you need binary interop/contiguous memory or have profiling evidence that they help. For small/short-lived collections, normal arrays can be faster and allocate less.
// GOOD — packed SMI array
const indices: number[] = []
for (let i = 0; i < n; i++) {
  indices.push(i)
}

// BAD — holey from the start
const indices = new Array(n)
for (let i = 0; i < n; i++) {
  indices[i] = i
}

Real Codebase Example

accumulateStreamChunks in app-render.tsx uses const staticChunks: Array<Uint8Array> = [] with push() — keeping a packed array of a single type throughout its lifetime.

Function Optimization and Deopts

Hot-Path Deopt Footguns

  • arguments object: using arguments in non-trivial ways (e.g. arguments[i] with variable i, leaking arguments). Use rest params instead.
  • Type instability at one call site: same operation sees both numbers and strings (or many object shapes) and becomes polymorphic/megamorphic.
  • eval / with: prevents optimization entirely.
  • Highly dynamic object iteration: avoid for...in on hot objects; prefer Object.keys() / Object.entries() when possible.

Favor Predictable Control Flow

// GOOD — predictable: always returns same type
function getStatus(code: number): string {
  if (code === 200) return 'ok'
  if (code === 404) return 'not found'
  return 'error'
}

// BAD — returns different types
function getStatus(code: number): string | null | undefined {
  if (code === 200) return 'ok'
  if (code === 404) return null
  // implicitly returns undefined
}

Watch Shape Diversity in switch Dispatch

// WATCH OUT — `node.type` IC can go megamorphic if many shapes hit one site
function render(node) {
  switch (node.type) {
    case 'div':
      return { tag: 'div', children: node.children }
    case 'span':
      return { tag: 'span', text: node.text }
    case 'img':
      return { src: node.src, alt: node.alt }
    // Many distinct node layouts can make this dispatch site polymorphic
  }
}

This pattern is not always bad. Often the main pressure is at the shared dispatch site (node.type), while properties used only in one branch stay monomorphic within that branch. Reach for normalization/splitting only when profiles show this site is hot and polymorphic.

String Operations

  • String concatenation in loops is usually fine in modern V8 (ropes make many concatenations cheap). For binary data, use Buffer.concat().
  • Template literals vs concatenation: equivalent performance in modern V8, but template literals are clearer.
  • string.indexOf() > regex for simple substring checks.
  • Reuse RegExp objects: don't create a new RegExp() inside a hot function — hoist it to module scope.
// GOOD — regex hoisted to module scope
const ROUTE_PATTERN = /^\/api\//

function isApiRoute(path: string): boolean {
  return ROUTE_PATTERN.test(path)
}

// BAD — regex recreated on every call
function isApiRoute(path: string): boolean {
  return /^\/api\//.test(path) // V8 may or may not cache this
}

Map and Set vs Plain Objects

  • Map is faster than plain objects for frequent additions/deletions (avoids hidden class transitions and dictionary mode).
  • Set is faster than obj[key] = true for membership checks with dynamic keys.
  • For static lookups (known keys at module load), plain objects or Object.freeze({...}) are fine — V8 optimizes them as constant.
  • Never use an object as a map if keys come from user input (prototype pollution risk + megamorphic shapes).

Profiling and Verification

V8 Flags for Diagnosing JIT Issues

# Trace which functions get optimized
node --trace-opt server.js 2>&1 | grep "my-function-name"

# Trace deoptimizations (critical for finding perf regressions)
node --trace-deopt server.js 2>&1 | grep "my-function-name"

# Combined: see the full opt/deopt lifecycle
node --trace-opt --trace-deopt server.js 2>&1 | tee /tmp/v8-trace.log

# Show IC state transitions (verbose)
node --trace-ic server.js 2>&1 | tee /tmp/ic-trace.log

# Print optimized code (advanced)
node --print-opt-code --code-comments server.js

Targeted Profiling in Next.js

# Profile a production build
node --cpu-prof --cpu-prof-dir=/tmp/profiles \
  node_modules/.bin/next build

# Profile the server during a benchmark
node --cpu-prof --cpu-prof-dir=/tmp/profiles \
  node_modules/.bin/next start &
# ... run benchmark ...
# Analyze in Chrome DevTools: chrome://inspect → Open dedicated DevTools

# Quick trace-deopt check on a specific test
node --trace-deopt $(which jest) --runInBand test/path/to/test.ts \
  2>&1 | grep -i "deopt" | head -50

Using % Natives (Development/Testing Only)

With --allow-natives-syntax:

function hotFunction(x) {
  return x + 1
}

// Force optimization
%PrepareFunctionForOptimization(hotFunction)
hotFunction(1)
hotFunction(2) % OptimizeFunctionOnNextCall(hotFunction)
hotFunction(3)

// Check optimization status
// 1 = optimized, 2 = not optimized, 3 = always optimized, 6 = maglev
console.log(%GetOptimizationStatus(hotFunction))

Checklist for Hot Path Code Reviews

  • All object properties initialized in constructor/literal, same order
  • No delete on hot objects
  • No post-construction property additions on hot objects
  • Functions receive consistent types (monomorphic call sites)
  • Type dispatch happens at boundaries, not deep in hot loops
  • No closures allocated inside tight loops
  • Module-scope constants for regex, encoders, tag buffers
  • Arrays are packed (no holes, no mixed types)
  • Map/Set used for dynamic key collections
  • No arguments object — use rest params
  • try/catch at function boundary, not inside tight loops
  • String building via array + join() or Buffer.concat()
  • Return types are consistent (no string | null | undefined mixes)

Related Skills

  • $dce-edge — DCE-safe require patterns (compile-time dead code)
  • $runtime-debug — runtime bundle debugging and profiling workflow

Alternatives

Compare before choosing

Computed 9382

aAAaqwq/AGI-Super-Team

ui-ux-pro-max

UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 9 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: g

Computed 9123,835

alirezarezvani/claude-skills

code-to-prd

Reverse-engineer any codebase into a complete Product Requirements Document (PRD). Analyzes routes, components, state management, API integrations, and user interactions to produce business-readable documentation detailed enough for engineers or AI agents to fully reconstruct every page and endpoint. Works with frontend frameworks (React, Vue, Angular, Svelte, Next.js, Nuxt), backend frameworks (NestJS, Django, Express, FastAPI), and fullstack applications. Use when users mention: generate PRD,

Computed 9123,835

alirezarezvani/claude-skills

senior-fullstack

Fullstack development toolkit with project scaffolding for Next.js, FastAPI, MERN, and Django stacks, code quality analysis with security and complexity scoring, and stack selection guidance. Use when the user asks to "scaffold a new project", "create a Next.js app", "set up FastAPI with React", "analyze code quality", "audit my codebase", "what stack should I use", "generate project boilerplate", or mentions fullstack development, project setup, or tech stack comparison.

Computed 90141,364

vercel/next.js

sandbox-bench

Benchmark React or Next.js changes on Vercel Sandbox VMs with paired A/B statistics: react PR/commit vs base, or Next.js PR/commit vs base, measured end-to-end through the bench/render-pipeline app (rps, latency, p95; TTFB, RSS and document/Flight bytes when the Next side captures them) and, for React changes, through the react repo's flight-ssr-bench fixture (Node AND Edge web-streams paths, Fizz and Flight+Fizz). Use whenever the user asks to bench, perf test, or A/B a React PR, a react-server