[TERMINAL · SKILLS]
> mounting /skills...
> indexing skill manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Use Cases/Build a Performance Budget Monitor

Build a Performance Budget Monitor

Build a performance budget system that tracks bundle sizes, Core Web Vitals, and page load metrics — blocking deployments that exceed budgets and alerting on regressions.

Development#typescript#tsconfig#type-checking#type-safety#javascript-migration
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 5 skills

Avg quality 96/100·All SAFE
>

typescript

v1.0.0

Sets up and repairs TypeScript projects: picks tsconfig.json settings for a Node.js service, an npm library or a bundled web app, runs type-checking in CI, and replaces `any` and unsafe casts with types the compiler can verify. Use when someone asks to "set up TypeScript", "fix this tsconfig", "fix a TypeScript error", "upgrade to TypeScript 6 or 7", "migrate JavaScript to TypeScript", "publish types for a package", "run .ts files in Node" or "make this type-safe". Covers the changed defaults in TypeScript 6.0 and 7.0, module and moduleResolution, strict mode, project references, declaration output, narrowing, satisfies and common compiler errors.

93/100 quality
7.30× impact
SAFE· stale score
View skill
>

redis

v1.1.0

Redis is an in-memory data store used as a cache, session store, message broker and counter service. Use when adding a cache layer, storing sessions, rate limiting an API, running pub/sub or Streams with consumer groups, taking a distributed lock, building a leaderboard, or configuring Redis for production (persistence, maxmemory, eviction).

93/100 quality
1.79× impact
SAFE· stale score
View skill
>

postgresql

v1.1.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

100/100 quality
5.15× impact
SAFE· stale score
View skill
>

hono

v1.1.0

Hono is a small web framework built on Web Standards (Request/Response) that runs unchanged on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda and Vercel. Use it when the user asks to build a Hono API, add Zod validation, JWT auth, CORS or middleware, use the type-safe RPC client (hc), render JSX, stream responses, or deploy one codebase to several runtimes.

100/100 quality
6.20× impact
SAFE· stale score
View skill
>

zod

v1.1.0

Zod is a TypeScript-first schema library that validates data at runtime and infers static types from the same schema. Use when validating API request bodies, form input, environment variables or config files, parsing untrusted JSON, or migrating from Zod 3 to Zod 4. Covers z.object, safeParse, z.infer, coercion, discriminated unions, refinements, JSON Schema output and the v4 API changes.

93/100 quality
3.87× impact
SAFE· stale score
View skill
$

The Problem

Emil leads frontend at a 25-person company. The app started at 200KB JavaScript and now ships 1.4MB. Page load went from 1.5s to 6.8s. Nobody noticed because it happened gradually — each PR added "just" 20KB. Core Web Vitals are red in Google Search Console, hurting SEO. They need a performance budget that prevents regressions: if a PR increases bundle size beyond the budget, the CI build fails. If real-user metrics degrade, the team gets alerted.

Step 1: Build the Performance Budget System

typescript
// src/performance/budget.ts — Performance budgets with CI integration and RUM tracking
import { pool } from "../db";
import { Redis } from "ioredis";

const redis = new Redis(process.env.REDIS_URL!);

interface PerformanceBudget {
  metric: string;
  target: number;
  warning: number;             // warn at this threshold
  unit: string;
  category: "size" | "timing" | "score";
}

const BUDGETS: PerformanceBudget[] = [
  // Bundle sizes
  { metric: "js_total", target: 300000, warning: 250000, unit: "bytes", category: "size" },
  { metric: "css_total", target: 100000, warning: 80000, unit: "bytes", category: "size" },
  { metric: "image_total", target: 500000, warning: 400000, unit: "bytes", category: "size" },
  { metric: "total_transfer", target: 1000000, warning: 800000, unit: "bytes", category: "size" },

  // Core Web Vitals
  { metric: "lcp", target: 2500, warning: 2000, unit: "ms", category: "timing" },         // Largest Contentful Paint
  { metric: "fid", target: 100, warning: 50, unit: "ms", category: "timing" },             // First Input Delay
  { metric: "cls", target: 0.1, warning: 0.05, unit: "score", category: "score" },         // Cumulative Layout Shift
  { metric: "ttfb", target: 800, warning: 500, unit: "ms", category: "timing" },           // Time to First Byte
  { metric: "fcp", target: 1800, warning: 1200, unit: "ms", category: "timing" },          // First Contentful Paint
  { metric: "inp", target: 200, warning: 100, unit: "ms", category: "timing" },            // Interaction to Next Paint

  // Other
  { metric: "dom_elements", target: 1500, warning: 1000, unit: "count", category: "score" },
  { metric: "requests", target: 50, warning: 35, unit: "count", category: "score" },
];

interface BudgetCheckResult {
  passed: boolean;
  results: Array<{
    metric: string;
    value: number;
    target: number;
    status: "pass" | "warning" | "fail";
    overBy: number;            // how much over budget (0 if under)
    unit: string;
  }>;
  summary: string;
}

// CI check: validate build against budgets
export async function checkBuildBudget(
  buildMetrics: Record<string, number>,
  commitSha: string,
  branch: string
): Promise<BudgetCheckResult> {
  const results = [];
  let hasFailure = false;
  let hasWarning = false;

  for (const budget of BUDGETS) {
    const value = buildMetrics[budget.metric];
    if (value === undefined) continue;

    let status: "pass" | "warning" | "fail" = "pass";
    if (value > budget.target) {
      status = "fail";
      hasFailure = true;
    } else if (value > budget.warning) {
      status = "warning";
      hasWarning = true;
    }

    results.push({
      metric: budget.metric,
      value,
      target: budget.target,
      status,
      overBy: Math.max(0, value - budget.target),
      unit: budget.unit,
    });
  }

  // Store build metrics for trend tracking
  await pool.query(
    `INSERT INTO build_metrics (commit_sha, branch, metrics, results, passed, created_at)
     VALUES ($1, $2, $3, $4, $5, NOW())`,
    [commitSha, branch, JSON.stringify(buildMetrics), JSON.stringify(results), !hasFailure]
  );

  // Compare with previous build
  const { rows: [previous] } = await pool.query(
    `SELECT metrics FROM build_metrics WHERE branch = 'main' ORDER BY created_at DESC LIMIT 1`
  );

  let regressions: string[] = [];
  if (previous) {
    const prevMetrics = JSON.parse(previous.metrics);
    for (const result of results) {
      const prevValue = prevMetrics[result.metric];
      if (prevValue !== undefined && result.value > prevValue * 1.05) {
        regressions.push(`${result.metric}: ${formatValue(prevValue, result.unit)} → ${formatValue(result.value, result.unit)} (+${Math.round((result.value / prevValue - 1) * 100)}%)`);
      }
    }
  }

  const failedMetrics = results.filter((r) => r.status === "fail");
  const warningMetrics = results.filter((r) => r.status === "warning");

  let summary = hasFailure
    ? `❌ Budget exceeded: ${failedMetrics.map((f) => `${f.metric} (${formatValue(f.value, f.unit)} > ${formatValue(f.target, f.unit)})`).join(", ")}`
    : hasWarning
    ? `⚠️ Approaching budget: ${warningMetrics.map((w) => w.metric).join(", ")}`
    : "✅ All performance budgets passed";

  if (regressions.length > 0) {
    summary += `\n📈 Regressions: ${regressions.join("; ")}`;
  }

  return { passed: !hasFailure, results, summary };
}

// Real User Monitoring (RUM): collect browser metrics
export async function collectRUM(metrics: {
  url: string;
  lcp: number;
  fid: number;
  cls: number;
  ttfb: number;
  fcp: number;
  inp: number;
  connectionType: string;
  deviceType: string;
  country: string;
}): Promise<void> {
  const day = new Date().toISOString().slice(0, 10);

  // Store in Redis for real-time aggregation
  for (const [metric, value] of Object.entries(metrics)) {
    if (typeof value !== "number") continue;
    await redis.rpush(`rum:${metric}:${day}`, String(value));
    await redis.expire(`rum:${metric}:${day}`, 86400 * 30);
  }

  // Batch persist
  await pool.query(
    `INSERT INTO rum_events (url, lcp, fid, cls, ttfb, fcp, inp, connection_type, device_type, country, created_at)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW())`,
    [metrics.url, metrics.lcp, metrics.fid, metrics.cls, metrics.ttfb, metrics.fcp, metrics.inp,
     metrics.connectionType, metrics.deviceType, metrics.country]
  );

  // Check budget violations on real traffic
  await checkRUMBudgets(metrics);
}

async function checkRUMBudgets(metrics: Record<string, any>): Promise<void> {
  for (const budget of BUDGETS.filter((b) => b.category === "timing" || b.category === "score")) {
    const value = metrics[budget.metric];
    if (value === undefined) continue;

    if (value > budget.target * 1.5) {
      // Severe violation — alert
      const alertKey = `rum:alert:${budget.metric}`;
      const alreadyAlerted = await redis.get(alertKey);
      if (!alreadyAlerted) {
        await redis.setex(alertKey, 3600, "1");
        await redis.rpush("notification:queue", JSON.stringify({
          type: "performance_alert",
          metric: budget.metric,
          value, target: budget.target,
          message: `${budget.metric} is ${formatValue(value, budget.unit)} (budget: ${formatValue(budget.target, budget.unit)})`,
        }));
      }
    }
  }
}

// Get RUM percentiles for dashboard
export async function getRUMStats(metric: string, days: number = 7): Promise<{
  p50: number; p75: number; p95: number; p99: number;
  trend: Array<{ date: string; p75: number }>;
  budget: number;
  status: "pass" | "warning" | "fail";
}> {
  const values: number[] = [];
  const trend = [];

  for (let i = 0; i < days; i++) {
    const date = new Date(Date.now() - i * 86400000).toISOString().slice(0, 10);
    const dayValues = (await redis.lrange(`rum:${metric}:${date}`, 0, -1)).map(Number).sort((a, b) => a - b);

    if (dayValues.length > 0) {
      values.push(...dayValues);
      trend.push({ date, p75: percentile(dayValues, 75) });
    }
  }

  values.sort((a, b) => a - b);
  const budget = BUDGETS.find((b) => b.metric === metric);
  const p75 = percentile(values, 75);

  return {
    p50: percentile(values, 50),
    p75,
    p95: percentile(values, 95),
    p99: percentile(values, 99),
    trend: trend.reverse(),
    budget: budget?.target || 0,
    status: p75 > (budget?.target || Infinity) ? "fail" : p75 > (budget?.warning || Infinity) ? "warning" : "pass",
  };
}

function percentile(arr: number[], p: number): number {
  if (arr.length === 0) return 0;
  const idx = Math.ceil(arr.length * p / 100) - 1;
  return arr[Math.max(0, idx)];
}

function formatValue(value: number, unit: string): string {
  if (unit === "bytes") return `${(value / 1024).toFixed(0)}KB`;
  if (unit === "ms") return `${value}ms`;
  return String(value);
}

Results

  • Bundle size: 1.4MB → 290KB — budget of 300KB enforced in CI; PRs that exceed it fail; team learned to code-split and lazy-load
  • LCP: 6.8s → 1.9s — Core Web Vitals budgets caught regressions early; each PR is checked against the 2.5s target
  • Google Search Console: red → green — all Core Web Vitals passed; SEO ranking improved; organic traffic up 25%
  • Gradual degradation impossible — "just 20KB" PRs that would push over budget are caught; performance death by a thousand cuts prevented
  • Real-user data validates lab metrics — RUM p75 LCP matches Lighthouse scores; team trusts the numbers because they come from actual users

Related use cases