@kurokeita/add-skill 2.6.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -64
- package/dist/bin/cli.js +270 -478
- package/dist/skills/statusline-setup/.claude-plugin/plugin.json +1 -1
- package/dist/skills/statusline-setup/SKILL.md +48 -448
- package/dist/skills/statusline-setup/mod/hooks/register.tsx +93 -103
- package/dist/skills/statusline-setup/mod/tests/work.test.ts +28 -10
- package/dist/skills/statusline-setup/mod/types/index.d.ts +2 -1
- package/dist/skills/universalize-agents/reference/mapping.md +3 -2
- package/package.json +3 -3
- package/dist/agents/backend-specialist.md +0 -277
- package/dist/agents/code-archaeologist.md +0 -114
- package/dist/agents/database-architect.md +0 -238
- package/dist/agents/debugger.md +0 -231
- package/dist/agents/devops-engineer.md +0 -242
- package/dist/agents/documentation-writer.md +0 -104
- package/dist/agents/explorer-agent.md +0 -79
- package/dist/agents/frontend-specialist.md +0 -594
- package/dist/agents/game-developer.md +0 -162
- package/dist/agents/mobile-developer.md +0 -383
- package/dist/agents/orchestrator.md +0 -435
- package/dist/agents/penetration-tester.md +0 -188
- package/dist/agents/performance-optimizer.md +0 -191
- package/dist/agents/product-manager.md +0 -122
- package/dist/agents/product-owner.md +0 -106
- package/dist/agents/project-planner.md +0 -408
- package/dist/agents/qa-automation-engineer.md +0 -109
- package/dist/agents/security-auditor.md +0 -171
- package/dist/agents/test-engineer.md +0 -158
- package/dist/skills/agent-identifier/SKILL.md +0 -431
- package/dist/skills/agent-identifier/examples/agent-creation-prompt.md +0 -247
- package/dist/skills/agent-identifier/examples/complete-agent-examples.md +0 -432
- package/dist/skills/agent-identifier/references/agent-creation-system-prompt.md +0 -215
- package/dist/skills/agent-identifier/references/system-prompt-design.md +0 -419
- package/dist/skills/agent-identifier/references/triggering-examples.md +0 -508
- package/dist/skills/agent-identifier/scripts/validate-agent.sh +0 -217
- package/dist/skills/code-review-excellence/SKILL.md +0 -525
- package/dist/skills/component-refactoring/SKILL.md +0 -492
- package/dist/skills/component-refactoring/references/complexity-patterns.md +0 -493
- package/dist/skills/component-refactoring/references/component-splitting.md +0 -477
- package/dist/skills/component-refactoring/references/hook-extraction.md +0 -317
- package/dist/skills/context7-guidelines/SKILL.md +0 -31
- package/dist/skills/feature-design-assistant/SKILL.md +0 -371
- package/dist/skills/find-bugs/SKILL.md +0 -75
- package/dist/skills/frontend-design/SKILL.md +0 -45
- package/dist/skills/go-concurrency-patterns/SKILL.md +0 -657
- package/dist/skills/grill-me/SKILL.md +0 -9
- package/dist/skills/handoff/SKILL.md +0 -16
- package/dist/skills/koreader-plugin-development/SKILL.md +0 -141
- package/dist/skills/koreader-plugin-development/examples/2-example-patch.lua +0 -17
- package/dist/skills/koreader-plugin-development/examples/_meta.lua +0 -6
- package/dist/skills/koreader-plugin-development/examples/helloworld.lua +0 -36
- package/dist/skills/koreader-plugin-development/references/debugging.md +0 -92
- package/dist/skills/koreader-plugin-development/references/events-and-widgets.md +0 -102
- package/dist/skills/koreader-plugin-development/references/patches.md +0 -118
- package/dist/skills/koreader-plugin-development/references/plugin-anatomy.md +0 -97
- package/dist/skills/make-instruction/SKILL.md +0 -68
- package/dist/skills/make-prompt/SKILL.md +0 -70
- package/dist/skills/make-skill/SKILL.md +0 -147
- package/dist/skills/prd-to-tasks/SKILL.md +0 -164
- package/dist/skills/react-native-async-patterns/SKILL.md +0 -256
- package/dist/skills/react-native-performance/SKILL.md +0 -425
- package/dist/skills/react-native-testing/SKILL.md +0 -458
- package/dist/skills/react-native-zustand-patterns/SKILL.md +0 -343
- package/dist/skills/skill-creator/SKILL.md +0 -385
- package/dist/skills/skill-creator/scripts/init_skill.cjs +0 -235
- package/dist/skills/skill-creator/scripts/package_skill.cjs +0 -84
- package/dist/skills/skill-creator/scripts/validate_skill.cjs +0 -127
- package/dist/skills/skill-development/SKILL.md +0 -676
- package/dist/skills/skill-development/references/skill-creator-original.md +0 -210
- package/dist/skills/sql-optimization-patterns/SKILL.md +0 -509
- package/dist/skills/test-driven-development/SKILL.md +0 -114
- package/dist/skills/test-driven-development/deep-modules.md +0 -33
- package/dist/skills/test-driven-development/interface-design.md +0 -35
- package/dist/skills/test-driven-development/mocking.md +0 -68
- package/dist/skills/test-driven-development/refactoring.md +0 -10
- package/dist/skills/test-driven-development/tests.md +0 -61
- package/dist/skills/vercel-react-best-practices/AGENTS.md +0 -2410
- package/dist/skills/vercel-react-best-practices/SKILL.md +0 -127
- package/dist/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
- package/dist/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -49
- package/dist/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -38
- package/dist/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -80
- package/dist/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -36
- package/dist/skills/vercel-react-best-practices/rules/async-parallel.md +0 -28
- package/dist/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
- package/dist/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -59
- package/dist/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
- package/dist/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
- package/dist/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
- package/dist/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -50
- package/dist/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
- package/dist/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
- package/dist/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
- package/dist/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
- package/dist/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -82
- package/dist/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
- package/dist/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
- package/dist/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
- package/dist/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
- package/dist/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -50
- package/dist/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
- package/dist/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -37
- package/dist/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -50
- package/dist/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
- package/dist/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
- package/dist/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
- package/dist/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -26
- package/dist/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
- package/dist/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
- package/dist/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
- package/dist/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
- package/dist/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
- package/dist/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
- package/dist/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
- package/dist/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
- package/dist/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
- package/dist/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
- package/dist/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
- package/dist/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -44
- package/dist/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
- package/dist/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
- package/dist/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
- package/dist/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -76
- package/dist/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
- package/dist/skills/vercel-react-best-practices/rules/server-serialization.md +0 -38
- package/dist/skills/web-design-guidelines/SKILL.md +0 -40
- package/dist/skills/workflow-patterns/SKILL.md +0 -623
- package/dist/skills/write-prd/SKILL.md +0 -162
- package/dist/workflows/brainstorm.md +0 -113
- package/dist/workflows/create.md +0 -60
- package/dist/workflows/debug.md +0 -107
- package/dist/workflows/deploy.md +0 -178
- package/dist/workflows/enhance.md +0 -64
- package/dist/workflows/github-pull-request-create.md +0 -134
- package/dist/workflows/orchestrate.md +0 -246
- package/dist/workflows/plan.md +0 -90
- package/dist/workflows/preview.md +0 -83
- package/dist/workflows/status.md +0 -87
- package/dist/workflows/test.md +0 -144
- package/dist/workflows/ui-ux-pro-max.md +0 -315
|
@@ -1,509 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sql-optimization-patterns
|
|
3
|
-
description: Master SQL query optimization, indexing strategies, and EXPLAIN analysis to dramatically improve database performance and eliminate slow queries. Use when debugging slow queries, designing database schemas, or optimizing application performance.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# SQL Optimization Patterns
|
|
7
|
-
|
|
8
|
-
Transform slow database queries into lightning-fast operations through systematic optimization, proper indexing, and query plan analysis.
|
|
9
|
-
|
|
10
|
-
## When to Use This Skill
|
|
11
|
-
|
|
12
|
-
- Debugging slow-running queries
|
|
13
|
-
- Designing performant database schemas
|
|
14
|
-
- Optimizing application response times
|
|
15
|
-
- Reducing database load and costs
|
|
16
|
-
- Improving scalability for growing datasets
|
|
17
|
-
- Analyzing EXPLAIN query plans
|
|
18
|
-
- Implementing efficient indexes
|
|
19
|
-
- Resolving N+1 query problems
|
|
20
|
-
|
|
21
|
-
## Core Concepts
|
|
22
|
-
|
|
23
|
-
### 1. Query Execution Plans (EXPLAIN)
|
|
24
|
-
|
|
25
|
-
Understanding EXPLAIN output is fundamental to optimization.
|
|
26
|
-
|
|
27
|
-
**PostgreSQL EXPLAIN:**
|
|
28
|
-
|
|
29
|
-
```sql
|
|
30
|
-
-- Basic explain
|
|
31
|
-
EXPLAIN SELECT * FROM users WHERE email = 'user@example.com';
|
|
32
|
-
|
|
33
|
-
-- With actual execution stats
|
|
34
|
-
EXPLAIN ANALYZE
|
|
35
|
-
SELECT * FROM users WHERE email = 'user@example.com';
|
|
36
|
-
|
|
37
|
-
-- Verbose output with more details
|
|
38
|
-
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
|
|
39
|
-
SELECT u.*, o.order_total
|
|
40
|
-
FROM users u
|
|
41
|
-
JOIN orders o ON u.id = o.user_id
|
|
42
|
-
WHERE u.created_at > NOW() - INTERVAL '30 days';
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
**Key Metrics to Watch:**
|
|
46
|
-
|
|
47
|
-
- **Seq Scan**: Full table scan (usually slow for large tables)
|
|
48
|
-
- **Index Scan**: Using index (good)
|
|
49
|
-
- **Index Only Scan**: Using index without touching table (best)
|
|
50
|
-
- **Nested Loop**: Join method (okay for small datasets)
|
|
51
|
-
- **Hash Join**: Join method (good for larger datasets)
|
|
52
|
-
- **Merge Join**: Join method (good for sorted data)
|
|
53
|
-
- **Cost**: Estimated query cost (lower is better)
|
|
54
|
-
- **Rows**: Estimated rows returned
|
|
55
|
-
- **Actual Time**: Real execution time
|
|
56
|
-
|
|
57
|
-
### 2. Index Strategies
|
|
58
|
-
|
|
59
|
-
Indexes are the most powerful optimization tool.
|
|
60
|
-
|
|
61
|
-
**Index Types:**
|
|
62
|
-
|
|
63
|
-
- **B-Tree**: Default, good for equality and range queries
|
|
64
|
-
- **Hash**: Only for equality (=) comparisons
|
|
65
|
-
- **GIN**: Full-text search, array queries, JSONB
|
|
66
|
-
- **GiST**: Geometric data, full-text search
|
|
67
|
-
- **BRIN**: Block Range INdex for very large tables with correlation
|
|
68
|
-
|
|
69
|
-
```sql
|
|
70
|
-
-- Standard B-Tree index
|
|
71
|
-
CREATE INDEX idx_users_email ON users(email);
|
|
72
|
-
|
|
73
|
-
-- Composite index (order matters!)
|
|
74
|
-
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
|
|
75
|
-
|
|
76
|
-
-- Partial index (index subset of rows)
|
|
77
|
-
CREATE INDEX idx_active_users ON users(email)
|
|
78
|
-
WHERE status = 'active';
|
|
79
|
-
|
|
80
|
-
-- Expression index
|
|
81
|
-
CREATE INDEX idx_users_lower_email ON users(LOWER(email));
|
|
82
|
-
|
|
83
|
-
-- Covering index (include additional columns)
|
|
84
|
-
CREATE INDEX idx_users_email_covering ON users(email)
|
|
85
|
-
INCLUDE (name, created_at);
|
|
86
|
-
|
|
87
|
-
-- Full-text search index
|
|
88
|
-
CREATE INDEX idx_posts_search ON posts
|
|
89
|
-
USING GIN(to_tsvector('english', title || ' ' || body));
|
|
90
|
-
|
|
91
|
-
-- JSONB index
|
|
92
|
-
CREATE INDEX idx_metadata ON events USING GIN(metadata);
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### 3. Query Optimization Patterns
|
|
96
|
-
|
|
97
|
-
**Avoid SELECT \*:**
|
|
98
|
-
|
|
99
|
-
```sql
|
|
100
|
-
-- Bad: Fetches unnecessary columns
|
|
101
|
-
SELECT * FROM users WHERE id = 123;
|
|
102
|
-
|
|
103
|
-
-- Good: Fetch only what you need
|
|
104
|
-
SELECT id, email, name FROM users WHERE id = 123;
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
**Use WHERE Clause Efficiently:**
|
|
108
|
-
|
|
109
|
-
```sql
|
|
110
|
-
-- Bad: Function prevents index usage
|
|
111
|
-
SELECT * FROM users WHERE LOWER(email) = 'user@example.com';
|
|
112
|
-
|
|
113
|
-
-- Good: Create functional index or use exact match
|
|
114
|
-
CREATE INDEX idx_users_email_lower ON users(LOWER(email));
|
|
115
|
-
-- Then:
|
|
116
|
-
SELECT * FROM users WHERE LOWER(email) = 'user@example.com';
|
|
117
|
-
|
|
118
|
-
-- Or store normalized data
|
|
119
|
-
SELECT * FROM users WHERE email = 'user@example.com';
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
**Optimize JOINs:**
|
|
123
|
-
|
|
124
|
-
```sql
|
|
125
|
-
-- Bad: Cartesian product then filter
|
|
126
|
-
SELECT u.name, o.total
|
|
127
|
-
FROM users u, orders o
|
|
128
|
-
WHERE u.id = o.user_id AND u.created_at > '2024-01-01';
|
|
129
|
-
|
|
130
|
-
-- Good: Filter before join
|
|
131
|
-
SELECT u.name, o.total
|
|
132
|
-
FROM users u
|
|
133
|
-
JOIN orders o ON u.id = o.user_id
|
|
134
|
-
WHERE u.created_at > '2024-01-01';
|
|
135
|
-
|
|
136
|
-
-- Better: Filter both tables
|
|
137
|
-
SELECT u.name, o.total
|
|
138
|
-
FROM (SELECT * FROM users WHERE created_at > '2024-01-01') u
|
|
139
|
-
JOIN orders o ON u.id = o.user_id;
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Optimization Patterns
|
|
143
|
-
|
|
144
|
-
### Pattern 1: Eliminate N+1 Queries
|
|
145
|
-
|
|
146
|
-
**Problem: N+1 Query Anti-Pattern**
|
|
147
|
-
|
|
148
|
-
```python
|
|
149
|
-
# Bad: Executes N+1 queries
|
|
150
|
-
users = db.query("SELECT * FROM users LIMIT 10")
|
|
151
|
-
for user in users:
|
|
152
|
-
orders = db.query("SELECT * FROM orders WHERE user_id = ?", user.id)
|
|
153
|
-
# Process orders
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
**Solution: Use JOINs or Batch Loading**
|
|
157
|
-
|
|
158
|
-
```sql
|
|
159
|
-
-- Solution 1: JOIN
|
|
160
|
-
SELECT
|
|
161
|
-
u.id, u.name,
|
|
162
|
-
o.id as order_id, o.total
|
|
163
|
-
FROM users u
|
|
164
|
-
LEFT JOIN orders o ON u.id = o.user_id
|
|
165
|
-
WHERE u.id IN (1, 2, 3, 4, 5);
|
|
166
|
-
|
|
167
|
-
-- Solution 2: Batch query
|
|
168
|
-
SELECT * FROM orders
|
|
169
|
-
WHERE user_id IN (1, 2, 3, 4, 5);
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
```python
|
|
173
|
-
# Good: Single query with JOIN or batch load
|
|
174
|
-
# Using JOIN
|
|
175
|
-
results = db.query("""
|
|
176
|
-
SELECT u.id, u.name, o.id as order_id, o.total
|
|
177
|
-
FROM users u
|
|
178
|
-
LEFT JOIN orders o ON u.id = o.user_id
|
|
179
|
-
WHERE u.id IN (1, 2, 3, 4, 5)
|
|
180
|
-
""")
|
|
181
|
-
|
|
182
|
-
# Or batch load
|
|
183
|
-
users = db.query("SELECT * FROM users LIMIT 10")
|
|
184
|
-
user_ids = [u.id for u in users]
|
|
185
|
-
orders = db.query(
|
|
186
|
-
"SELECT * FROM orders WHERE user_id IN (?)",
|
|
187
|
-
user_ids
|
|
188
|
-
)
|
|
189
|
-
# Group orders by user_id
|
|
190
|
-
orders_by_user = {}
|
|
191
|
-
for order in orders:
|
|
192
|
-
orders_by_user.setdefault(order.user_id, []).append(order)
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Pattern 2: Optimize Pagination
|
|
196
|
-
|
|
197
|
-
**Bad: OFFSET on Large Tables**
|
|
198
|
-
|
|
199
|
-
```sql
|
|
200
|
-
-- Slow for large offsets
|
|
201
|
-
SELECT * FROM users
|
|
202
|
-
ORDER BY created_at DESC
|
|
203
|
-
LIMIT 20 OFFSET 100000; -- Very slow!
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
**Good: Cursor-Based Pagination**
|
|
207
|
-
|
|
208
|
-
```sql
|
|
209
|
-
-- Much faster: Use cursor (last seen ID)
|
|
210
|
-
SELECT * FROM users
|
|
211
|
-
WHERE created_at < '2024-01-15 10:30:00' -- Last cursor
|
|
212
|
-
ORDER BY created_at DESC
|
|
213
|
-
LIMIT 20;
|
|
214
|
-
|
|
215
|
-
-- With composite sorting
|
|
216
|
-
SELECT * FROM users
|
|
217
|
-
WHERE (created_at, id) < ('2024-01-15 10:30:00', 12345)
|
|
218
|
-
ORDER BY created_at DESC, id DESC
|
|
219
|
-
LIMIT 20;
|
|
220
|
-
|
|
221
|
-
-- Requires index
|
|
222
|
-
CREATE INDEX idx_users_cursor ON users(created_at DESC, id DESC);
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Pattern 3: Aggregate Efficiently
|
|
226
|
-
|
|
227
|
-
**Optimize COUNT Queries:**
|
|
228
|
-
|
|
229
|
-
```sql
|
|
230
|
-
-- Bad: Counts all rows
|
|
231
|
-
SELECT COUNT(*) FROM orders; -- Slow on large tables
|
|
232
|
-
|
|
233
|
-
-- Good: Use estimates for approximate counts
|
|
234
|
-
SELECT reltuples::bigint AS estimate
|
|
235
|
-
FROM pg_class
|
|
236
|
-
WHERE relname = 'orders';
|
|
237
|
-
|
|
238
|
-
-- Good: Filter before counting
|
|
239
|
-
SELECT COUNT(*) FROM orders
|
|
240
|
-
WHERE created_at > NOW() - INTERVAL '7 days';
|
|
241
|
-
|
|
242
|
-
-- Better: Use index-only scan
|
|
243
|
-
CREATE INDEX idx_orders_created ON orders(created_at);
|
|
244
|
-
SELECT COUNT(*) FROM orders
|
|
245
|
-
WHERE created_at > NOW() - INTERVAL '7 days';
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
**Optimize GROUP BY:**
|
|
249
|
-
|
|
250
|
-
```sql
|
|
251
|
-
-- Bad: Group by then filter
|
|
252
|
-
SELECT user_id, COUNT(*) as order_count
|
|
253
|
-
FROM orders
|
|
254
|
-
GROUP BY user_id
|
|
255
|
-
HAVING COUNT(*) > 10;
|
|
256
|
-
|
|
257
|
-
-- Better: Filter first, then group (if possible)
|
|
258
|
-
SELECT user_id, COUNT(*) as order_count
|
|
259
|
-
FROM orders
|
|
260
|
-
WHERE status = 'completed'
|
|
261
|
-
GROUP BY user_id
|
|
262
|
-
HAVING COUNT(*) > 10;
|
|
263
|
-
|
|
264
|
-
-- Best: Use covering index
|
|
265
|
-
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Pattern 4: Subquery Optimization
|
|
269
|
-
|
|
270
|
-
**Transform Correlated Subqueries:**
|
|
271
|
-
|
|
272
|
-
```sql
|
|
273
|
-
-- Bad: Correlated subquery (runs for each row)
|
|
274
|
-
SELECT u.name, u.email,
|
|
275
|
-
(SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) as order_count
|
|
276
|
-
FROM users u;
|
|
277
|
-
|
|
278
|
-
-- Good: JOIN with aggregation
|
|
279
|
-
SELECT u.name, u.email, COUNT(o.id) as order_count
|
|
280
|
-
FROM users u
|
|
281
|
-
LEFT JOIN orders o ON o.user_id = u.id
|
|
282
|
-
GROUP BY u.id, u.name, u.email;
|
|
283
|
-
|
|
284
|
-
-- Better: Use window functions
|
|
285
|
-
SELECT DISTINCT ON (u.id)
|
|
286
|
-
u.name, u.email,
|
|
287
|
-
COUNT(o.id) OVER (PARTITION BY u.id) as order_count
|
|
288
|
-
FROM users u
|
|
289
|
-
LEFT JOIN orders o ON o.user_id = u.id;
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
**Use CTEs for Clarity:**
|
|
293
|
-
|
|
294
|
-
```sql
|
|
295
|
-
-- Using Common Table Expressions
|
|
296
|
-
WITH recent_users AS (
|
|
297
|
-
SELECT id, name, email
|
|
298
|
-
FROM users
|
|
299
|
-
WHERE created_at > NOW() - INTERVAL '30 days'
|
|
300
|
-
),
|
|
301
|
-
user_order_counts AS (
|
|
302
|
-
SELECT user_id, COUNT(*) as order_count
|
|
303
|
-
FROM orders
|
|
304
|
-
WHERE created_at > NOW() - INTERVAL '30 days'
|
|
305
|
-
GROUP BY user_id
|
|
306
|
-
)
|
|
307
|
-
SELECT ru.name, ru.email, COALESCE(uoc.order_count, 0) as orders
|
|
308
|
-
FROM recent_users ru
|
|
309
|
-
LEFT JOIN user_order_counts uoc ON ru.id = uoc.user_id;
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### Pattern 5: Batch Operations
|
|
313
|
-
|
|
314
|
-
**Batch INSERT:**
|
|
315
|
-
|
|
316
|
-
```sql
|
|
317
|
-
-- Bad: Multiple individual inserts
|
|
318
|
-
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
|
|
319
|
-
INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
|
|
320
|
-
INSERT INTO users (name, email) VALUES ('Carol', 'carol@example.com');
|
|
321
|
-
|
|
322
|
-
-- Good: Batch insert
|
|
323
|
-
INSERT INTO users (name, email) VALUES
|
|
324
|
-
('Alice', 'alice@example.com'),
|
|
325
|
-
('Bob', 'bob@example.com'),
|
|
326
|
-
('Carol', 'carol@example.com');
|
|
327
|
-
|
|
328
|
-
-- Better: Use COPY for bulk inserts (PostgreSQL)
|
|
329
|
-
COPY users (name, email) FROM '/tmp/users.csv' CSV HEADER;
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
**Batch UPDATE:**
|
|
333
|
-
|
|
334
|
-
```sql
|
|
335
|
-
-- Bad: Update in loop
|
|
336
|
-
UPDATE users SET status = 'active' WHERE id = 1;
|
|
337
|
-
UPDATE users SET status = 'active' WHERE id = 2;
|
|
338
|
-
-- ... repeat for many IDs
|
|
339
|
-
|
|
340
|
-
-- Good: Single UPDATE with IN clause
|
|
341
|
-
UPDATE users
|
|
342
|
-
SET status = 'active'
|
|
343
|
-
WHERE id IN (1, 2, 3, 4, 5, ...);
|
|
344
|
-
|
|
345
|
-
-- Better: Use temporary table for large batches
|
|
346
|
-
CREATE TEMP TABLE temp_user_updates (id INT, new_status VARCHAR);
|
|
347
|
-
INSERT INTO temp_user_updates VALUES (1, 'active'), (2, 'active'), ...;
|
|
348
|
-
|
|
349
|
-
UPDATE users u
|
|
350
|
-
SET status = t.new_status
|
|
351
|
-
FROM temp_user_updates t
|
|
352
|
-
WHERE u.id = t.id;
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
## Advanced Techniques
|
|
356
|
-
|
|
357
|
-
### Materialized Views
|
|
358
|
-
|
|
359
|
-
Pre-compute expensive queries.
|
|
360
|
-
|
|
361
|
-
```sql
|
|
362
|
-
-- Create materialized view
|
|
363
|
-
CREATE MATERIALIZED VIEW user_order_summary AS
|
|
364
|
-
SELECT
|
|
365
|
-
u.id,
|
|
366
|
-
u.name,
|
|
367
|
-
COUNT(o.id) as total_orders,
|
|
368
|
-
SUM(o.total) as total_spent,
|
|
369
|
-
MAX(o.created_at) as last_order_date
|
|
370
|
-
FROM users u
|
|
371
|
-
LEFT JOIN orders o ON u.id = o.user_id
|
|
372
|
-
GROUP BY u.id, u.name;
|
|
373
|
-
|
|
374
|
-
-- Add index to materialized view
|
|
375
|
-
CREATE INDEX idx_user_summary_spent ON user_order_summary(total_spent DESC);
|
|
376
|
-
|
|
377
|
-
-- Refresh materialized view
|
|
378
|
-
REFRESH MATERIALIZED VIEW user_order_summary;
|
|
379
|
-
|
|
380
|
-
-- Concurrent refresh (PostgreSQL)
|
|
381
|
-
REFRESH MATERIALIZED VIEW CONCURRENTLY user_order_summary;
|
|
382
|
-
|
|
383
|
-
-- Query materialized view (very fast)
|
|
384
|
-
SELECT * FROM user_order_summary
|
|
385
|
-
WHERE total_spent > 1000
|
|
386
|
-
ORDER BY total_spent DESC;
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### Partitioning
|
|
390
|
-
|
|
391
|
-
Split large tables for better performance.
|
|
392
|
-
|
|
393
|
-
```sql
|
|
394
|
-
-- Range partitioning by date (PostgreSQL)
|
|
395
|
-
CREATE TABLE orders (
|
|
396
|
-
id SERIAL,
|
|
397
|
-
user_id INT,
|
|
398
|
-
total DECIMAL,
|
|
399
|
-
created_at TIMESTAMP
|
|
400
|
-
) PARTITION BY RANGE (created_at);
|
|
401
|
-
|
|
402
|
-
-- Create partitions
|
|
403
|
-
CREATE TABLE orders_2024_q1 PARTITION OF orders
|
|
404
|
-
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
|
|
405
|
-
|
|
406
|
-
CREATE TABLE orders_2024_q2 PARTITION OF orders
|
|
407
|
-
FOR VALUES FROM ('2024-04-01') TO ('2024-07-01');
|
|
408
|
-
|
|
409
|
-
-- Queries automatically use appropriate partition
|
|
410
|
-
SELECT * FROM orders
|
|
411
|
-
WHERE created_at BETWEEN '2024-02-01' AND '2024-02-28';
|
|
412
|
-
-- Only scans orders_2024_q1 partition
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
### Query Hints and Optimization
|
|
416
|
-
|
|
417
|
-
```sql
|
|
418
|
-
-- Force index usage (MySQL)
|
|
419
|
-
SELECT * FROM users
|
|
420
|
-
USE INDEX (idx_users_email)
|
|
421
|
-
WHERE email = 'user@example.com';
|
|
422
|
-
|
|
423
|
-
-- Parallel query (PostgreSQL)
|
|
424
|
-
SET max_parallel_workers_per_gather = 4;
|
|
425
|
-
SELECT * FROM large_table WHERE condition;
|
|
426
|
-
|
|
427
|
-
-- Join hints (PostgreSQL)
|
|
428
|
-
SET enable_nestloop = OFF; -- Force hash or merge join
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
## Best Practices
|
|
432
|
-
|
|
433
|
-
1. **Index Selectively**: Too many indexes slow down writes
|
|
434
|
-
2. **Monitor Query Performance**: Use slow query logs
|
|
435
|
-
3. **Keep Statistics Updated**: Run ANALYZE regularly
|
|
436
|
-
4. **Use Appropriate Data Types**: Smaller types = better performance
|
|
437
|
-
5. **Normalize Thoughtfully**: Balance normalization vs performance
|
|
438
|
-
6. **Cache Frequently Accessed Data**: Use application-level caching
|
|
439
|
-
7. **Connection Pooling**: Reuse database connections
|
|
440
|
-
8. **Regular Maintenance**: VACUUM, ANALYZE, rebuild indexes
|
|
441
|
-
|
|
442
|
-
```sql
|
|
443
|
-
-- Update statistics
|
|
444
|
-
ANALYZE users;
|
|
445
|
-
ANALYZE VERBOSE orders;
|
|
446
|
-
|
|
447
|
-
-- Vacuum (PostgreSQL)
|
|
448
|
-
VACUUM ANALYZE users;
|
|
449
|
-
VACUUM FULL users; -- Reclaim space (locks table)
|
|
450
|
-
|
|
451
|
-
-- Reindex
|
|
452
|
-
REINDEX INDEX idx_users_email;
|
|
453
|
-
REINDEX TABLE users;
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
## Common Pitfalls
|
|
457
|
-
|
|
458
|
-
- **Over-Indexing**: Each index slows down INSERT/UPDATE/DELETE
|
|
459
|
-
- **Unused Indexes**: Waste space and slow writes
|
|
460
|
-
- **Missing Indexes**: Slow queries, full table scans
|
|
461
|
-
- **Implicit Type Conversion**: Prevents index usage
|
|
462
|
-
- **OR Conditions**: Can't use indexes efficiently
|
|
463
|
-
- **LIKE with Leading Wildcard**: `LIKE '%abc'` can't use index
|
|
464
|
-
- **Function in WHERE**: Prevents index usage unless functional index exists
|
|
465
|
-
|
|
466
|
-
## Monitoring Queries
|
|
467
|
-
|
|
468
|
-
```sql
|
|
469
|
-
-- Find slow queries (PostgreSQL)
|
|
470
|
-
SELECT query, calls, total_time, mean_time
|
|
471
|
-
FROM pg_stat_statements
|
|
472
|
-
ORDER BY mean_time DESC
|
|
473
|
-
LIMIT 10;
|
|
474
|
-
|
|
475
|
-
-- Find missing indexes (PostgreSQL)
|
|
476
|
-
SELECT
|
|
477
|
-
schemaname,
|
|
478
|
-
tablename,
|
|
479
|
-
seq_scan,
|
|
480
|
-
seq_tup_read,
|
|
481
|
-
idx_scan,
|
|
482
|
-
seq_tup_read / seq_scan AS avg_seq_tup_read
|
|
483
|
-
FROM pg_stat_user_tables
|
|
484
|
-
WHERE seq_scan > 0
|
|
485
|
-
ORDER BY seq_tup_read DESC
|
|
486
|
-
LIMIT 10;
|
|
487
|
-
|
|
488
|
-
-- Find unused indexes (PostgreSQL)
|
|
489
|
-
SELECT
|
|
490
|
-
schemaname,
|
|
491
|
-
tablename,
|
|
492
|
-
indexname,
|
|
493
|
-
idx_scan,
|
|
494
|
-
idx_tup_read,
|
|
495
|
-
idx_tup_fetch
|
|
496
|
-
FROM pg_stat_user_indexes
|
|
497
|
-
WHERE idx_scan = 0
|
|
498
|
-
ORDER BY pg_relation_size(indexrelid) DESC;
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
## Resources
|
|
502
|
-
|
|
503
|
-
- **references/postgres-optimization-guide.md**: PostgreSQL-specific optimization
|
|
504
|
-
- **references/mysql-optimization-guide.md**: MySQL/MariaDB optimization
|
|
505
|
-
- **references/query-plan-analysis.md**: Deep dive into EXPLAIN plans
|
|
506
|
-
- **assets/index-strategy-checklist.md**: When and how to create indexes
|
|
507
|
-
- **assets/query-optimization-checklist.md**: Step-by-step optimization guide
|
|
508
|
-
- **scripts/analyze-slow-queries.sql**: Identify slow queries in your database
|
|
509
|
-
- **scripts/index-recommendations.sql**: Generate index recommendations
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: tdd
|
|
3
|
-
description: Test-driven development with red-green-refactor loop. Use when user wants to build features or fix bugs using TDD, mentions "red-green-refactor", wants integration tests, or asks for test-first development.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Test-Driven Development
|
|
7
|
-
|
|
8
|
-
## Philosophy
|
|
9
|
-
|
|
10
|
-
**Core principle**: Tests should verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't.
|
|
11
|
-
|
|
12
|
-
**Good tests** are integration-style: they exercise real code paths through public APIs. They describe _what_ the system does, not _how_ it does it. A good test reads like a specification - "user can checkout with valid cart" tells you exactly what capability exists. These tests survive refactors because they don't care about internal structure.
|
|
13
|
-
|
|
14
|
-
**Bad tests** are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed. If you rename an internal function and tests fail, those tests were testing implementation, not behavior.
|
|
15
|
-
|
|
16
|
-
See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
|
|
17
|
-
|
|
18
|
-
## Anti-Pattern: Horizontal Slices
|
|
19
|
-
|
|
20
|
-
**DO NOT write all tests first, then all implementation.** This is "horizontal slicing" - treating RED as "write all tests" and GREEN as "write all code."
|
|
21
|
-
|
|
22
|
-
This produces **crap tests**:
|
|
23
|
-
|
|
24
|
-
- Tests written in bulk test _imagined_ behavior, not _actual_ behavior
|
|
25
|
-
- You end up testing the _shape_ of things (data structures, function signatures) rather than user-facing behavior
|
|
26
|
-
- Tests become insensitive to real changes - they pass when behavior breaks, fail when behavior is fine
|
|
27
|
-
- You outrun your headlights, committing to test structure before understanding the implementation
|
|
28
|
-
|
|
29
|
-
**Correct approach**: Vertical slices via tracer bullets. One test → one implementation → repeat. Each test responds to what you learned from the previous cycle. Because you just wrote the code, you know exactly what behavior matters and how to verify it.
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
WRONG (horizontal):
|
|
33
|
-
RED: test1, test2, test3, test4, test5
|
|
34
|
-
GREEN: impl1, impl2, impl3, impl4, impl5
|
|
35
|
-
|
|
36
|
-
RIGHT (vertical):
|
|
37
|
-
RED→GREEN: test1→impl1
|
|
38
|
-
RED→GREEN: test2→impl2
|
|
39
|
-
RED→GREEN: test3→impl3
|
|
40
|
-
...
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## Workflow
|
|
44
|
-
|
|
45
|
-
### 1. Planning
|
|
46
|
-
|
|
47
|
-
Before writing any code:
|
|
48
|
-
|
|
49
|
-
- [ ] Confirm with user what interface changes are needed
|
|
50
|
-
- [ ] Confirm with user which behaviors to test (prioritize)
|
|
51
|
-
- [ ] Identify opportunities for [deep modules](deep-modules.md) (small interface, deep implementation)
|
|
52
|
-
- [ ] Design interfaces for [testability](interface-design.md)
|
|
53
|
-
- [ ] List the behaviors to test (not implementation steps)
|
|
54
|
-
- [ ] Get user approval on the plan
|
|
55
|
-
|
|
56
|
-
Ask: "What should the public interface look like? Which behaviors are most important to test?"
|
|
57
|
-
|
|
58
|
-
**You can't test everything.** Confirm with the user exactly which behaviors matter most. Focus testing effort on critical paths and complex logic, not every possible edge case.
|
|
59
|
-
|
|
60
|
-
### 2. Tracer Bullet
|
|
61
|
-
|
|
62
|
-
Write ONE test that confirms ONE thing about the system:
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
RED: Write test for first behavior
|
|
66
|
-
↓ RUN TESTS — confirm it fails for the right reason
|
|
67
|
-
GREEN: Write minimal code to pass
|
|
68
|
-
↓ RUN TESTS — confirm it passes
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
This is your tracer bullet - proves the path works end-to-end.
|
|
72
|
-
|
|
73
|
-
**You MUST run the test before writing any implementation.** A test that was never observed to fail might always pass vacuously, be testing the wrong thing, or be broken. The failure message tells you what the test is actually checking.
|
|
74
|
-
|
|
75
|
-
### 3. Incremental Loop
|
|
76
|
-
|
|
77
|
-
For each remaining behavior:
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
RED: Write next test
|
|
81
|
-
↓ RUN TESTS — confirm new test fails, existing pass
|
|
82
|
-
GREEN: Minimal code to pass
|
|
83
|
-
↓ RUN TESTS — confirm all pass
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Rules:
|
|
87
|
-
|
|
88
|
-
- One test at a time
|
|
89
|
-
- Run tests after writing each test (confirm RED) and after each implementation step (confirm GREEN)
|
|
90
|
-
- Only enough code to pass current test
|
|
91
|
-
- Don't anticipate future tests
|
|
92
|
-
- Keep tests focused on observable behavior
|
|
93
|
-
|
|
94
|
-
### 4. Refactor
|
|
95
|
-
|
|
96
|
-
After all tests pass, look for [refactor candidates](refactoring.md):
|
|
97
|
-
|
|
98
|
-
- [ ] Extract duplication
|
|
99
|
-
- [ ] Deepen modules (move complexity behind simple interfaces)
|
|
100
|
-
- [ ] Apply SOLID principles where natural
|
|
101
|
-
- [ ] Consider what new code reveals about existing code
|
|
102
|
-
- [ ] Run tests after each refactor step
|
|
103
|
-
|
|
104
|
-
**Never refactor while RED.** Get to GREEN first.
|
|
105
|
-
|
|
106
|
-
## Checklist Per Cycle
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
[ ] Test describes behavior, not implementation
|
|
110
|
-
[ ] Test uses public interface only
|
|
111
|
-
[ ] Test would survive internal refactor
|
|
112
|
-
[ ] Code is minimal for this test
|
|
113
|
-
[ ] No speculative features added
|
|
114
|
-
```
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# Deep Modules
|
|
2
|
-
|
|
3
|
-
From "A Philosophy of Software Design":
|
|
4
|
-
|
|
5
|
-
**Deep module** = small interface + lots of implementation
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
┌─────────────────────┐
|
|
9
|
-
│ Small Interface │ ← Few methods, simple params
|
|
10
|
-
├─────────────────────┤
|
|
11
|
-
│ │
|
|
12
|
-
│ │
|
|
13
|
-
│ Deep Implementation│ ← Complex logic hidden
|
|
14
|
-
│ │
|
|
15
|
-
│ │
|
|
16
|
-
└─────────────────────┘
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
**Shallow module** = large interface + little implementation (avoid)
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
┌─────────────────────────────────┐
|
|
23
|
-
│ Large Interface │ ← Many methods, complex params
|
|
24
|
-
├─────────────────────────────────┤
|
|
25
|
-
│ Thin Implementation │ ← Just passes through
|
|
26
|
-
└─────────────────────────────────┘
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
When designing interfaces, ask:
|
|
30
|
-
|
|
31
|
-
- Can I reduce the number of methods?
|
|
32
|
-
- Can I simplify the parameters?
|
|
33
|
-
- Can I hide more complexity inside?
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# Interface Design for Testability
|
|
2
|
-
|
|
3
|
-
Good interfaces make testing natural:
|
|
4
|
-
|
|
5
|
-
1. **Accept dependencies, don't create them**
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// Testable
|
|
9
|
-
function processOrder(order, paymentGateway) {}
|
|
10
|
-
|
|
11
|
-
// Hard to test
|
|
12
|
-
function processOrder(order) {
|
|
13
|
-
const gateway = new StripeGateway();
|
|
14
|
-
}
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
2. **Return results, don't produce side effects**
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
// Testable
|
|
21
|
-
function calculateDiscount(cart): Discount {}
|
|
22
|
-
|
|
23
|
-
// Hard to test
|
|
24
|
-
function applyDiscount(cart): void {
|
|
25
|
-
cart.total -= discount;
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
3. **Small surface area**
|
|
30
|
-
- Fewer methods = fewer tests needed
|
|
31
|
-
- Fewer params = simpler test setup
|
|
32
|
-
|
|
33
|
-
4. **Keep test-only helpers out of production APIs**
|
|
34
|
-
|
|
35
|
-
If cleanup or inspection logic exists only for tests, put it in test utilities rather than adding methods to production classes. Production interfaces should reflect real runtime behavior, not test harness needs.
|