Code Review for Web
Review and debug web application code with a focus on the patterns that actually break production. Stack-agnostic principles in SKILL.md. Stack-specific patterns in references.
When to use
- Reviewing a pull request before merging
- Debugging a production issue
- Investigating a build failure
- Auditing security or performance of existing code
- Investigating environment variable or configuration issues
- Triaging a "the site is broken" report
When NOT to use
- Writing a new feature spec (use
pm-spec-writing)
- Pre-launch QA against the running site (use
qa-testing)
- Performance deep-dive on Core Web Vitals (use
performance-optimization)
- Deep accessibility compliance review (use
accessibility-audit)
Required inputs
- The code, PR, error message, or symptom under review
- Access to logs (build logs, function logs, server logs) if debugging
- The stack (framework, hosting, database) - even at high level
If just a symptom is provided ("the site is broken"), the workflow's first step is gathering enough context to investigate.
The framework: 5 review dimensions
Every code review covers five dimensions. Pick the depth based on the situation.
1. Correctness
Does the code do what it claims to do?
- Logic matches the intent stated in the spec or PR description
- Edge cases handled (empty states, error states, network failures)
- Off-by-one errors, null/undefined handling, async race conditions
- Tests exist for the change, or there's a reason they don't
- The change does not break existing functionality (regression risk)
2. Security
Does the code expose anything sensitive or open an attack surface?
- Secrets handling. No secrets, API keys, or service-role credentials in client-side code or version control
- Auth checks. Every mutation endpoint validates the caller before acting
- Input validation. User input sanitized before use in queries, file paths, or HTML
- External requests. Outbound URLs validated; no SSRF on user-controlled inputs
- CSRF protection. State-changing requests require a token or same-origin policy
- Rate limiting. Public-facing mutation endpoints have rate limits
- HTTPS-only. No HTTP in production code paths
- Cookies. Session cookies have
Secure, HttpOnly, SameSite attributes set
- Environment variables. Server-only secrets are not prefixed with anything that exposes them to the client bundle
3. Performance
Will this code scale and stay fast?
- Database queries. No N+1 patterns. Joins or batch fetches preferred over loops with queries.
- Pagination. Large result sets paginated, never loaded entirely.
- Caching. Appropriate cache strategy for the data freshness needs.
- Bundle size. Client-side dependencies justified. Tree-shaking working.
- Image handling. Modern formats, lazy loading, explicit dimensions.
- Background work. Slow operations moved off the request path.
- Cold start sensitivity. Cold paths optimized if frequently triggered.
4. Reliability
What happens when this fails?
- Error handling. Caught and handled, not swallowed. Errors logged with context.
- Retries. Network calls have retry logic for transient failures.
- Timeouts. External calls have explicit timeouts (no infinite waits).
- Graceful degradation. Failure of non-critical paths does not crash the page.
- Idempotency. Mutations that might be retried are safe to retry.
- Logging. Enough context in logs to diagnose without reproducing.
5. Maintainability
Will the next person (or future you) understand this in six months?
- Naming. Functions and variables named for what they do, not how.
- Comments. Explain why, not what. The code says what.
- Complexity. Functions do one thing. If a function takes 200 lines, it's doing too much.
- Duplication. Same logic in multiple places gets extracted.
- Dependencies. New dependencies justified. Each one is a maintenance burden.
- Magic values. No literal
60000 in code. Use named constants.
Common bug patterns (stack-agnostic)
Patterns that recur across stacks and are worth checking on every review.
Build and deploy
- Build-time data fetches that timeout. Routes that query a database during static generation can fail at scale. Mark them as runtime-rendered if the data must be fresh.
- Environment variables not propagating. A var that works locally but breaks in production usually means it was not added to the production environment.
- Mismatched env between preview and production. Deploys that work in preview but break on the production domain often have stack-specific URLs hardcoded.
URL and domain issues
- Canonical pointing at staging or preview URL. Caused by client-exposed environment variables that pick up the wrong domain. Canonical domain should come from a server-only environment variable.
- API URL pointing at the main domain that loops back. After a DNS cutover, the main domain may now serve a different application. APIs should live on dedicated subdomains.
- HTTPS upgrade issues. Mixed content (HTTP resources loaded into HTTPS pages) breaks browsers' security model.
Cache invalidation
- Stale content after deploy. Either the cache was not invalidated, or the invalidation requires a manual trigger that did not run.
- CDN serving old asset under same filename. Always use a new filename or cache-bust query string when replacing assets.
- Cache headers too aggressive. Long max-age on resources that change frequently leads to users seeing stale content for hours or days.
Database and data
- N+1 queries. Loop with a query inside the loop. Replace with batch fetch or join.
- Missing limits on table scans. Forgetting
LIMIT on queries that hit large tables.
- Connection pool exhaustion. Too many concurrent connections, often from build-time fetches in parallel routes.
- Schema migration without backfill. Adding a NOT NULL column without populating it for existing rows.
Image handling
- Image not loading after upload. CDN cached the previous filename. Use new filenames for replacements.
- Layout shift from images. Missing width/height attributes. Always specify both.
- Slow LCP from large images. Hero images not optimized for size or format.
External integrations
- Bot mitigation blocking server-to-server calls. CDN or firewall is challenging legitimate automated traffic. Whitelist server IPs or disable challenges for API endpoints.
- API rate limit triggered in production. Worked locally where traffic was tiny. Add backoff and rate limiting awareness.
Security
- Unprotected revalidation or admin endpoints. Always require a secret token.
- PII in URLs. Visible in server logs, browser history, referrer headers.
- Secrets exposed in client bundle. Anything that gets sent to the browser is public.
Workflow
- Gather context. What stack? What's broken or under review? Logs available?
- Pick the depth. Quick scan for a small PR. Full review for a major change. Deep dive for a production incident.
- Run through the 5 dimensions. Note issues by severity (blocker, important, minor).
- Check stack-specific patterns. Reference the appropriate stack guide.
- For incidents: identify the smallest hypothesis-driven fix. Reproduce locally if possible.
- Write the review. Use the template in
references/review-template.md for formal reviews.
Failure patterns
- "Looks good to me" on a 500-line PR. If the review takes 5 minutes on a large PR, the review didn't happen.
- Reviewing without running the code. Some bugs only surface at runtime. Pull the branch and run it.
- Over-indexing on style. Bikeshedding on formatting while missing logic bugs.
- Skipping security review on "internal" features. Internal becomes external faster than expected.
- Treating warnings as decoration. Build warnings often become production errors after a dependency update.
- Debugging without reading the full error message. First line of the stack trace is often not the actual cause. Read all of it.
Debugging workflow
When a production issue is reported:
- Read the full error message. Including the stack trace.
- Check hosting build and function logs. The exact failing line is usually here.
- Identify the last working version.
git log --oneline and check recent commits.
- Reproduce locally. Confirms it's a code issue and not an environment issue.
- Check environment variables. Especially after deploys or DNS changes.
- Check cache state. Force a cache invalidation before concluding it's a code bug.
- Make the minimal fix. Big refactors during incidents create more incidents.
- Verify in production. Check the actual fix worked, not just that the deploy succeeded.
- Document. What was the root cause? What would have prevented it? File the learnings.
Output format
For PR reviews: comments inline on the PR, plus a summary if needed.
For formal code reviews: a markdown document at code-review-[date].md with:
- Scope (what was reviewed)
- Summary (overall assessment)
- Critical issues (blockers)
- Important issues
- Minor issues
- Suggestions for follow-up
For incidents: a postmortem document. See after-action-report for that format.
Reference files
references/review-template.md - Markdown template for formal code reviews.
references/nextjs-patterns.md - Stack-specific patterns for Next.js (App Router, ISR, Server Components, common bugs).
references/wordpress-headless-patterns.md - Stack-specific patterns for headless WordPress integrations.
1---2name: code-review-web3description: Review web application code for bugs, security issues, performance problems, and stack-specific anti-patterns. Use this skill whenever the user wants to review code, debug a production issue, investigate a build failure, audit security, or check a PR before merging. Triggers on code review, review my code, debug, build error, broken, not working, why is X failing, check this code, security check, PR review, audit code, refactor. Also triggers when investigating 4xx or 5xx errors, deploy failures, environment variable issues, and CMS integration problems.4---5
6# Code Review for Web
7
8Review and debug web application code with a focus on the patterns that actually break production. Stack-agnostic principles in SKILL.md. Stack-specific patterns in references.
9
10---
11
12## When to use
13
14- Reviewing a pull request before merging
15- Debugging a production issue
16- Investigating a build failure
17- Auditing security or performance of existing code
18- Investigating environment variable or configuration issues
19- Triaging a "the site is broken" report
20
21## When NOT to use
22
23- Writing a new feature spec (use `pm-spec-writing`)
24- Pre-launch QA against the running site (use `qa-testing`)
25- Performance deep-dive on Core Web Vitals (use `performance-optimization`)
26- Deep accessibility compliance review (use `accessibility-audit`)
27
28---
29
30## Required inputs
31
32- The code, PR, error message, or symptom under review
33- Access to logs (build logs, function logs, server logs) if debugging
34- The stack (framework, hosting, database) - even at high level
35
36If just a symptom is provided ("the site is broken"), the workflow's first step is gathering enough context to investigate.
37
38---
39
40## The framework: 5 review dimensions
41
42Every code review covers five dimensions. Pick the depth based on the situation.
43
44### 1. Correctness
45
46Does the code do what it claims to do?
47
48- Logic matches the intent stated in the spec or PR description
49- Edge cases handled (empty states, error states, network failures)
50- Off-by-one errors, null/undefined handling, async race conditions
51- Tests exist for the change, or there's a reason they don't
52- The change does not break existing functionality (regression risk)
53
54### 2. Security
55
56Does the code expose anything sensitive or open an attack surface?
57
58- **Secrets handling.** No secrets, API keys, or service-role credentials in client-side code or version control
59- **Auth checks.** Every mutation endpoint validates the caller before acting
60- **Input validation.** User input sanitized before use in queries, file paths, or HTML
61- **External requests.** Outbound URLs validated; no SSRF on user-controlled inputs
62- **CSRF protection.** State-changing requests require a token or same-origin policy
63- **Rate limiting.** Public-facing mutation endpoints have rate limits
64- **HTTPS-only.** No HTTP in production code paths
65- **Cookies.** Session cookies have `Secure`, `HttpOnly`, `SameSite` attributes set
66- **Environment variables.** Server-only secrets are not prefixed with anything that exposes them to the client bundle
67
68### 3. Performance
69
70Will this code scale and stay fast?
71
72- **Database queries.** No N+1 patterns. Joins or batch fetches preferred over loops with queries.
73- **Pagination.** Large result sets paginated, never loaded entirely.
74- **Caching.** Appropriate cache strategy for the data freshness needs.
75- **Bundle size.** Client-side dependencies justified. Tree-shaking working.
76- **Image handling.** Modern formats, lazy loading, explicit dimensions.
77- **Background work.** Slow operations moved off the request path.
78- **Cold start sensitivity.** Cold paths optimized if frequently triggered.
79
80### 4. Reliability
81
82What happens when this fails?
83
84- **Error handling.** Caught and handled, not swallowed. Errors logged with context.
85- **Retries.** Network calls have retry logic for transient failures.
86- **Timeouts.** External calls have explicit timeouts (no infinite waits).
87- **Graceful degradation.** Failure of non-critical paths does not crash the page.
88- **Idempotency.** Mutations that might be retried are safe to retry.
89- **Logging.** Enough context in logs to diagnose without reproducing.
90
91### 5. Maintainability
92
93Will the next person (or future you) understand this in six months?
94
95- **Naming.** Functions and variables named for what they do, not how.
96- **Comments.** Explain why, not what. The code says what.
97- **Complexity.** Functions do one thing. If a function takes 200 lines, it's doing too much.
98- **Duplication.** Same logic in multiple places gets extracted.
99- **Dependencies.** New dependencies justified. Each one is a maintenance burden.
100- **Magic values.** No literal `60000` in code. Use named constants.
101
102---
103
104## Common bug patterns (stack-agnostic)
105
106Patterns that recur across stacks and are worth checking on every review.
107
108### Build and deploy
109
110- **Build-time data fetches that timeout.** Routes that query a database during static generation can fail at scale. Mark them as runtime-rendered if the data must be fresh.
111- **Environment variables not propagating.** A var that works locally but breaks in production usually means it was not added to the production environment.
112- **Mismatched env between preview and production.** Deploys that work in preview but break on the production domain often have stack-specific URLs hardcoded.
113
114### URL and domain issues
115
116- **Canonical pointing at staging or preview URL.** Caused by client-exposed environment variables that pick up the wrong domain. Canonical domain should come from a server-only environment variable.
117- **API URL pointing at the main domain that loops back.** After a DNS cutover, the main domain may now serve a different application. APIs should live on dedicated subdomains.
118- **HTTPS upgrade issues.** Mixed content (HTTP resources loaded into HTTPS pages) breaks browsers' security model.
119
120### Cache invalidation
121
122- **Stale content after deploy.** Either the cache was not invalidated, or the invalidation requires a manual trigger that did not run.
123- **CDN serving old asset under same filename.** Always use a new filename or cache-bust query string when replacing assets.
124- **Cache headers too aggressive.** Long max-age on resources that change frequently leads to users seeing stale content for hours or days.
125
126### Database and data
127
128- **N+1 queries.** Loop with a query inside the loop. Replace with batch fetch or join.
129- **Missing limits on table scans.** Forgetting `LIMIT` on queries that hit large tables.
130- **Connection pool exhaustion.** Too many concurrent connections, often from build-time fetches in parallel routes.
131- **Schema migration without backfill.** Adding a NOT NULL column without populating it for existing rows.
132
133### Image handling
134
135- **Image not loading after upload.** CDN cached the previous filename. Use new filenames for replacements.
136- **Layout shift from images.** Missing width/height attributes. Always specify both.
137- **Slow LCP from large images.** Hero images not optimized for size or format.
138
139### External integrations
140
141- **Bot mitigation blocking server-to-server calls.** CDN or firewall is challenging legitimate automated traffic. Whitelist server IPs or disable challenges for API endpoints.
142- **API rate limit triggered in production.** Worked locally where traffic was tiny. Add backoff and rate limiting awareness.
143
144### Security
145
146- **Unprotected revalidation or admin endpoints.** Always require a secret token.
147- **PII in URLs.** Visible in server logs, browser history, referrer headers.
148- **Secrets exposed in client bundle.** Anything that gets sent to the browser is public.
149
150---
151
152## Workflow
153
1541. **Gather context.** What stack? What's broken or under review? Logs available?
1552. **Pick the depth.** Quick scan for a small PR. Full review for a major change. Deep dive for a production incident.
1563. **Run through the 5 dimensions.** Note issues by severity (blocker, important, minor).
1574. **Check stack-specific patterns.** Reference the appropriate stack guide.
1585. **For incidents:** identify the smallest hypothesis-driven fix. Reproduce locally if possible.
1596. **Write the review.** Use the template in [`references/review-template.md`](references/review-template.md) for formal reviews.
160
161---
162
163## Failure patterns
164
165- **"Looks good to me" on a 500-line PR.** If the review takes 5 minutes on a large PR, the review didn't happen.
166- **Reviewing without running the code.** Some bugs only surface at runtime. Pull the branch and run it.
167- **Over-indexing on style.** Bikeshedding on formatting while missing logic bugs.
168- **Skipping security review on "internal" features.** Internal becomes external faster than expected.
169- **Treating warnings as decoration.** Build warnings often become production errors after a dependency update.
170- **Debugging without reading the full error message.** First line of the stack trace is often not the actual cause. Read all of it.
171
172---
173
174## Debugging workflow
175
176When a production issue is reported:
177
1781. **Read the full error message.** Including the stack trace.
1792. **Check hosting build and function logs.** The exact failing line is usually here.
1803. **Identify the last working version.** `git log --oneline` and check recent commits.
1814. **Reproduce locally.** Confirms it's a code issue and not an environment issue.
1825. **Check environment variables.** Especially after deploys or DNS changes.
1836. **Check cache state.** Force a cache invalidation before concluding it's a code bug.
1847. **Make the minimal fix.** Big refactors during incidents create more incidents.
1858. **Verify in production.** Check the actual fix worked, not just that the deploy succeeded.
1869. **Document.** What was the root cause? What would have prevented it? File the learnings.
187
188---
189
190## Output format
191
192For PR reviews: comments inline on the PR, plus a summary if needed.
193
194For formal code reviews: a markdown document at `code-review-[date].md` with:
1951. Scope (what was reviewed)
1962. Summary (overall assessment)
1973. Critical issues (blockers)
1984. Important issues
1995. Minor issues
2006. Suggestions for follow-up
201
202For incidents: a postmortem document. See `after-action-report` for that format.
203
204---
205
206## Reference files
207
208- [`references/review-template.md`](references/review-template.md) - Markdown template for formal code reviews.
209- [`references/nextjs-patterns.md`](references/nextjs-patterns.md) - Stack-specific patterns for Next.js (App Router, ISR, Server Components, common bugs).
210- [`references/wordpress-headless-patterns.md`](references/wordpress-headless-patterns.md) - Stack-specific patterns for headless WordPress integrations.