@claude-code-mastery/starter-kit 1.0.0
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/.claude/.starter-kit/profiles/clean.md +113 -0
- package/.claude/.starter-kit/profiles/go.md +458 -0
- package/.claude/.starter-kit/profiles/node.md +429 -0
- package/.claude/.starter-kit/profiles/python.md +475 -0
- package/.claude/.starter-kit/shared/analytics-rybbit.md +55 -0
- package/.claude/.starter-kit/shared/claude-md-base.md +93 -0
- package/.claude/.starter-kit/shared/deployment-dokploy.md +158 -0
- package/.claude/.starter-kit/shared/feature-manifest.md +43 -0
- package/.claude/.starter-kit/shared/mcp-and-pooler.md +38 -0
- package/.claude/.starter-kit/shared/mongo-setup.md +20 -0
- package/.claude/.starter-kit/shared/profile-config.md +65 -0
- package/.claude/.starter-kit/shared/seo.md +113 -0
- package/.claude/.starter-kit/shared/sql-setup.md +37 -0
- package/.claude/commands/add-feature.md +349 -0
- package/.claude/commands/add-project-setup.md +156 -0
- package/.claude/commands/architecture.md +27 -0
- package/.claude/commands/commit.md +61 -0
- package/.claude/commands/convert-project-to-starter-kit.md +508 -0
- package/.claude/commands/create-api.md +385 -0
- package/.claude/commands/create-e2e.md +230 -0
- package/.claude/commands/diagram.md +301 -0
- package/.claude/commands/help.md +120 -0
- package/.claude/commands/install-global.md +145 -0
- package/.claude/commands/new-project.md +244 -0
- package/.claude/commands/optimize-docker.md +352 -0
- package/.claude/commands/progress.md +61 -0
- package/.claude/commands/projects-created.md +79 -0
- package/.claude/commands/quickstart.md +105 -0
- package/.claude/commands/refactor.md +267 -0
- package/.claude/commands/remove-project.md +95 -0
- package/.claude/commands/review.md +59 -0
- package/.claude/commands/security-check.md +77 -0
- package/.claude/commands/set-project-profile-default.md +79 -0
- package/.claude/commands/setup.md +337 -0
- package/.claude/commands/show-user-guide.md +58 -0
- package/.claude/commands/starter-kit.md +90 -0
- package/.claude/commands/test-plan.md +118 -0
- package/.claude/commands/update-project.md +413 -0
- package/.claude/commands/what-is-my-ai-doing.md +42 -0
- package/.claude/commands/worktree.md +124 -0
- package/.claude/hooks/block-dangerous-bash.py +55 -0
- package/.claude/hooks/check-branch.sh +116 -0
- package/.claude/hooks/check-e2e.sh +71 -0
- package/.claude/hooks/check-env-sync.sh +41 -0
- package/.claude/hooks/check-file-length.py +47 -0
- package/.claude/hooks/check-ports.sh +59 -0
- package/.claude/hooks/check-rulecatch.sh +33 -0
- package/.claude/hooks/check-rybbit.sh +63 -0
- package/.claude/hooks/lint-on-save.sh +59 -0
- package/.claude/hooks/verify-no-secrets.sh +80 -0
- package/.claude/settings.json +34 -0
- package/.claude/skills/api-conventions/SKILL.md +34 -0
- package/.claude/skills/code-review/SKILL.md +87 -0
- package/.claude/skills/code-review/references/mongodb-checks.md +25 -0
- package/.claude/skills/code-review/references/project-checks.md +38 -0
- package/.claude/skills/create-service/SKILL.md +222 -0
- package/.claude/skills/debugger/SKILL.md +39 -0
- package/.claude/skills/dependency-vetting/SKILL.md +46 -0
- package/.claude/skills/design-review/SKILL.md +50 -0
- package/.claude/skills/mcp-builder/SKILL.md +57 -0
- package/.claude/skills/mongodb-rules/SKILL.md +62 -0
- package/.claude/skills/terminal-tui/SKILL.md +106 -0
- package/.claude/skills/test-writer/SKILL.md +78 -0
- package/LICENSE +21 -0
- package/README.md +2152 -0
- package/bin/cli.js +205 -0
- package/claude-mastery-project.conf +220 -0
- package/global-claude-md/CLAUDE.md +212 -0
- package/global-claude-md/settings.json +3 -0
- package/package.json +81 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-review
|
|
3
|
+
description: Review changed code for correctness, security, and maintainability with cited, severity-ranked findings. Use when asked to review a diff, a PR, a commit, or named files, to audit code, or to check before merge. Runs isolated and read-only, reports findings, does not edit.
|
|
4
|
+
when_to_use: |
|
|
5
|
+
- User asks to review code, audit a file, or check a PR before merge
|
|
6
|
+
- User says "review", "audit", "check this code", or "find issues"
|
|
7
|
+
- Do NOT use for formatting or style-only passes
|
|
8
|
+
context: fork
|
|
9
|
+
allowed-tools: "Read, Grep, Bash"
|
|
10
|
+
effort: high
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Code Review
|
|
14
|
+
|
|
15
|
+
You review like a senior engineer reviews a pull request: concrete findings, each tied to a line, each with a fix and a reason. No generic praise, no style nitpicking dressed up as risk. You report only. You do not edit code.
|
|
16
|
+
|
|
17
|
+
## Priority order
|
|
18
|
+
|
|
19
|
+
When triaging what to flag and how hard, rank in this order:
|
|
20
|
+
|
|
21
|
+
1. **Security** — secrets, injection, auth bypass
|
|
22
|
+
2. **Correctness** — logic errors, race conditions, null risks
|
|
23
|
+
3. **Performance** — N+1 queries, leaks, missing indexes
|
|
24
|
+
4. **Type safety** — `any`, missing null checks, unsafe casts
|
|
25
|
+
5. **Maintainability** — dead code, unclear naming (lowest)
|
|
26
|
+
|
|
27
|
+
If the code is good, say so. Do not invent issues to fill a report.
|
|
28
|
+
|
|
29
|
+
## 1. Scope
|
|
30
|
+
|
|
31
|
+
Establish what changed: `git diff --name-only HEAD~1`, or read the files named. Identify the primary concern (security, correctness, performance) and pull conventions from `CLAUDE.md` if present.
|
|
32
|
+
|
|
33
|
+
## 2. Cheap wins first (run tools before reading)
|
|
34
|
+
|
|
35
|
+
Run what the project supports, skip what's missing, never fail the review over a missing tool:
|
|
36
|
+
|
|
37
|
+
- Dependency CVEs: `npm audit` / `pip-audit` / `cargo audit`
|
|
38
|
+
- Secrets in the diff: grep changed files for assignments to keys, tokens, passwords with long literal values
|
|
39
|
+
- Recent context: `git log --oneline -5`
|
|
40
|
+
|
|
41
|
+
## 3. Read by diff size
|
|
42
|
+
|
|
43
|
+
- Under 20 files: read each changed file in full.
|
|
44
|
+
- 20 to 100: read the diff, then deep-read the high-risk files (auth, payment, config, migrations, shared utilities, anything touching the data layer).
|
|
45
|
+
- Over 100: ask for a narrower scope before proceeding.
|
|
46
|
+
|
|
47
|
+
## 4. Project checks (highest priority)
|
|
48
|
+
|
|
49
|
+
Read `references/project-checks.md` and apply every check in it. These are this codebase's hard rules: data access and driver discipline, StrictDB-or-native, API versioning, service boundaries. Violations here are CRITICAL by default, they are the failures generic review misses.
|
|
50
|
+
|
|
51
|
+
## 5. General checks
|
|
52
|
+
|
|
53
|
+
Security: user input reaching a query, command, or file path without sanitization; auth checks that can be bypassed or absent; secrets or PII in logs or responses; hand-rolled crypto instead of standard library.
|
|
54
|
+
|
|
55
|
+
Error handling: external calls (network, DB, file I/O) with no failure path; cleanup that won't run on the error branch; errors that swallow context or leak internals.
|
|
56
|
+
|
|
57
|
+
Tests: assertions on behavior, not implementation; missing edge cases (empty, boundary, concurrent); mocks that bleed state.
|
|
58
|
+
|
|
59
|
+
Performance: queries inside loops (N+1); whole collections loaded into memory instead of paginated; missing indexes on filtered or joined fields.
|
|
60
|
+
|
|
61
|
+
## 6. Language checks (the high-value ones)
|
|
62
|
+
|
|
63
|
+
- **TypeScript** — every `any` needs a typed replacement or a justified suppression; `strict: true` present; floating promises (not awaited, not handled); null access in critical paths.
|
|
64
|
+
- **Python** — mutable default args (`def f(x=[])`); bare `except:`; `eval`/`exec` on user-influenced input; missing type hints on public signatures.
|
|
65
|
+
- **Go** — errors discarded with `_` on non-trivial paths; goroutines with no cancellation path; `defer` inside a loop.
|
|
66
|
+
|
|
67
|
+
## Output
|
|
68
|
+
|
|
69
|
+
Every finding in this shape:
|
|
70
|
+
|
|
71
|
+
> **[🔴 CRITICAL | 🟡 WARNING | 🔵 SUGGESTION] `file:line` — one-line description**
|
|
72
|
+
> Issue: what's wrong.
|
|
73
|
+
> Fix: the concrete change.
|
|
74
|
+
> Why: what breaks if it ships.
|
|
75
|
+
|
|
76
|
+
A finding with no `file:line` is not a finding. Drop it or go find the line.
|
|
77
|
+
|
|
78
|
+
## RuleCatch
|
|
79
|
+
|
|
80
|
+
After the manual pass, check RuleCatch for automated violations:
|
|
81
|
+
|
|
82
|
+
- If the RuleCatch MCP server is available, query it for violations on the reviewed files and add a dedicated **RuleCatch Violations** section. This catches pattern-based violations the manual pass can miss.
|
|
83
|
+
- If RuleCatch MCP is not connected, add one line: "Install RuleCatch MCP for automated violation monitoring."
|
|
84
|
+
|
|
85
|
+
## Close
|
|
86
|
+
|
|
87
|
+
One line: files examined, count per severity, the single highest-priority issue, and a verdict: **BLOCK**, **APPROVE WITH SUGGESTIONS**, or **APPROVE**.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Project Review Checks: Data Layer and Architecture
|
|
2
|
+
|
|
3
|
+
Apply these to every diff. These are this codebase's hard rules. A violation here is CRITICAL unless noted. They exist because generic reviewers don't know them.
|
|
4
|
+
|
|
5
|
+
## Data access
|
|
6
|
+
|
|
7
|
+
- [ ] **No Mongoose.** Flag any Mongoose import, model, or schema. This codebase never uses Mongoose.
|
|
8
|
+
- [ ] **The adapter is the boundary.** Flag raw collection access in feature code. Data access goes through the adapter, which uses StrictDB if installed, otherwise the native driver.
|
|
9
|
+
- [ ] **No `_id` in a write body.** Flag any update or upsert payload that contains `_id`. It belongs in the filter, never the update.
|
|
10
|
+
- [ ] **Type-safe `_id`.** Flag any query comparing a string against an `ObjectId` `_id`. This silently returns nothing. Confirm the type is rehydrated before the query.
|
|
11
|
+
- [ ] **Upsert type rehydration.** Flag upserts that run on JSON-parsed data without `rehydrateTypes`. A string-vs-`ObjectId` `_id` throws code 66.
|
|
12
|
+
|
|
13
|
+
## Reads and writes
|
|
14
|
+
|
|
15
|
+
- [ ] **Reads are aggregation pipelines, not `find()`.** Flag `find()` in feature code; it should be a pipeline.
|
|
16
|
+
- [ ] **Multi-document writes use `bulkWrite`.** Flag any loop that issues one write per iteration.
|
|
17
|
+
- [ ] **No `createIndex` in a request path.** Flag `createIndex` anywhere in a handler or hot loop. Indexes are declared once at startup or in a migration.
|
|
18
|
+
- [ ] **No deep nested-array queries.** Flag queries that reach into arrays-within-arrays past `maxDepth=3`. The model needs flattening.
|
|
19
|
+
- [ ] **`$elemMatch` is a smell.** Flag `$elemMatch` usage and note that the field probably belongs on the document or in a separate collection.
|
|
20
|
+
|
|
21
|
+
## Architecture
|
|
22
|
+
|
|
23
|
+
- [ ] **API versioning.** Routes live under `/api/v1/`. Flag unversioned routes.
|
|
24
|
+
- [ ] **No business logic in route handlers.** Handlers wire request to service and back. Flag domain logic inline in a handler.
|
|
25
|
+
- [ ] **Service separation respected.** Flag a service reaching across a boundary into another service's internals instead of going through its interface.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Project Review Checks: Architecture and Data Layer
|
|
2
|
+
|
|
3
|
+
Apply these to every diff. They are this codebase's hard rules. A violation is CRITICAL unless noted. They exist because generic reviewers don't know them.
|
|
4
|
+
|
|
5
|
+
## Layering (server → handlers → adapters)
|
|
6
|
+
|
|
7
|
+
- [ ] **Routes under `/api/v1/`.** Flag unversioned routes.
|
|
8
|
+
- [ ] **`server.ts` is thin.** Flag business logic in `server.ts` or in a route definition; it belongs in `handlers/`.
|
|
9
|
+
- [ ] **Business logic in `handlers/`,** one domain per file.
|
|
10
|
+
- [ ] **Handlers don't touch external systems directly.** Flag a handler importing a DB driver or calling an external API inline; that goes through an adapter in `adapters/`.
|
|
11
|
+
- [ ] **Service (package) separation.** Flag a package reaching into another package's internals instead of going through its interface.
|
|
12
|
+
|
|
13
|
+
## Data access (lives in the adapter layer)
|
|
14
|
+
|
|
15
|
+
- [ ] **No Mongoose.** Flag any Mongoose import, model, or schema.
|
|
16
|
+
- [ ] **Data access goes through the adapter.** Flag raw collection access outside `adapters/`. Inside the adapter, StrictDB if installed else the native driver, the driver choice isn't a violation; Mongoose and raw access in feature code are.
|
|
17
|
+
- [ ] **One shared `MongoClient`.** Flag `new MongoClient` inside a request handler or per-call path; the client is created once and reused. In serverless, it must be at module scope, not inside the handler.
|
|
18
|
+
- [ ] **No `_id` in a write body.** Flag any update or upsert payload containing `_id`; it belongs in the filter.
|
|
19
|
+
- [ ] **Type-safe `_id`.** Flag a query comparing a string against an `ObjectId` `_id`; it silently returns nothing. For `$in`, every element must be converted.
|
|
20
|
+
- [ ] **Rehydrate types at the boundary.** Flag saving or querying with a JSON-parsed body whose `ObjectId` and `Date` fields weren't revived; a string-vs-`ObjectId` `_id` throws code 66 and date strings break range/sort.
|
|
21
|
+
- [ ] **Reads are aggregation pipelines, not `find()`.** Flag `find()` in feature code.
|
|
22
|
+
- [ ] **Multi-document writes use `bulkWrite`,** built once. Flag a database call inside a loop; build the ops array and execute one `bulkWrite` after the loop.
|
|
23
|
+
- [ ] **No `createIndex` in a request path.** Flag `createIndex` in a handler or hot loop; indexes are declared once at startup or in a migration.
|
|
24
|
+
- [ ] **No deep nested-array queries** past `maxDepth=3`. The model needs flattening.
|
|
25
|
+
- [ ] **`$elemMatch` is a smell.** Flag it and note the field probably belongs on the document or in a separate collection.
|
|
26
|
+
|
|
27
|
+
## Data correctness and scale
|
|
28
|
+
|
|
29
|
+
- [ ] **`null` vs missing.** Flag a `{ field: null }` match where the intent was "has no value"; it also matches missing. Suggest `$exists` for presence or `$ne: null` for a real value.
|
|
30
|
+
- [ ] **No `$skip` for deep pagination.** Flag `$skip` with a large or page-number offset; suggest range pagination on an indexed key with an `_id` tie-breaker.
|
|
31
|
+
- [ ] **Right count for the job.** Flag a `countDocuments` used for a rough whole-collection total where `estimatedDocumentCount` belongs, and a `distinct` on a high-cardinality field at scale that should be a `$group`.
|
|
32
|
+
- [ ] **No unbounded embedding.** Flag an array that grows without bound (comments, events, logs) being embedded; it should be a referenced collection.
|
|
33
|
+
- [ ] **Uniqueness via index, not app check.** Flag an application-level "does this already exist" guard standing in for a unique index, and a plain unique index on an optional field that should be partial.
|
|
34
|
+
- [ ] **Transactions are for cross-document atomicity.** Flag a transaction wrapping writes to a single entity (a modeling smell), and a blind whole-batch retry after a partial `bulkWrite` failure.
|
|
35
|
+
|
|
36
|
+
## Structure
|
|
37
|
+
|
|
38
|
+
- [ ] **No file exceeds 300 lines.** Flag any file over the limit and point at the natural split (one concern per file).
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-service
|
|
3
|
+
description: Scaffold a new microservice that follows the project's server/handlers/adapters architecture. Use when asked to create, scaffold, or add a new service or package. Writes files and may create a git branch, so it runs only when invoked explicitly.
|
|
4
|
+
when_to_use: |
|
|
5
|
+
- User asks to create, scaffold, or add a new service or microservice
|
|
6
|
+
- User says "new service", "scaffold service", "create service", "add service"
|
|
7
|
+
- Do NOT auto-run; this writes files and touches git
|
|
8
|
+
disable-model-invocation: true
|
|
9
|
+
allowed-tools: "Read, Write, Edit, Bash"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Create Service
|
|
13
|
+
|
|
14
|
+
Scaffold a new service that follows the project architecture. This writes files and may create a branch, so it only runs when you type `/create-service`.
|
|
15
|
+
|
|
16
|
+
## Architecture
|
|
17
|
+
|
|
18
|
+
Three layers, one direction. `server.ts` is thin, `handlers/` hold logic, `adapters/` wrap everything external.
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
server.ts routes only, NEVER business logic
|
|
22
|
+
│
|
|
23
|
+
▼
|
|
24
|
+
handlers/ business logic, one file per domain
|
|
25
|
+
│
|
|
26
|
+
▼
|
|
27
|
+
adapters/ external wrappers (database via StrictDB, APIs, queues)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This matches the `api-conventions` skill. Keep them in sync: if the layering changes, change both.
|
|
31
|
+
|
|
32
|
+
## Directory structure
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
packages/{name}/
|
|
36
|
+
├── src/
|
|
37
|
+
│ ├── server.ts # entry point — routes only
|
|
38
|
+
│ ├── handlers/ # business logic
|
|
39
|
+
│ │ └── index.ts
|
|
40
|
+
│ ├── adapters/ # external wrappers
|
|
41
|
+
│ │ ├── index.ts
|
|
42
|
+
│ │ └── db.ts # StrictDB data adapter (the only place the driver lives)
|
|
43
|
+
│ └── types.ts # TypeScript types
|
|
44
|
+
├── tests/
|
|
45
|
+
│ └── handlers.test.ts
|
|
46
|
+
├── package.json
|
|
47
|
+
├── tsconfig.json
|
|
48
|
+
└── CLAUDE.md # service-specific instructions
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## package.json — resolve versions at scaffold time
|
|
52
|
+
|
|
53
|
+
**Do not hardcode dependency versions.** Before writing `package.json`, resolve the current stable version of each dependency (`npm view <pkg> version`, or context7) and pin those. Hardcoded versions rot the day they ship, and a stale pin is how you get an Express 4 runtime against Express 5 types.
|
|
54
|
+
|
|
55
|
+
Dependencies to resolve and include:
|
|
56
|
+
|
|
57
|
+
- runtime: `express` (current major is 5)
|
|
58
|
+
- dev: `tsx`, `typescript`, `vitest`, `@types/express`
|
|
59
|
+
|
|
60
|
+
Make the `@types/express` major match the `express` major. Verify, don't assume.
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"name": "@project/{name}",
|
|
65
|
+
"version": "1.0.0",
|
|
66
|
+
"type": "module",
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "tsc",
|
|
69
|
+
"dev": "tsx watch src/server.ts",
|
|
70
|
+
"start": "node dist/server.js",
|
|
71
|
+
"test": "vitest run"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"express": "<resolved>"
|
|
75
|
+
},
|
|
76
|
+
"devDependencies": {
|
|
77
|
+
"tsx": "<resolved>",
|
|
78
|
+
"typescript": "<resolved>",
|
|
79
|
+
"vitest": "<resolved>",
|
|
80
|
+
"@types/express": "<resolved, major matching express>"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Template: src/server.ts
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import express from 'express';
|
|
89
|
+
import { handlers } from './handlers/index.js';
|
|
90
|
+
|
|
91
|
+
const app = express();
|
|
92
|
+
const PORT = process.env.PORT || 3000;
|
|
93
|
+
|
|
94
|
+
app.use(express.json());
|
|
95
|
+
|
|
96
|
+
app.get('/health', (_req, res) => {
|
|
97
|
+
res.json({ status: 'ok', service: '{name}' });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Routes delegate to handlers. NEVER put logic here.
|
|
101
|
+
// Replace this catch-all with real REST routes per domain.
|
|
102
|
+
app.post('/api/v1/:action', handlers.handleAction);
|
|
103
|
+
|
|
104
|
+
process.on('unhandledRejection', (reason) => {
|
|
105
|
+
console.error('Unhandled Rejection:', reason);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
process.on('uncaughtException', (error) => {
|
|
110
|
+
console.error('Uncaught Exception:', error);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
app.listen(PORT, () => {
|
|
115
|
+
console.log(`{name} running on port ${PORT}`);
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Template: src/adapters/db.ts
|
|
120
|
+
|
|
121
|
+
The data adapter is the only place the driver is touched. Use StrictDB if it's installed, otherwise the native MongoDB driver. Never Mongoose. Handlers import this, never the driver.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Wire to your StrictDB package/API. This is the data boundary for the service.
|
|
125
|
+
// Rules enforced here (see the mongodb-rules skill):
|
|
126
|
+
// - StrictDB if installed, else the native driver; never Mongoose
|
|
127
|
+
// - reads are aggregation pipelines, not find()
|
|
128
|
+
// - multi-document writes use bulkWrite
|
|
129
|
+
// - never put _id in a write body; rehydrate types before upserts
|
|
130
|
+
import { StrictDB } from 'strictdb'; // if StrictDB isn't installed, import { MongoClient } from 'mongodb' and use that instead
|
|
131
|
+
|
|
132
|
+
const db = new StrictDB({ uri: process.env.MONGODB_URI! });
|
|
133
|
+
|
|
134
|
+
export const dbAdapter = {
|
|
135
|
+
// Example read — express as an aggregation pipeline in real methods.
|
|
136
|
+
async getById(collection: string, id: unknown) {
|
|
137
|
+
// ensure `id` is an ObjectId, not a string, before querying
|
|
138
|
+
return db.collection(collection).aggregate([{ $match: { _id: id } }]).next();
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
// Example write — use bulkWrite for multi-document operations.
|
|
142
|
+
async upsertMany(collection: string, ops: unknown[]) {
|
|
143
|
+
return db.collection(collection).bulkWrite(ops);
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Template: src/types.ts
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
export interface ServiceConfig {
|
|
152
|
+
port: number;
|
|
153
|
+
name: string;
|
|
154
|
+
environment: 'development' | 'staging' | 'production';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Add your domain types here.
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Template: tsconfig.json
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"compilerOptions": {
|
|
165
|
+
"target": "ES2022",
|
|
166
|
+
"module": "ESNext",
|
|
167
|
+
"moduleResolution": "bundler",
|
|
168
|
+
"outDir": "dist",
|
|
169
|
+
"rootDir": "src",
|
|
170
|
+
"strict": true,
|
|
171
|
+
"esModuleInterop": true,
|
|
172
|
+
"skipLibCheck": true,
|
|
173
|
+
"declaration": true
|
|
174
|
+
},
|
|
175
|
+
"include": ["src/**/*"],
|
|
176
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Auto-branch (if on main)
|
|
181
|
+
|
|
182
|
+
Before scaffolding, check the branch:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
git branch --show-current
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Default (`auto_branch = true` in `claude-mastery-project.conf`):
|
|
189
|
+
|
|
190
|
+
- On `main`/`master`: create and switch to a feature branch, then report it.
|
|
191
|
+
```bash
|
|
192
|
+
git checkout -b feat/<service-name>
|
|
193
|
+
```
|
|
194
|
+
"Created branch `feat/<service-name>`, main stays untouched."
|
|
195
|
+
- On a feature branch already: proceed.
|
|
196
|
+
- Not a git repo: skip.
|
|
197
|
+
- **If `claude-mastery-project.conf` is missing:** treat `auto_branch` as unset and ask before creating a branch on main, rather than assuming.
|
|
198
|
+
|
|
199
|
+
To disable: set `auto_branch = false`. When disabled, warn and ask before proceeding on main.
|
|
200
|
+
|
|
201
|
+
## After creating — checklist
|
|
202
|
+
|
|
203
|
+
- [ ] Directory matches the template, including `adapters/db.ts`
|
|
204
|
+
- [ ] `package.json` versions were resolved at scaffold time, not copied
|
|
205
|
+
- [ ] `@types/express` major matches `express` major
|
|
206
|
+
- [ ] TypeScript strict mode on
|
|
207
|
+
- [ ] Entry point has both `unhandledRejection` and `uncaughtException` handlers
|
|
208
|
+
- [ ] All routes under `/api/v1/`
|
|
209
|
+
- [ ] Business logic in `handlers/`, not `server.ts`
|
|
210
|
+
- [ ] Database access through the adapter in `adapters/` (StrictDB if installed, else native driver), no Mongoose, no raw driver in handlers
|
|
211
|
+
- [ ] No file exceeds 300 lines
|
|
212
|
+
- [ ] Port assigned in the root CLAUDE.md port table
|
|
213
|
+
- [ ] Service added to `project-docs/ARCHITECTURE.md`
|
|
214
|
+
- [ ] Basic test file created
|
|
215
|
+
- [ ] `.dockerignore` created (if using Docker)
|
|
216
|
+
|
|
217
|
+
## RuleCatch
|
|
218
|
+
|
|
219
|
+
After scaffolding, check RuleCatch:
|
|
220
|
+
|
|
221
|
+
- If the RuleCatch MCP server is available, query it for violations in the new service files and report them.
|
|
222
|
+
- If not connected, suggest checking the RuleCatch dashboard.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debugger
|
|
3
|
+
description: Diagnose failures by root cause, not symptom. Use for crashes, stack traces, intermittent bugs, memory growth, race conditions, and "works locally, fails in prod" cases where you need a reproducible cause before a fix. Runs isolated and read-only, reports the root cause and fix.
|
|
4
|
+
when_to_use: |
|
|
5
|
+
- User reports a crash, exception, stack trace, or failing behavior and wants the cause
|
|
6
|
+
- A bug is intermittent, environment-specific, or not reproducing locally
|
|
7
|
+
- Memory or resource growth needs tracing to a source
|
|
8
|
+
context: fork
|
|
9
|
+
allowed-tools: "Read, Grep, Bash"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Debugger
|
|
13
|
+
|
|
14
|
+
You find root causes. You do not patch symptoms, and you do not guess.
|
|
15
|
+
|
|
16
|
+
## Method
|
|
17
|
+
|
|
18
|
+
Work these in order. Do not skip ahead.
|
|
19
|
+
|
|
20
|
+
1. **Reproduce before anything else.** Build the smallest script or test that triggers the failure every time. If you cannot reproduce it, stop. The bug is now "why can't I reproduce this," and you investigate that gap instead of writing a fix blind.
|
|
21
|
+
2. **State observed vs expected, precisely.** "Under condition X, the system does Y; it should do Z." If you can't fill that in, you don't understand the bug yet.
|
|
22
|
+
3. **Rank two or three hypotheses.** Order by likelihood, weighted toward whatever changed most recently. Name each one.
|
|
23
|
+
4. **Falsify the top hypothesis with the cheapest possible probe.** One log line, one targeted grep, one assertion. Try to prove yourself wrong before writing any fix. A hypothesis you only confirmed is one you didn't test.
|
|
24
|
+
5. **Fix, and add the regression test in the same change.** The test must fail on the old code and pass on the new. Fix without test is not done.
|
|
25
|
+
6. **Record the root cause and one prevention step.** What it was, what the falsifying probe showed, and the one change that stops the whole class from recurring.
|
|
26
|
+
|
|
27
|
+
## Production incidents
|
|
28
|
+
|
|
29
|
+
For anything live, do these three before opening a source file. Most incidents resolve here.
|
|
30
|
+
|
|
31
|
+
1. **Change correlation first.** What deployed, what flag flipped, what config changed, what traffic shifted in the 30 minutes before the first error. `git log --since`, deploy history, flag state. A correlated change usually is the answer.
|
|
32
|
+
2. **Trace to the first failing span.** Start from the earliest operation that errored or blew its latency budget, not the symptom the user reported. The symptom is downstream.
|
|
33
|
+
3. **Logs, tightly windowed.** ±2 minutes around that first error, filtered to the failing service and correlation ID. `grep`, `jq`, `awk`.
|
|
34
|
+
|
|
35
|
+
## Non-negotiable
|
|
36
|
+
|
|
37
|
+
- Never ship a fix for a bug you could not reproduce.
|
|
38
|
+
- The fix and its regression test land together or not at all.
|
|
39
|
+
- Every fix ends with one named prevention measure.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dependency-vetting
|
|
3
|
+
description: Vet an npm (or other) package for supply-chain risk before it's added to a project. Use when asked whether a package is safe, trustworthy, or worth adding, when about to install an unfamiliar dependency, or when something about a package feels off. Runs isolated and read-only, reports an evidence-backed risk verdict, does not install anything.
|
|
4
|
+
when_to_use: |
|
|
5
|
+
- User asks "is this package safe", "should I add X", "can I trust this dependency"
|
|
6
|
+
- About to install an unfamiliar or low-profile package
|
|
7
|
+
- A package's downloads, stars, or behavior look suspicious
|
|
8
|
+
- Do NOT use for known-good, widely-used packages where the answer is obvious
|
|
9
|
+
context: fork
|
|
10
|
+
allowed-tools: "Read, Grep, Bash, WebFetch"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Vet a Dependency
|
|
14
|
+
|
|
15
|
+
`npm audit` catches known CVEs in packages you already trust. This is the other problem: deciding whether to trust a package in the first place. The goal is an evidence-backed verdict, not a vibe.
|
|
16
|
+
|
|
17
|
+
## What to gather
|
|
18
|
+
|
|
19
|
+
Pull the facts before judging. Use `npm view <pkg>`, the npm and GitHub pages, and the published tarball.
|
|
20
|
+
|
|
21
|
+
- **Downloads vs. stars vs. dependents.** A package with thousands of weekly downloads, single-digit stars, and no dependents is a mismatch worth explaining. Real adoption shows up in all three. Check the download *shape* too: organic growth is a curve; isolated single-day spikes with dead baseline around them are manufactured.
|
|
22
|
+
- **Install scripts.** Read `package.json` for `preinstall`, `install`, `postinstall`. Anything that runs on install is the highest-risk surface, read exactly what it does.
|
|
23
|
+
- **`curl | sh` and friends.** Any install step that pipes a remote script into a shell, or `npx`-runs a second package, or registers a persistent MCP server, is a red flag. The package should do what its README says and nothing the README doesn't.
|
|
24
|
+
- **What it actually does vs. claims.** Read the real entry points. Network calls, child_process, filesystem writes outside its own dir, env/credential reads, and telemetry that the README doesn't disclose are all findings.
|
|
25
|
+
- **Obfuscation.** Minified-only source with no readable repo, `eval`, base64 blobs, or dynamic `require` of constructed strings. Legitimate packages ship readable code.
|
|
26
|
+
- **Maintainer and provenance.** Account age, other packages, repo activity, whether the npm package's repo link actually matches a real, public repo. A brand-new author with one oddly-popular package warrants more scrutiny.
|
|
27
|
+
- **Typosquatting.** Is the name one character off a popular package? Is it impersonating a known scope?
|
|
28
|
+
- **License sanity.** Missing, contradictory, or relicensed-from-someone-else (a file marked MIT that's actually CC-BY upstream) is both a legal and a trust signal.
|
|
29
|
+
|
|
30
|
+
## How to judge, and how to phrase it
|
|
31
|
+
|
|
32
|
+
Separate what the evidence supports from what it doesn't. This is the discipline that keeps a verdict defensible:
|
|
33
|
+
|
|
34
|
+
- You can say a package **behaves in ways that warrant caution** and point at the exact behavior (an undisclosed postinstall, a downloads/stars mismatch, an undisclosed network call). That's an evidence claim.
|
|
35
|
+
- You generally **cannot** say it "is malware" or attribute it to a specific actor or coordinated scheme. Intent and authorship usually aren't provable from the artifact. Don't claim what you can't show.
|
|
36
|
+
- Stars and dependents are a truer adoption signal than downloads, which are cheap to manufacture. Weight accordingly.
|
|
37
|
+
|
|
38
|
+
## Output
|
|
39
|
+
|
|
40
|
+
A short report:
|
|
41
|
+
|
|
42
|
+
- **Verdict**: Safe to add / Add with caution / Avoid.
|
|
43
|
+
- **Evidence**: the specific findings, each tied to what you observed, in order of severity.
|
|
44
|
+
- **If "caution" or "avoid"**: what would have to change to trust it, or a safer, more-adopted alternative.
|
|
45
|
+
|
|
46
|
+
Never soften a real finding to be agreeable, and never inflate a thin one into an accusation. Report what the artifact shows.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-review
|
|
3
|
+
description: Critique UI and UX for usability, accessibility, and distinctiveness with cited, evidence-based findings. Use when reviewing a screen, component, mockup, CSS or markup, or design tokens, or when asked for feedback on layout, typography, color, contrast, or user experience. Runs isolated and read-only, reports findings, does not edit.
|
|
4
|
+
when_to_use: |
|
|
5
|
+
- User shares a UI, component, CSS/HTML, or mockup and wants feedback
|
|
6
|
+
- User asks about layout, type, color, contrast, accessibility, or "does this look right"
|
|
7
|
+
- Do NOT use for backend, data, or non-visual code
|
|
8
|
+
context: fork
|
|
9
|
+
allowed-tools: "Read, Grep, Glob, WebFetch"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Design Review
|
|
13
|
+
|
|
14
|
+
You critique interfaces like a senior designer who respects the user's time. Opinionated, evidence-based, specific. You report. You do not edit.
|
|
15
|
+
|
|
16
|
+
## Stance
|
|
17
|
+
|
|
18
|
+
- **Cite, don't assert.** Any claim about user behavior ("users scan the left first," "this fails contrast") points to a source or a measurable standard. If you can't source it, say so and mark it as opinion. Verify a figure before stating it. Never invent a statistic.
|
|
19
|
+
- **Distinctive over default.** Fight templated "AI slop": the purple gradient, Inter everywhere, a card for everything, centered everything, the same SaaS landing page as every other site. Generic isn't safe, it's forgettable. Push for choices that have a reason behind them.
|
|
20
|
+
- **Usability over trend.** When a fashionable pattern hurts the user (low-contrast text, unlabeled icons, scroll-jacking, motion that can't be stopped), name the cost.
|
|
21
|
+
|
|
22
|
+
## Accessibility (pass/fail, not opinion)
|
|
23
|
+
|
|
24
|
+
- Contrast meets WCAG AA: 4.5:1 for body text, 3:1 for large text. Flag anything under.
|
|
25
|
+
- Every interactive element has a visible focus state.
|
|
26
|
+
- Icon-only buttons have an accessible label; form inputs have real `<label>`s, not just placeholders.
|
|
27
|
+
- Motion respects a reduced-motion preference.
|
|
28
|
+
- Nothing conveys meaning by color alone.
|
|
29
|
+
|
|
30
|
+
## Hierarchy and scannability
|
|
31
|
+
|
|
32
|
+
- The most important thing on the screen is the most prominent. If everything is emphasized, nothing is.
|
|
33
|
+
- Front-load what matters. People scan far more than they read; structure for the scan, not the close read.
|
|
34
|
+
- Group related elements, separate unrelated ones. Spacing is information.
|
|
35
|
+
|
|
36
|
+
## Consistency
|
|
37
|
+
|
|
38
|
+
- Reuse existing components and tokens before inventing new ones. Flag a one-off that duplicates something the system already has.
|
|
39
|
+
- Color, spacing, and type come from defined tokens, not hardcoded values scattered in markup.
|
|
40
|
+
|
|
41
|
+
## Output
|
|
42
|
+
|
|
43
|
+
Every finding in this shape:
|
|
44
|
+
|
|
45
|
+
> **[🔴 BLOCKER | 🟡 ISSUE | 🔵 SUGGESTION] location — one line**
|
|
46
|
+
> Problem: what's wrong and who it hurts.
|
|
47
|
+
> Fix: the concrete change.
|
|
48
|
+
> Basis: the standard or the sourced research, or "opinion" if it's a judgment call.
|
|
49
|
+
|
|
50
|
+
Close with the single highest-impact change and whether the surface is ship-ready.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mcp-builder
|
|
3
|
+
description: Build a high-quality MCP server that exposes a service or API to an LLM through well-designed tools. Use when asked to create, scaffold, or add an MCP server, wrap an API as MCP tools, or expose a service to Claude/agents. Writes files, so it runs only when invoked explicitly.
|
|
4
|
+
when_to_use: |
|
|
5
|
+
- User asks to build, scaffold, or add an MCP server
|
|
6
|
+
- User wants to wrap an API, service, or database as MCP tools
|
|
7
|
+
- User says "MCP server", "expose this as tools", "make this callable by an agent"
|
|
8
|
+
- Do NOT auto-run; this writes files
|
|
9
|
+
disable-model-invocation: true
|
|
10
|
+
allowed-tools: "Read, Write, Edit, Bash"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Build an MCP Server
|
|
14
|
+
|
|
15
|
+
An MCP server is judged by one thing: whether an LLM can use its tools to finish real tasks. Tool count, endpoint coverage, and cleverness are means, not the goal. Build for the agent that will call it.
|
|
16
|
+
|
|
17
|
+
## Default stack
|
|
18
|
+
|
|
19
|
+
- **TypeScript, on the MCP SDK.** Strong typing and good model familiarity. Drop to Python (FastMCP) only when the service it wraps is Python-native.
|
|
20
|
+
- **Transport: streamable HTTP with stateless JSON for remote servers; stdio for local.** Stateless scales and debugs far more easily than session streaming.
|
|
21
|
+
- **Resolve the SDK version at build time**, don't hardcode from memory. The current SDK README is the source of truth.
|
|
22
|
+
|
|
23
|
+
## The four phases
|
|
24
|
+
|
|
25
|
+
**1. Understand the target.** Read the service's API or schema first. Identify the handful of tasks a user actually wants done through it, not every endpoint. A wrapper that mirrors 60 REST routes one-to-one is worse than 8 tools shaped around real workflows.
|
|
26
|
+
|
|
27
|
+
**2. Design the tools.** This is where quality is won or lost.
|
|
28
|
+
|
|
29
|
+
- **Discoverable names**: `verb_noun`, scoped by service, for example `github_create_issue`, `orders_find_by_customer`. The name should tell the model when to reach for it.
|
|
30
|
+
- **Descriptions written for the model**, stating what the tool does and when to use it, not just what it returns.
|
|
31
|
+
- **Explicit input and output schemas.** Constrain inputs; return structured, parseable output, not prose the model has to scrape.
|
|
32
|
+
- **Annotations**: mark each tool `readOnlyHint`, `destructiveHint`, or `idempotentHint` so the client can reason about safety.
|
|
33
|
+
- **Actionable errors.** "Invalid date" is useless; "expected ISO 8601, got 03/04/2026, did you mean 2026-03-04" lets the model self-correct.
|
|
34
|
+
- **Balance coverage against workflow tools.** Comprehensive low-level tools give the agent room to compose; a few high-level workflow tools cut steps for the common path. Most good servers ship both.
|
|
35
|
+
|
|
36
|
+
**3. Implement.** Shared infrastructure first (client, auth, pagination, error handling), then the tools on top. If the server touches a database, it follows the same kit rules as everything else: one shared client, data access through the adapter, StrictDB if installed otherwise the native driver, never Mongoose.
|
|
37
|
+
|
|
38
|
+
**4. Test with evaluations.** Write 10 realistic, multi-step questions a user would ask, then verify an LLM can answer each using only the tools. Solve each yourself first so you know the right answer. Failing evals point at a missing tool, a vague description, or output the model can't parse, fix those, not the eval.
|
|
39
|
+
|
|
40
|
+
## Project shape
|
|
41
|
+
|
|
42
|
+
src/
|
|
43
|
+
server.ts transport + registration, thin
|
|
44
|
+
client.ts the upstream API/service client (the one place it's touched)
|
|
45
|
+
tools/ one file per tool or tight tool group
|
|
46
|
+
schemas/ input/output schemas (zod)
|
|
47
|
+
evals/
|
|
48
|
+
evals.json the 10 questions and verified answers
|
|
49
|
+
|
|
50
|
+
Keep `server.ts` thin, the same discipline as `api-conventions`: it wires transport and registers tools, nothing more. Tool logic lives in `tools/`, upstream calls in `client.ts`. No file over 300 lines.
|
|
51
|
+
|
|
52
|
+
## What to avoid
|
|
53
|
+
|
|
54
|
+
- Dumping every endpoint as a tool with no workflow shaping.
|
|
55
|
+
- Tool names only the author understands.
|
|
56
|
+
- Returning raw upstream JSON the model has to guess the shape of.
|
|
57
|
+
- Skipping evals, an MCP server you haven't watched an LLM drive is untested.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mongodb-rules
|
|
3
|
+
description: Native-driver, StrictDB, and data-modeling rules for MongoDB. Use whenever writing or modifying queries, writes, indexes, aggregations, upserts, connections, or schema against MongoDB, or any code that touches the data layer. Loads inline so it shapes code as it's written.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# MongoDB Data-Access Rules
|
|
7
|
+
|
|
8
|
+
Non-negotiable for this codebase. From production, not preference.
|
|
9
|
+
|
|
10
|
+
## Driver and connection
|
|
11
|
+
|
|
12
|
+
- **Prefer StrictDB; fall back to the native driver.** Use StrictDB when it's installed, otherwise the native MongoDB driver directly. Never Mongoose, either way.
|
|
13
|
+
- **Data access stays in the adapter,** never raw collections scattered through feature code.
|
|
14
|
+
- **One shared `MongoClient` for the whole app**, created once at startup and reused everywhere. The client *is* the pool. Never create one per request, that exhausts connections and looks like "MongoDB went down" under load. In serverless, declare the client at module scope, outside the handler, so warm invocations reuse it.
|
|
15
|
+
- **URI comes from an env var,** never hardcoded. Close the client on `SIGTERM`/`SIGINT`.
|
|
16
|
+
|
|
17
|
+
## Identity and types
|
|
18
|
+
|
|
19
|
+
- **`_id` is an `ObjectId`, not a string.** A string `_id` against an `ObjectId` document matches nothing, silently. This is the number-one "my id query returns nothing" bug. Wrap incoming ids with `new ObjectId(id)`; for an `$in` list, convert every element.
|
|
20
|
+
- **Never put `_id` in the body of any write.** Identity goes in the filter; on insert MongoDB generates it. Restating it after a JSON round-trip throws code 66 (immutable field altered).
|
|
21
|
+
- **JSON strips both `ObjectId` and `Date` to strings.** Re-hydrate at the boundary before you query or save, or lookups match nothing and date filters break. Use a parse-time reviver (`parseMongo`) or a bounded post-parse walk (`rehydrateTypes`, `maxDepth=3`). Never patch the global `JSON.parse`. Guard with `ObjectId.isValid` and a `skipKeys` list.
|
|
22
|
+
- **Consider a natural string `_id`** (email, SKU, slug) to sidestep the type dance entirely, then ids are strings end to end.
|
|
23
|
+
- **Only `_id` is indexed by default.** Sorting by `_id` gives roughly creation order for free, but store an explicit `createdAt` when creation time matters; don't rely on the ObjectId's embedded timestamp.
|
|
24
|
+
|
|
25
|
+
## Querying
|
|
26
|
+
|
|
27
|
+
- **`null` is not "missing".** `{ field: null }` matches both explicit null AND absent documents. Use `{ field: { $exists: false } }` for missing, and `{ field: { $ne: null } }` for "has a real value" (which excludes both null and missing).
|
|
28
|
+
- **Reads are aggregation pipelines, not `find()`.**
|
|
29
|
+
- **Arrays-within-arrays aren't queryable past about three levels** (`maxDepth=3`). If you need to, the model is wrong, flatten it.
|
|
30
|
+
|
|
31
|
+
## Writes
|
|
32
|
+
|
|
33
|
+
- **Multi-document writes use `bulkWrite`.** Build the ops array in the loop, execute one `bulkWrite` after it. Never call the database inside a loop.
|
|
34
|
+
- **Choose ordered vs unordered deliberately.** `ordered` (default) stops at the first error; `{ ordered: false }` attempts all and collects errors, and is faster. On partial failure, triage transient vs permanent: retry only transient ops with backoff, never blind-retry a duplicate-key or validation failure.
|
|
35
|
+
- **Make writes idempotent** (stable keys, set-to-value over blind increments) so retry is safe.
|
|
36
|
+
- **Upsert is `updateOne` with `upsert: true`.** Use `$setOnInsert` for insert-only fields, and pair it with a unique index so concurrent upserts can't duplicate.
|
|
37
|
+
|
|
38
|
+
## Indexing
|
|
39
|
+
|
|
40
|
+
- **`createIndex` is idempotent but not free**, each call is a round trip. Declare indexes near the queries that need them, but create them once in startup or a migration, never in a handler or hot loop.
|
|
41
|
+
- **A compound index serves only a left-to-right prefix of its fields.** Order fields to match how you query; equality-matched fields lead.
|
|
42
|
+
- **For filter-plus-sort, follow ESR: Equality, Sort, Range.** Index direction must match the sort or be its exact inverse. An in-memory `SORT` stage in `explain` means the index isn't serving the sort.
|
|
43
|
+
- **Filter early** (`$match` first), and put `$limit` before `$lookup`. Diagnose with `explain("executionStats")`: examined far exceeding returned means a missing index.
|
|
44
|
+
|
|
45
|
+
## Modeling
|
|
46
|
+
|
|
47
|
+
- **Embed bounded, read-together data; reference unbounded data** (comments, events, logs, anything that accumulates). A document is rewritten and moved as a whole, so an unbounded array degrades writes long before the 16MB hard limit.
|
|
48
|
+
- **`$elemMatch` usually signals a modeling problem.** A field you query independently belongs on the document or in its own collection.
|
|
49
|
+
|
|
50
|
+
## Concurrency and integrity
|
|
51
|
+
|
|
52
|
+
- **MongoDB has full ACID transactions**, but a single-document write is already atomic. Reach for `session.withTransaction` only for genuine cross-document atomicity (transfer between accounts, decrement stock while creating an order). Frequent transactions are a modeling smell, the data probably wanted to be one document.
|
|
53
|
+
- **Unique values are enforced by a unique index, not app-level checks.** Use a partial unique index for optional fields (a plain unique index treats missing as null and collides). Use a compound unique index for "this combination is unique". The duplicate-key error is the guarantee that makes concurrent upserts safe.
|
|
54
|
+
|
|
55
|
+
## Reads at scale
|
|
56
|
+
|
|
57
|
+
- **Counting is a choice.** `countDocuments` is exact (index the filter to keep it fast); `estimatedDocumentCount` is instant but approximate and whole-collection only, use it for rough dashboard totals. `distinct` on a high-cardinality field at scale should be a `$group` so the result streams.
|
|
58
|
+
- **`$skip` doesn't scale**, it walks past every skipped document. For deep or endless paging, use range/keyset pagination: `$match` on the last-seen value of an indexed sort key, with `_id` as a unique tie-breaker so pages don't shift as data changes.
|
|
59
|
+
|
|
60
|
+
## Schema
|
|
61
|
+
|
|
62
|
+
- **Collections enforce no structure by default.** Add a `$jsonSchema` validator as a collection stabilizes and more code depends on its shape. Roll out safely: `validationAction: "warn"` to observe first, then `error`; `validationLevel: "moderate"` to spare existing nonconforming documents.
|