@coralai/sps-cli 0.42.0 → 0.44.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/README.md +59 -4
- package/dist/commands/consoleCommand.d.ts +2 -0
- package/dist/commands/consoleCommand.d.ts.map +1 -0
- package/dist/commands/consoleCommand.js +129 -0
- package/dist/commands/consoleCommand.js.map +1 -0
- package/dist/commands/projectInit.d.ts.map +1 -1
- package/dist/commands/projectInit.js +40 -53
- package/dist/commands/projectInit.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +14 -2
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/skillCommand.d.ts +2 -0
- package/dist/commands/skillCommand.d.ts.map +1 -0
- package/dist/commands/skillCommand.js +235 -0
- package/dist/commands/skillCommand.js.map +1 -0
- package/dist/console-assets/assets/index-Bhd2f9AP.js +125 -0
- package/dist/console-assets/assets/index-bsAN2a12.css +1 -0
- package/dist/console-assets/index.html +16 -0
- package/dist/console-server/index.d.ts +29 -0
- package/dist/console-server/index.d.ts.map +1 -0
- package/dist/console-server/index.js +145 -0
- package/dist/console-server/index.js.map +1 -0
- package/dist/console-server/lib/lockFile.d.ts +17 -0
- package/dist/console-server/lib/lockFile.d.ts.map +1 -0
- package/dist/console-server/lib/lockFile.js +61 -0
- package/dist/console-server/lib/lockFile.js.map +1 -0
- package/dist/console-server/lib/portPicker.d.ts +3 -0
- package/dist/console-server/lib/portPicker.d.ts.map +1 -0
- package/dist/console-server/lib/portPicker.js +25 -0
- package/dist/console-server/lib/portPicker.js.map +1 -0
- package/dist/console-server/routes/projects.d.ts +11 -0
- package/dist/console-server/routes/projects.d.ts.map +1 -0
- package/dist/console-server/routes/projects.js +149 -0
- package/dist/console-server/routes/projects.js.map +1 -0
- package/dist/console-server/routes/system.d.ts +7 -0
- package/dist/console-server/routes/system.d.ts.map +1 -0
- package/dist/console-server/routes/system.js +19 -0
- package/dist/console-server/routes/system.js.map +1 -0
- package/dist/console-server/sse/eventBus.d.ts +25 -0
- package/dist/console-server/sse/eventBus.d.ts.map +1 -0
- package/dist/console-server/sse/eventBus.js +32 -0
- package/dist/console-server/sse/eventBus.js.map +1 -0
- package/dist/console-server/watchers/cardWatcher.d.ts +9 -0
- package/dist/console-server/watchers/cardWatcher.d.ts.map +1 -0
- package/dist/console-server/watchers/cardWatcher.js +42 -0
- package/dist/console-server/watchers/cardWatcher.js.map +1 -0
- package/dist/core/skillStore.d.ts +46 -0
- package/dist/core/skillStore.d.ts.map +1 -0
- package/dist/core/skillStore.js +210 -0
- package/dist/core/skillStore.js.map +1 -0
- package/dist/core/skillStore.test.d.ts +2 -0
- package/dist/core/skillStore.test.d.ts.map +1 -0
- package/dist/core/skillStore.test.js +203 -0
- package/dist/core/skillStore.test.js.map +1 -0
- package/dist/main.js +27 -17
- package/dist/main.js.map +1 -1
- package/package.json +8 -2
- package/skills/architecture-decision-records/SKILL.md +207 -0
- package/skills/backend/SKILL.md +62 -0
- package/skills/backend/references/api-design.md +168 -0
- package/skills/backend/references/caching.md +181 -0
- package/skills/backend/references/data-access.md +173 -0
- package/skills/backend/references/layering.md +181 -0
- package/skills/backend/references/observability.md +190 -0
- package/skills/backend/references/resilience.md +201 -0
- package/skills/backend/references/security.md +186 -0
- package/skills/backend-architect/SKILL.md +119 -0
- package/skills/code-reviewer/SKILL.md +143 -0
- package/skills/coding-standards/SKILL.md +60 -0
- package/skills/coding-standards/references/clean-code.md +258 -0
- package/skills/coding-standards/references/code-review.md +192 -0
- package/skills/coding-standards/references/commits-and-prs.md +226 -0
- package/skills/coding-standards/references/error-strategy.md +193 -0
- package/skills/coding-standards/references/naming.md +185 -0
- package/skills/coding-standards/references/tdd.md +171 -0
- package/skills/database/SKILL.md +53 -0
- package/skills/database/references/indexing.md +190 -0
- package/skills/database/references/migrations.md +199 -0
- package/skills/database/references/nosql.md +185 -0
- package/skills/database/references/queries.md +295 -0
- package/skills/database/references/scaling.md +203 -0
- package/skills/database/references/schema.md +191 -0
- package/skills/database-optimizer/SKILL.md +168 -0
- package/skills/debugging-workflow/SKILL.md +244 -0
- package/skills/devops/SKILL.md +55 -0
- package/skills/devops/references/ci-cd.md +204 -0
- package/skills/devops/references/containers.md +272 -0
- package/skills/devops/references/deploy.md +201 -0
- package/skills/devops/references/iac.md +252 -0
- package/skills/devops/references/observability.md +228 -0
- package/skills/devops/references/secrets.md +178 -0
- package/skills/devops-automator/SKILL.md +164 -0
- package/skills/frontend/SKILL.md +52 -0
- package/skills/frontend/references/accessibility.md +222 -0
- package/skills/frontend/references/components.md +206 -0
- package/skills/frontend/references/performance.md +219 -0
- package/skills/frontend/references/routing.md +209 -0
- package/skills/frontend/references/state.md +190 -0
- package/skills/frontend/references/testing.md +216 -0
- package/skills/frontend-developer/SKILL.md +115 -0
- package/skills/git-workflow/SKILL.md +355 -0
- package/skills/golang/SKILL.md +49 -0
- package/skills/golang/references/concurrency.md +284 -0
- package/skills/golang/references/errors.md +241 -0
- package/skills/golang/references/idioms.md +285 -0
- package/skills/golang/references/testing.md +238 -0
- package/skills/java/SKILL.md +50 -0
- package/skills/java/references/concurrency.md +194 -0
- package/skills/java/references/idioms.md +283 -0
- package/skills/java/references/testing.md +228 -0
- package/skills/kotlin/SKILL.md +47 -0
- package/skills/kotlin/references/coroutines.md +240 -0
- package/skills/kotlin/references/idioms.md +268 -0
- package/skills/kotlin/references/testing.md +219 -0
- package/skills/mobile/SKILL.md +50 -0
- package/skills/mobile/references/architecture.md +204 -0
- package/skills/mobile/references/navigation.md +158 -0
- package/skills/mobile/references/performance.md +152 -0
- package/skills/mobile/references/platform.md +166 -0
- package/skills/mobile/references/state-and-data.md +174 -0
- package/skills/python/SKILL.md +51 -0
- package/skills/python/THIRD_PARTY.md +14 -0
- package/skills/python/references/async.md +218 -0
- package/skills/python/references/error-handling.md +254 -0
- package/skills/python/references/idioms.md +279 -0
- package/skills/python/references/packaging.md +233 -0
- package/skills/python/references/testing.md +269 -0
- package/skills/python/references/typing.md +292 -0
- package/skills/qa-tester/SKILL.md +186 -0
- package/skills/rust/SKILL.md +50 -0
- package/skills/rust/references/async.md +224 -0
- package/skills/rust/references/errors.md +240 -0
- package/skills/rust/references/ownership.md +263 -0
- package/skills/rust/references/testing.md +274 -0
- package/skills/rust/references/traits.md +250 -0
- package/skills/security-engineer/SKILL.md +157 -0
- package/skills/swift/SKILL.md +48 -0
- package/skills/swift/references/concurrency.md +280 -0
- package/skills/swift/references/idioms.md +334 -0
- package/skills/swift/references/testing.md +229 -0
- package/skills/typescript/SKILL.md +51 -0
- package/skills/typescript/references/async.md +241 -0
- package/skills/typescript/references/errors.md +208 -0
- package/skills/typescript/references/idioms.md +246 -0
- package/skills/typescript/references/testing.md +225 -0
- package/skills/typescript/references/tooling.md +208 -0
- package/skills/typescript/references/types.md +259 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-reviewer
|
|
3
|
+
description: Persona skill — review code like a senior engineer. Prioritize correctness, security, clarity over taste. Overlay on top of language + end skills. For the checklist detail, see `coding-standards/references/code-review.md`.
|
|
4
|
+
origin: agency-agents-fork + original (https://github.com/msitarzewski/agency-agents, MIT)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Code Reviewer
|
|
8
|
+
|
|
9
|
+
Review with intention. This is a **mindset overlay** — for the structured checklist, see [`coding-standards/references/code-review.md`](../coding-standards/references/code-review.md).
|
|
10
|
+
|
|
11
|
+
## When to load
|
|
12
|
+
|
|
13
|
+
- Reviewing a PR (yours or someone else's)
|
|
14
|
+
- Writing a self-review checklist before opening a PR
|
|
15
|
+
- Training a more junior reviewer (what to look for, in what order)
|
|
16
|
+
|
|
17
|
+
## The posture
|
|
18
|
+
|
|
19
|
+
1. **Correctness before style.** Lint is a machine's job. Humans find logic bugs, missing edges, bad abstractions.
|
|
20
|
+
2. **Simplicity is a feature.** Fewer moving parts = fewer bugs. Prefer the shorter correct solution.
|
|
21
|
+
3. **Review the diff, think about the system.** A clean diff that makes the system messier is a net negative.
|
|
22
|
+
4. **Comment to teach, not to score.** The author reads every comment. "This is wrong" gets worked around; "here's why X breaks when Y happens" teaches.
|
|
23
|
+
5. **Approve or block — decide.** "LGTM but…" is indecision. Say yes or no.
|
|
24
|
+
6. **Respond quickly, even partially.** "Looking at this now, initial thoughts below" beats silence.
|
|
25
|
+
7. **Trust but verify.** Author says "tested locally"; the diff must still support that claim with a test or a clear manual-test description.
|
|
26
|
+
|
|
27
|
+
## Priority order (top first)
|
|
28
|
+
|
|
29
|
+
Walk through in this order. Spend minutes on each upper item before considering the next.
|
|
30
|
+
|
|
31
|
+
1. **Understand the change.** What problem does this solve? Is this the right fix or a symptom patch? Is there a simpler approach?
|
|
32
|
+
2. **Correctness.** Happy path + edges: empty / duplicate / concurrent / partial failure. Race conditions. Order-of-operations.
|
|
33
|
+
3. **Security.** Input validation at boundary. SQL / command / template injection. Auth/authz check. Secret handling.
|
|
34
|
+
4. **Tests.** Does a test exist that would fail without this fix? Edge cases covered? Flaky patterns?
|
|
35
|
+
5. **Data / migrations.** Backward compatible with running code during deploy? Backfill safe on large tables? Reversible?
|
|
36
|
+
6. **Observability.** Enough log / metric to diagnose a failure? New alerts needed?
|
|
37
|
+
7. **Layering.** Business logic stays out of adapters. Framework types stay out of the domain.
|
|
38
|
+
8. **Style.** Names, formatting, dead code. Last.
|
|
39
|
+
|
|
40
|
+
If the formatter and linter disagree with the code, the PR shouldn't have reached you. Don't spend review time on what tooling catches.
|
|
41
|
+
|
|
42
|
+
## Comment vocabulary
|
|
43
|
+
|
|
44
|
+
Small, predictable prefixes so the author knows what blocks.
|
|
45
|
+
|
|
46
|
+
| Prefix | Meaning | Action |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `Blocker:` | Must fix before merge | Don't approve |
|
|
49
|
+
| `Question:` | I don't understand | Ask |
|
|
50
|
+
| `Suggestion:` | Consider, non-blocking | Approve anyway |
|
|
51
|
+
| `Nit:` | Style / taste | Approve |
|
|
52
|
+
| `Praise:` | This is good | Approve (and mean it) |
|
|
53
|
+
|
|
54
|
+
If you only left `Nit:` / `Suggestion:`, **approve**. Don't hold up a PR for taste.
|
|
55
|
+
|
|
56
|
+
## Good review comments
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
Blocker: This 500s when `roles` is empty (line 43 assumes at least one role).
|
|
60
|
+
Can you add a test with an empty roles list?
|
|
61
|
+
|
|
62
|
+
Question: Why retry on 401? That looks like a permanent auth failure, not transient.
|
|
63
|
+
|
|
64
|
+
Suggestion: Pull this parse block into a helper — it's duplicated in orders.py:33.
|
|
65
|
+
|
|
66
|
+
Praise: Nice refactor. Untangled what I've been worried about for months.
|
|
67
|
+
|
|
68
|
+
Nit: `usr` → `user`.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Bad review comments
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
"This is weird." ← not actionable
|
|
75
|
+
"Why would you do it this way?" ← confrontational; say what you'd prefer
|
|
76
|
+
"I would have done X." ← if X is better, ask for X
|
|
77
|
+
"FYI, there's a library for this." ← link, justify, or drop
|
|
78
|
+
Long digressions about architecture ← file a separate issue
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## What you check no matter what
|
|
82
|
+
|
|
83
|
+
- **"What happens when X is null / empty / wrong type?"** — trace each input.
|
|
84
|
+
- **"What's the failure response visible to the user / caller?"** — status code, error shape, logs.
|
|
85
|
+
- **"What's new in prod that wasn't there before?"** — new dep, new env var, new migration, new cron.
|
|
86
|
+
- **"Is anything silently caught?"** — every `catch` clause, grep for bare `except:` / `catch (e) {}`.
|
|
87
|
+
- **"Does this introduce a new coupling?"** — new import between modules that shouldn't know each other.
|
|
88
|
+
|
|
89
|
+
## What you let go
|
|
90
|
+
|
|
91
|
+
- **Personal stylistic preferences.** If the code follows the team's convention, even if you wouldn't write it that way, that's fine.
|
|
92
|
+
- **Perfection over shipping.** A good-enough change now beats a perfect one in three weeks.
|
|
93
|
+
- **Every abstraction could be prettier.** So could yours.
|
|
94
|
+
|
|
95
|
+
## Red flags to always flag
|
|
96
|
+
|
|
97
|
+
- `TODO` / `FIXME` with no owner or date.
|
|
98
|
+
- Commented-out code.
|
|
99
|
+
- Tests with no assertions (or a single `assertTrue(true)`).
|
|
100
|
+
- `console.log` / `print` left in.
|
|
101
|
+
- Catch-all exception handlers that don't log or re-raise.
|
|
102
|
+
- Hard-coded secrets / IPs / URLs.
|
|
103
|
+
- New dependencies not justified in the PR description.
|
|
104
|
+
- Huge diffs that mix refactor and behaviour change.
|
|
105
|
+
- `any` / `dynamic` / `interface{}` in typed code without comment.
|
|
106
|
+
- Changes to shared utilities without review from those utilities' owners.
|
|
107
|
+
|
|
108
|
+
## Size discipline
|
|
109
|
+
|
|
110
|
+
| Diff size | What to do |
|
|
111
|
+
|---|---|
|
|
112
|
+
| < 100 lines | Thorough review |
|
|
113
|
+
| 100–400 | Careful review |
|
|
114
|
+
| 400–1000 | Skim; ask to split |
|
|
115
|
+
| 1000+ | Send back: split this |
|
|
116
|
+
|
|
117
|
+
A large PR that's rubber-stamped is worse than no review.
|
|
118
|
+
|
|
119
|
+
## Review response time
|
|
120
|
+
|
|
121
|
+
- First response within one working day.
|
|
122
|
+
- Partial response early is better than silent perfect response.
|
|
123
|
+
- Blocking a PR for days with no reason is a failure of the reviewer.
|
|
124
|
+
|
|
125
|
+
## When to push for changes vs. accept
|
|
126
|
+
|
|
127
|
+
Push when:
|
|
128
|
+
- Correctness / security concern.
|
|
129
|
+
- Architecture drift that compounds (a new bad pattern that will be copied).
|
|
130
|
+
- Tests missing for a non-trivial change.
|
|
131
|
+
|
|
132
|
+
Accept when:
|
|
133
|
+
- Small stylistic preferences.
|
|
134
|
+
- "I would have done it differently" (without concrete "better" reason).
|
|
135
|
+
- Refactor opportunities not on the change's path.
|
|
136
|
+
|
|
137
|
+
Follow up separately for the accept cases. Don't use PR review as the lever for every idea you've ever had.
|
|
138
|
+
|
|
139
|
+
## Pair with
|
|
140
|
+
|
|
141
|
+
- [`coding-standards`](../coding-standards/SKILL.md) — principles and checklists.
|
|
142
|
+
- The relevant language skill for the language being reviewed.
|
|
143
|
+
- [`backend`](../backend/SKILL.md) / [`frontend`](../frontend/SKILL.md) — the domain of what's being reviewed.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coding-standards
|
|
3
|
+
description: Cross-language engineering principles — TDD, naming, error strategy, code review, commits/PRs, clean code. Load alongside any language / end skill. Principles only; language-specific syntax lives in the language skill.
|
|
4
|
+
origin: ecc-fork + original (https://github.com/affaan-m/everything-claude-code, MIT)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Coding Standards
|
|
8
|
+
|
|
9
|
+
Language- and stack-neutral engineering principles. Covers the "what" and "why"; language skills cover the "how".
|
|
10
|
+
|
|
11
|
+
## When to load
|
|
12
|
+
|
|
13
|
+
- Any coding task (load this + a language skill + an end skill)
|
|
14
|
+
- Code review
|
|
15
|
+
- Writing tests
|
|
16
|
+
- Opening a PR
|
|
17
|
+
- Deciding where error handling / retries / logging live
|
|
18
|
+
|
|
19
|
+
## Hierarchy of rules
|
|
20
|
+
|
|
21
|
+
1. **Correctness** — the code does what's intended
|
|
22
|
+
2. **Readability** — the next person (or you, six months later) can follow it
|
|
23
|
+
3. **Testability** — behavior is verifiable without the universe
|
|
24
|
+
4. **Simplicity** — least code and least abstraction that meet 1–3
|
|
25
|
+
5. **Performance** — only after 1–4 are met
|
|
26
|
+
|
|
27
|
+
In that order. A fast bug is worse than a slow correct answer.
|
|
28
|
+
|
|
29
|
+
## Core commitments
|
|
30
|
+
|
|
31
|
+
- **Tests first** for anything non-trivial. Write a failing test, make it pass, refactor.
|
|
32
|
+
- **Specific exceptions only.** Never `except:` bare. Never swallow without logging.
|
|
33
|
+
- **Name things for intent, not implementation.** `users_over_18` > `filtered_list_2`.
|
|
34
|
+
- **Small functions that do one thing.** If you can't say what it does in one sentence, split it.
|
|
35
|
+
- **No comments that restate the code.** Comments explain the *why*, never the *what*.
|
|
36
|
+
- **No speculative abstractions.** Two similar snippets: duplicate. Three: consider abstracting.
|
|
37
|
+
- **PRs under 400 lines of diff.** Anything larger, split.
|
|
38
|
+
|
|
39
|
+
## How to use references
|
|
40
|
+
|
|
41
|
+
| Reference | When to load |
|
|
42
|
+
|---|---|
|
|
43
|
+
| [`references/tdd.md`](references/tdd.md) | Starting a feature, writing tests, unclear what "done" means |
|
|
44
|
+
| [`references/naming.md`](references/naming.md) | Naming a function, variable, module, endpoint, feature flag |
|
|
45
|
+
| [`references/error-strategy.md`](references/error-strategy.md) | Deciding whether to raise, return a result, log, or swallow |
|
|
46
|
+
| [`references/code-review.md`](references/code-review.md) | Reviewing a PR (yours or someone else's) |
|
|
47
|
+
| [`references/commits-and-prs.md`](references/commits-and-prs.md) | Writing a commit message; opening / sizing a PR |
|
|
48
|
+
| [`references/clean-code.md`](references/clean-code.md) | Function shape, DRY vs WET, comments, dead code, magic numbers |
|
|
49
|
+
|
|
50
|
+
## Forbidden patterns (auto-reject)
|
|
51
|
+
|
|
52
|
+
- Commented-out code committed to main
|
|
53
|
+
- `TODO` without an owner or date
|
|
54
|
+
- Tests that don't actually assert anything
|
|
55
|
+
- Mutable global state for business logic
|
|
56
|
+
- Dead code kept "just in case"
|
|
57
|
+
- Magic numbers / strings without named constants
|
|
58
|
+
- Catch-all error handlers without re-raise or specific recovery
|
|
59
|
+
- Abstractions that have exactly one implementation
|
|
60
|
+
- PRs that mix a refactor with a feature change
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# Clean Code
|
|
2
|
+
|
|
3
|
+
Function shape, DRY vs WET, comments, dead code, magic numbers. Language-neutral hygiene.
|
|
4
|
+
|
|
5
|
+
## Functions
|
|
6
|
+
|
|
7
|
+
### One thing
|
|
8
|
+
|
|
9
|
+
A function should do one thing, at one level of abstraction. If the function's name needs "and", split it.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
# ❌
|
|
13
|
+
def validate_and_save_user(user): ...
|
|
14
|
+
|
|
15
|
+
# ✅
|
|
16
|
+
def validate(user): ...
|
|
17
|
+
def save(user): ...
|
|
18
|
+
|
|
19
|
+
def register_user(user):
|
|
20
|
+
validate(user)
|
|
21
|
+
save(user)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Small
|
|
25
|
+
|
|
26
|
+
Rough targets, not dogma:
|
|
27
|
+
- Functions ≤ 20 lines most of the time.
|
|
28
|
+
- If a function has three indentation levels, consider extracting.
|
|
29
|
+
- A function you can't see in one screen is a warning sign.
|
|
30
|
+
|
|
31
|
+
Exceptions: a switch-like dispatch that's naturally long is fine. A 200-line function because you kept adding cases is not.
|
|
32
|
+
|
|
33
|
+
### Arguments — few
|
|
34
|
+
|
|
35
|
+
Zero is best. One is fine. Two is OK. Three is suspicious. Four is a refactor.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
# ❌
|
|
39
|
+
create_user(name, email, age, role, active, parent_id, billing_addr, shipping_addr, ...)
|
|
40
|
+
|
|
41
|
+
# ✅ — group related args
|
|
42
|
+
create_user(identity, profile, permissions)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Boolean flags doubly so:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
# ❌
|
|
49
|
+
render_page(user, is_admin, is_preview, skip_cache)
|
|
50
|
+
|
|
51
|
+
# ✅ — flags mean you're doing two things in one function
|
|
52
|
+
render_page_for_admin(user, options)
|
|
53
|
+
render_preview_page(user)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Return one type
|
|
57
|
+
|
|
58
|
+
If a function can return a `User`, or `None`, or a string error code, pick one contract. Use `Optional` or a result type for "success or not found". Don't overload the return to mean three different things.
|
|
59
|
+
|
|
60
|
+
### Pure where possible
|
|
61
|
+
|
|
62
|
+
Pure = same input produces same output, no side effects. Pure code is trivial to test and reason about. Push side effects (I/O, state mutation, logging) to the edges.
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
# core: pure
|
|
66
|
+
def calculate_price(items, promo): ...
|
|
67
|
+
|
|
68
|
+
# edge: side effects
|
|
69
|
+
def checkout(items, promo):
|
|
70
|
+
price = calculate_price(items, promo)
|
|
71
|
+
payment = charge(price)
|
|
72
|
+
db.save_order(items, price, payment.id)
|
|
73
|
+
eventBus.publish(OrderPlaced(...))
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## DRY vs WET — know the ratio
|
|
77
|
+
|
|
78
|
+
| Situation | Do |
|
|
79
|
+
|---|---|
|
|
80
|
+
| Two similar snippets | **Duplicate**. Too early to know the shape. |
|
|
81
|
+
| Three similar snippets | Consider abstracting — if the shape is clear. |
|
|
82
|
+
| Four+ similar snippets | Abstract; you've learned what varies. |
|
|
83
|
+
|
|
84
|
+
Bad abstractions cost more than duplication. A wrong shape locks every caller into a detour forever.
|
|
85
|
+
|
|
86
|
+
Rule: abstract when the shape is obvious, not when the count reaches a number.
|
|
87
|
+
|
|
88
|
+
## Comments
|
|
89
|
+
|
|
90
|
+
Default: don't write one. If you do, explain *why*, never *what*.
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
# ❌ noise
|
|
94
|
+
i += 1 # increment i
|
|
95
|
+
|
|
96
|
+
def save(user):
|
|
97
|
+
# saves a user
|
|
98
|
+
db.insert(user)
|
|
99
|
+
|
|
100
|
+
# ✅ non-obvious why
|
|
101
|
+
# Upstream API returns 503 for ~500ms during leader election;
|
|
102
|
+
# retry window tuned from their SRE post, not ours.
|
|
103
|
+
sleep(0.6)
|
|
104
|
+
|
|
105
|
+
# HACK: patch until [TICKET-481] lifts the 100-col DB constraint.
|
|
106
|
+
name = name[:100]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Delete comments that:
|
|
110
|
+
- Restate the code (`# loop over items`)
|
|
111
|
+
- Restate the function name (`# validates input`)
|
|
112
|
+
- Are out of date (the code changed; the comment didn't)
|
|
113
|
+
- Reference past states (`# was previously doing X`)
|
|
114
|
+
|
|
115
|
+
Git remembers history. Code is the present tense.
|
|
116
|
+
|
|
117
|
+
### Docstrings
|
|
118
|
+
|
|
119
|
+
For public functions/classes. Describe contract, not implementation.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
# ✅
|
|
123
|
+
"""Return the canonical URL for this resource, or None if it isn't published."""
|
|
124
|
+
|
|
125
|
+
# ❌
|
|
126
|
+
"""This function takes a resource and returns its URL by looking up the slug in the DB."""
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
One sentence is often enough. Longer docstrings: inputs, outputs, errors, side effects.
|
|
130
|
+
|
|
131
|
+
## Dead code — delete it
|
|
132
|
+
|
|
133
|
+
If a function, class, or branch is unreachable, remove it. "Just in case" is not a reason to keep dead code.
|
|
134
|
+
|
|
135
|
+
- Unused imports → remove
|
|
136
|
+
- Commented-out code → remove (git has it)
|
|
137
|
+
- `if False:` branches → remove
|
|
138
|
+
- `TODO` from two years ago → remove or fix
|
|
139
|
+
|
|
140
|
+
Dead code rots. It confuses readers, breaks grep, and occasionally gets re-animated with stale assumptions.
|
|
141
|
+
|
|
142
|
+
## Magic numbers and strings
|
|
143
|
+
|
|
144
|
+
Named constants when the value has meaning. Literals are fine when they're self-explanatory.
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
# ✅ named — intent is clear
|
|
148
|
+
MAX_LOGIN_ATTEMPTS = 5
|
|
149
|
+
DEFAULT_PAGE_SIZE = 20
|
|
150
|
+
HTTP_OK = 200
|
|
151
|
+
|
|
152
|
+
# ✅ literal — self-explanatory in context
|
|
153
|
+
str[:10]
|
|
154
|
+
for i in range(3): ...
|
|
155
|
+
x * 2
|
|
156
|
+
|
|
157
|
+
# ❌ magic
|
|
158
|
+
if user.attempts > 5: ...
|
|
159
|
+
if status == "pa": # what's "pa"?
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Early return over nested conditions
|
|
163
|
+
|
|
164
|
+
Flatter reads better than deeply nested `if`s.
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
# ❌ pyramid of doom
|
|
168
|
+
def process(user):
|
|
169
|
+
if user is not None:
|
|
170
|
+
if user.is_active:
|
|
171
|
+
if user.has_permission():
|
|
172
|
+
if user.payment_method_valid():
|
|
173
|
+
return do_thing(user)
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
# ✅ guard clauses, early returns
|
|
177
|
+
def process(user):
|
|
178
|
+
if user is None: return None
|
|
179
|
+
if not user.is_active: return None
|
|
180
|
+
if not user.has_permission(): return None
|
|
181
|
+
if not user.payment_method_valid(): return None
|
|
182
|
+
return do_thing(user)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Avoid mutable global state
|
|
186
|
+
|
|
187
|
+
Global mutables are action at a distance. A test sets a flag → unrelated tests fail.
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
# ❌
|
|
191
|
+
_CURRENT_USER = None
|
|
192
|
+
|
|
193
|
+
def set_user(u): global _CURRENT_USER; _CURRENT_USER = u
|
|
194
|
+
def current(): return _CURRENT_USER
|
|
195
|
+
|
|
196
|
+
# ✅ pass explicitly, or use a request-scoped context
|
|
197
|
+
def handler(req, user):
|
|
198
|
+
current_user = resolve_user(req)
|
|
199
|
+
...
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Request-scoped context objects are fine (HTTP request context, trace context). Globals for business data are not.
|
|
203
|
+
|
|
204
|
+
## Avoid clever one-liners
|
|
205
|
+
|
|
206
|
+
If a line needs 30 seconds to parse, it costs every future reader 30 seconds.
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
# ❌
|
|
210
|
+
return [next((v for v in xs if p(v, k)), default) for k in sorted({f(x) for x in data} - skip)]
|
|
211
|
+
|
|
212
|
+
# ✅
|
|
213
|
+
keys = {f(x) for x in data} - skip
|
|
214
|
+
out = []
|
|
215
|
+
for k in sorted(keys):
|
|
216
|
+
match = next((v for v in xs if p(v, k)), default)
|
|
217
|
+
out.append(match)
|
|
218
|
+
return out
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Density ≠ clarity. A loop you can read in one pass beats a comprehension you have to puzzle out.
|
|
222
|
+
|
|
223
|
+
## Prefer immutability
|
|
224
|
+
|
|
225
|
+
Where the language supports it, prefer immutable values.
|
|
226
|
+
|
|
227
|
+
- Makes reasoning local (a value can't change out from under you)
|
|
228
|
+
- Safer in concurrent code
|
|
229
|
+
- Better cache keys, better log entries
|
|
230
|
+
|
|
231
|
+
Use mutation for internal data structures when performance matters. Don't propagate mutation to the public API.
|
|
232
|
+
|
|
233
|
+
## Feature flags — clean up
|
|
234
|
+
|
|
235
|
+
Flags are temporary. Long-lived flags become part of the product accidentally.
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
# a few weeks after launch
|
|
239
|
+
if feature_enabled("partial_shipments"):
|
|
240
|
+
...
|
|
241
|
+
|
|
242
|
+
# six months later: rollout is done. Inline. Delete the flag.
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Track flag age. Delete dead flags. A flag that's been "on for everyone" for three months is technical debt.
|
|
246
|
+
|
|
247
|
+
## Anti-patterns
|
|
248
|
+
|
|
249
|
+
| Anti-pattern | Fix |
|
|
250
|
+
|---|---|
|
|
251
|
+
| 200-line function | Extract; each piece named for intent |
|
|
252
|
+
| Mixing abstraction levels (business rule + DB call + HTTP call) | Layer: see backend/layering |
|
|
253
|
+
| Flag arguments (`do(x, is_special=True)`) | Split into two functions |
|
|
254
|
+
| "Utils" modules | Split by domain; utils becomes a dumping ground |
|
|
255
|
+
| Deep nesting | Guard clauses, early returns |
|
|
256
|
+
| Duplicated constants across files | Promote to a shared constants module |
|
|
257
|
+
| Commented-out code "in case we need it" | Delete; git has it |
|
|
258
|
+
| Over-abstracted single-use interface | Delete the interface; use the concrete type |
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Code Review
|
|
2
|
+
|
|
3
|
+
How to review. How to be reviewed. A checklist that works regardless of language.
|
|
4
|
+
|
|
5
|
+
## What review is for
|
|
6
|
+
|
|
7
|
+
In order of importance:
|
|
8
|
+
|
|
9
|
+
1. **Catching defects** — correctness bugs, security issues, data loss paths
|
|
10
|
+
2. **Sharing knowledge** — both directions; reviewer learns too
|
|
11
|
+
3. **Maintaining consistency** — convention drift, naming, layering
|
|
12
|
+
4. **Coaching** — especially for junior → senior flow
|
|
13
|
+
5. **Rubber-stamping** — never the goal, though sometimes the outcome
|
|
14
|
+
|
|
15
|
+
Review is NOT:
|
|
16
|
+
- A style argument you could have automated (use a formatter)
|
|
17
|
+
- A place to re-litigate architecture decisions already made
|
|
18
|
+
- An opportunity to impose personal preferences as requirements
|
|
19
|
+
|
|
20
|
+
## Reviewer checklist
|
|
21
|
+
|
|
22
|
+
Go through in this order. Don't start with nits.
|
|
23
|
+
|
|
24
|
+
### 1. Understand the change
|
|
25
|
+
|
|
26
|
+
- What problem does this solve? (Read the PR description; if it's empty, push back.)
|
|
27
|
+
- Is this the right fix, or is it treating a symptom?
|
|
28
|
+
- Is there a simpler approach that also works?
|
|
29
|
+
|
|
30
|
+
### 2. Correctness
|
|
31
|
+
|
|
32
|
+
- Does it handle the happy path?
|
|
33
|
+
- What about the edges: empty inputs, duplicates, concurrency, partial failures?
|
|
34
|
+
- What happens on error? Is the error propagation / handling at the right layer?
|
|
35
|
+
- Are there new race conditions? Deadlocks? Order-of-operations assumptions?
|
|
36
|
+
|
|
37
|
+
### 3. Security
|
|
38
|
+
|
|
39
|
+
- Any user input that isn't validated at the boundary?
|
|
40
|
+
- Any SQL / command / template construction from strings?
|
|
41
|
+
- Any auth/authz check missing? Any leak of existence / enumeration?
|
|
42
|
+
- Any new secret / credential in code or config?
|
|
43
|
+
|
|
44
|
+
### 4. Tests
|
|
45
|
+
|
|
46
|
+
- Is there a test for the change? (No test on non-trivial change = push back.)
|
|
47
|
+
- Does the test actually fail without the fix? (Reviewer can mentally check this.)
|
|
48
|
+
- Are edge cases tested, not just happy path?
|
|
49
|
+
- Any flaky patterns: sleeps, network calls, order dependencies?
|
|
50
|
+
|
|
51
|
+
### 5. Data and migrations
|
|
52
|
+
|
|
53
|
+
- Schema change: is it backward compatible with the running code during deploy?
|
|
54
|
+
- Backfill strategy: safe on large tables? rate-limited?
|
|
55
|
+
- Is this reversible? If not, is the forward-only plan documented?
|
|
56
|
+
|
|
57
|
+
### 6. Observability
|
|
58
|
+
|
|
59
|
+
- Does the new code log errors with enough context (request id, user id)?
|
|
60
|
+
- Are new metrics / dashboards needed? Alerts?
|
|
61
|
+
- Can an oncall diagnose a failure from what's logged?
|
|
62
|
+
|
|
63
|
+
### 7. Layering & design
|
|
64
|
+
|
|
65
|
+
- Does business logic stay out of adapters? Do adapters stay out of the domain?
|
|
66
|
+
- Any new leakage: framework types into domain, DB rows into API responses?
|
|
67
|
+
- Any abstraction that has exactly one implementation? (Likely premature.)
|
|
68
|
+
|
|
69
|
+
### 8. Style (last)
|
|
70
|
+
|
|
71
|
+
- Formatter run? Linter clean?
|
|
72
|
+
- Names clear?
|
|
73
|
+
- Any comments that should be removed / added?
|
|
74
|
+
|
|
75
|
+
If the formatter and linter disagree with the code, the PR shouldn't have reached human review.
|
|
76
|
+
|
|
77
|
+
## How to comment
|
|
78
|
+
|
|
79
|
+
Use a small vocabulary so the author knows what's blocking.
|
|
80
|
+
|
|
81
|
+
| Prefix | Meaning | Action |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `Blocker:` | Must fix before merge | Don't approve |
|
|
84
|
+
| `Question:` | I don't understand; please explain | Ask |
|
|
85
|
+
| `Suggestion:` | Consider, non-blocking | Approve anyway |
|
|
86
|
+
| `Nit:` | Style / taste, truly optional | Approve |
|
|
87
|
+
| `Praise:` | This is good; say it | Approve (and mean it) |
|
|
88
|
+
|
|
89
|
+
If you only leave `Nit:` and `Suggestion:`, approve. Don't hold up a PR for taste.
|
|
90
|
+
|
|
91
|
+
## Good comments
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Blocker: This will 500 when `user.roles` is empty (line 43 assumes at least one).
|
|
95
|
+
Can you add a test with an empty roles list?
|
|
96
|
+
|
|
97
|
+
Suggestion: Pull this 3-line parse block into a helper — it's duplicated in
|
|
98
|
+
handlers/orders.py too.
|
|
99
|
+
|
|
100
|
+
Question: Why are we retrying on 401? That looks like the credential is wrong,
|
|
101
|
+
not transient.
|
|
102
|
+
|
|
103
|
+
Nit: `usr` → `user`.
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Bad comments
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
"This is weird." ← not actionable
|
|
110
|
+
"Why would you do it this way?" ← confrontational; say what you'd prefer
|
|
111
|
+
"I would have done X." ← if X is better, ask for X; otherwise irrelevant
|
|
112
|
+
"FYI, there's a library for this." ← link, justify, or drop it
|
|
113
|
+
Long digressions about architecture ← file a separate issue
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Reviewing size
|
|
117
|
+
|
|
118
|
+
| Diff size | Typical quality of review |
|
|
119
|
+
|---|---|
|
|
120
|
+
| < 100 lines | Thorough |
|
|
121
|
+
| 100–400 | Careful |
|
|
122
|
+
| 400–1000 | Skimmed; defects slip |
|
|
123
|
+
| > 1000 | Rubber stamp |
|
|
124
|
+
|
|
125
|
+
If the PR is > 400 lines and not refactor-only (e.g., rename), ask the author to split. Reviewers can't do their job on huge diffs.
|
|
126
|
+
|
|
127
|
+
## Review speed
|
|
128
|
+
|
|
129
|
+
- Aim to first-respond within one working day.
|
|
130
|
+
- Incremental responses are fine: "looking at it now, initial thoughts below".
|
|
131
|
+
- Blocking a PR for days with no reason is a failure mode.
|
|
132
|
+
|
|
133
|
+
## Being reviewed
|
|
134
|
+
|
|
135
|
+
Make the reviewer's job easy.
|
|
136
|
+
|
|
137
|
+
### The PR description
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
## What
|
|
141
|
+
One-line summary of the change.
|
|
142
|
+
|
|
143
|
+
## Why
|
|
144
|
+
Link to ticket / incident. Business / technical reason.
|
|
145
|
+
|
|
146
|
+
## How
|
|
147
|
+
Brief description of the approach and why this approach.
|
|
148
|
+
|
|
149
|
+
## Tests
|
|
150
|
+
What was added / run locally. Any manual verification.
|
|
151
|
+
|
|
152
|
+
## Risks / rollout
|
|
153
|
+
Feature flag? Migration? Rollback plan?
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Split your PRs
|
|
157
|
+
|
|
158
|
+
- **Refactor** (no behavior change) and **feature** (behavior change) in separate PRs.
|
|
159
|
+
- **Rename** PRs are mechanical — reviewer should only need to confirm "nothing else changed".
|
|
160
|
+
- **Dependency bumps** separate from feature work.
|
|
161
|
+
|
|
162
|
+
### Respond to all comments
|
|
163
|
+
|
|
164
|
+
- `Fixed.` (with the follow-up commit ref)
|
|
165
|
+
- `Good catch, added a test for that too.`
|
|
166
|
+
- `I see it differently — <reason>. Want to discuss on a call?`
|
|
167
|
+
|
|
168
|
+
Don't leave comments unanswered. "Ignored" on a review is how reviewers stop reviewing carefully.
|
|
169
|
+
|
|
170
|
+
### Don't take it personally
|
|
171
|
+
|
|
172
|
+
Code is being reviewed, not you. "This is wrong" is about the line, not your worth. If a comment does feel personal, ask the reviewer offline — don't escalate in the PR.
|
|
173
|
+
|
|
174
|
+
## Approving
|
|
175
|
+
|
|
176
|
+
- **Approve** when you'd be comfortable being paged at 3am because of this code.
|
|
177
|
+
- **Request changes** when there are blockers.
|
|
178
|
+
- **Comment** (neither approve nor block) when you've given feedback but someone more authoritative should approve.
|
|
179
|
+
|
|
180
|
+
Don't approve-and-block ("LGTM but…"). Decide.
|
|
181
|
+
|
|
182
|
+
## Anti-patterns
|
|
183
|
+
|
|
184
|
+
| Anti-pattern | Why |
|
|
185
|
+
|---|---|
|
|
186
|
+
| Review-bombing with 50 nits | Exhausts the author; misses the real issue |
|
|
187
|
+
| Approving without reading | Wastes the review; defects sneak through |
|
|
188
|
+
| Re-litigating the design at review time | Should have been a design doc earlier |
|
|
189
|
+
| "Can you just rewrite this?" | If true, say specifically why; offer an example |
|
|
190
|
+
| Ignoring your own review comments later | Inconsistent; erodes trust |
|
|
191
|
+
| Big PR → "looks good" after 5 min | Not an actual review |
|
|
192
|
+
| Review as gate without shared standards | Reviewer's mood decides the outcome |
|