home / skills / sgcarstrends / sgcarstrends / performance

performance skill

/.claude/skills/performance

This skill helps you optimize application performance across frontend, backend, and database by profiling, tuning, and caching before production.

npx playbooks add skill sgcarstrends/sgcarstrends --skill performance

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
6.1 KB
---
name: performance
description: Optimize application performance - bundle size, API response times, database queries, React rendering, and serverless function performance. Use when investigating slow pages, profiling, load testing, or before production deployments.
allowed-tools: Read, Edit, Bash, Grep, Glob
---

# Performance Skill

## Performance Targets

**Frontend (Web Vitals):** LCP < 2.5s, FID < 100ms, CLS < 0.1, FCP < 1.8s, TTFB < 600ms
**Backend:** Response time < 500ms (p95), cold start < 2s, error rate < 1%
**Database:** Query time < 100ms, cache hit rate > 80%

## Measure Performance

```bash
# Lighthouse audit
npx lighthouse https://sgcarstrends.com --view

# Bundle analysis
cd apps/web && ANALYZE=true pnpm build

# Load test with k6
k6 run --vus 100 --duration 5m load-test.js
```

## Bundle Size Optimization

### Dynamic Imports

```typescript
// ❌ Static import (loads immediately)
import { HeavyComponent } from "./heavy-component";

// ✅ Dynamic import (lazy load)
import dynamic from "next/dynamic";
const HeavyComponent = dynamic(() => import("./heavy-component"), {
  loading: () => <div>Loading...</div>,
  ssr: false,
});
```

### Tree Shaking

```typescript
// ❌ Imports entire library
import _ from "lodash";

// ✅ Import only what you need
import uniq from "lodash/uniq";
```

## React Performance

### Prevent Re-renders

```typescript
// useMemo for expensive calculations
const processed = useMemo(() => expensiveOperation(data), [data]);

// useCallback for stable function references
const handleClick = useCallback(() => doSomething(), []);

// React.memo for pure components
const Child = React.memo(function Child({ name }) {
  return <div>{name}</div>;
});
```

### Virtualize Long Lists

```typescript
import { FixedSizeList } from "react-window";

<FixedSizeList height={600} itemCount={items.length} itemSize={100} width="100%">
  {({ index, style }) => <Item style={style} data={items[index]} />}
</FixedSizeList>
```

## Database Query Optimization

### Add Indexes

```typescript
// packages/database/src/schema/cars.ts
export const cars = pgTable("cars", {
  make: text("make").notNull(),
  month: text("month").notNull(),
}, (table) => ({
  makeIdx: index("cars_make_idx").on(table.make),
}));
```

### Avoid N+1 Queries

```typescript
// ❌ N+1 queries
for (const post of posts) {
  post.author = await db.query.users.findFirst({ where: eq(users.id, post.authorId) });
}

// ✅ Single query with relation
const posts = await db.query.posts.findMany({ with: { author: true } });
```

### Select Only Needed Columns

```typescript
// ❌ Selects all columns
const users = await db.query.users.findMany();

// ✅ Select specific columns
const users = await db.select({ id: users.id, name: users.name }).from(users);
```

### Use Pagination

```typescript
const cars = await db.query.cars.findMany({
  limit: 20,
  offset: (page - 1) * 20,
});
```

## Caching

### Redis Caching

```typescript
import { redis } from "@sgcarstrends/utils";

export async function getCarsWithCache(make: string) {
  const cacheKey = `cars:${make}`;
  const cached = await redis.get(cacheKey);
  if (cached) return JSON.parse(cached as string);

  const cars = await db.query.cars.findMany({ where: eq(cars.make, make) });
  await redis.set(cacheKey, JSON.stringify(cars), { ex: 3600 });
  return cars;
}
```

### Next.js Caching

```typescript
// Revalidate every hour
export const revalidate = 3600;

// Or use fetch with caching
const data = await fetch(url, { next: { revalidate: 3600 } });
```

## Image Optimization

```typescript
import Image from "next/image";

// ✅ Optimized image with priority for above-fold
<Image
  src="/hero.jpg"
  alt="Hero"
  fill
  sizes="(max-width: 768px) 100vw, 50vw"
  priority
/>
```

## Vercel Function Optimization

```typescript
// API route with optimized config
export const config = {
  maxDuration: 60, // seconds
};

// Use edge functions for low-latency endpoints
export const runtime = "edge";
```

## Profiling

### Performance Middleware

```typescript
// apps/api/src/middleware/performance.ts
export const performanceMiddleware = async (c: Context, next: Next) => {
  const start = performance.now();
  await next();
  const duration = performance.now() - start;

  c.header("X-Response-Time", `${Math.round(duration)}ms`);

  if (duration > 1000) {
    log.warn("Slow request", { path: c.req.path, duration: Math.round(duration) });
  }
};
```

### Query Profiling

```typescript
const start = performance.now();
const result = await db.query.cars.findMany();
const duration = performance.now() - start;

if (duration > 100) {
  console.warn(`Slow query: ${duration}ms`);
}
```

### Vercel Analytics

Check Vercel Dashboard → Analytics for:
- Function execution times
- Edge response times
- Web Vitals metrics
- Request volumes and errors

## Load Testing

```javascript
// load-test.js (k6)
import http from "k6/http";
import { check } from "k6";

export const options = {
  stages: [
    { duration: "2m", target: 100 },
    { duration: "5m", target: 100 },
    { duration: "2m", target: 0 },
  ],
  thresholds: {
    http_req_duration: ["p(95)<500"],
    http_req_failed: ["rate<0.01"],
  },
};

export default function() {
  const res = http.get("https://api.sgcarstrends.com/api/v1/cars/makes");
  check(res, { "status 200": (r) => r.status === 200 });
}
```

## Performance Checklist

- [ ] Bundle size < 200KB (initial load)
- [ ] LCP < 2.5s, FID < 100ms, CLS < 0.1
- [ ] API responses < 500ms
- [ ] Database queries < 100ms
- [ ] Images optimized (WebP/AVIF)
- [ ] Code splitting implemented
- [ ] Caching strategy in place
- [ ] Long lists virtualized
- [ ] Compression enabled

## Quick Wins

1. **Compression**: Enable gzip/brotli (60-80% size reduction)
2. **Image Optimization**: Use Next.js Image component
3. **Caching**: Cache expensive operations in Redis
4. **Indexes**: Add indexes to frequently queried columns
5. **Code Splitting**: Lazy load heavy components
6. **Memoization**: Use useMemo/useCallback for expensive operations

## References

- Web Vitals: https://web.dev/vitals
- Next.js Performance: https://nextjs.org/docs/app/building-your-application/optimizing
- k6 Load Testing: https://k6.io/docs

Overview

This skill helps optimize application performance across frontend, backend, database, and serverless functions for the SG Cars Trends monorepo. It focuses on measurable targets, practical profiling, bundle and React performance fixes, caching, and load testing so you can diagnose and fix slow pages before production. The guidance is geared toward TypeScript services, Vercel/AWS Lambda deployment patterns, and Neon/Postgres + Upstash caching.

How this skill works

It inspects common performance bottlenecks and applies targeted fixes: bundle analysis and tree-shaking for frontend size, React rendering optimizations, query profiling and indexing for the database, Redis caching for hot reads, and cold-start and response-time tuning for serverless functions. It uses Lighthouse, k6 load tests, runtime middleware, and simple timing wrappers to measure impact and enforce thresholds.

When to use it

  • Investigating slow page loads or poor Web Vitals
  • Profiling API endpoints or database queries with high p95 latency
  • Before production deploys to validate performance targets
  • During load testing and capacity planning
  • When reducing bundle size or fixing large initial payloads

Best practices

  • Set clear targets: backend p95 <500ms, LCP <2.5s, DB queries <100ms
  • Measure before changing: use Lighthouse, k6, and simple timing logs to baseline
  • Apply incremental optimizations: cache first, then indexes, then query tuning
  • Prefer lazy-loading and code-splitting for heavy UI components; keep initial bundle <200KB
  • Use memoization and virtualization to avoid unnecessary React re-renders
  • Monitor continuously: expose X-Response-Time headers and send function metrics to dashboards

Example use cases

  • Run Lighthouse and analyze bundle output for the web app to prioritize dynamic imports and tree-shaking
  • Add Redis caching around expensive car-makes queries and verify cache hit rate >80%
  • Profile slow API route with middleware to capture slow requests and log paths with duration
  • Add indexes on frequently filtered columns (e.g., cars.make, cars.month) and replace N+1 loops with relation queries
  • Run k6 scenario to validate p95 response times under 100 concurrent users before a release

FAQ

What quick changes give the biggest impact?

Enable gzip/brotli, optimize above-the-fold images, add Redis caching for expensive reads, and add indexes for frequent filters.

How do I find slow DB queries?

Wrap queries with simple timers in code or use the DB engine profiler; flag queries >100ms and inspect execution plans to add indexes or reduce selected columns.