[TERMINAL · SKILLS]
> mounting /skills...
> indexing 295 manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Terminal.skills
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.

#redis#caching#database#pub-sub#queues
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 5 skills

Avg quality 93/100·All SAFE
>

typescript

v

Not yet scored
View skill
>

redis

v1.0.0

Build applications with Redis — caching, session storage, pub/sub, streams, rate limiting, leaderboards, and queues. Use when tasks involve in-memory data storage, real-time messaging, distributed locking, or performance optimization with caching layers.

93/100 quality
1.81× impact
SAFE
View skill
>

postgresql

v1.0.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.

87/100 quality
1.53× impact
SAFE
View skill
>

hono

v1.0.0

You are an expert in Hono, the ultrafast web framework for the edge. You help developers build APIs and web applications that run on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda, and Vercel Edge — with a tiny footprint (~14KB), middleware ecosystem, JSX support, RPC client, and Web Standards API compatibility that makes code truly portable across runtimes.

93/100 quality
3.00× impact
SAFE
View skill
>

zod

v1.0.0

You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.

100/100 quality
1.21× impact
SAFE
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