codecall 1.3.0 → 1.4.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.
Files changed (23) hide show
  1. package/README.md +22 -0
  2. package/package.json +1 -1
  3. package/plugins/codecall/.claude-plugin/plugin.json +1 -1
  4. package/plugins/codecall/.codex-plugin/plugin.json +1 -1
  5. package/plugins/codecall/agents/codecall-planner.md +6 -1
  6. package/plugins/codecall/skills/codecall/SKILL.md +20 -3
  7. package/plugins/codecall/skills/codecall/references/backend-map/api.md +113 -0
  8. package/plugins/codecall/skills/codecall/references/backend-map/architecture.md +65 -0
  9. package/plugins/codecall/skills/codecall/references/backend-map/async.md +81 -0
  10. package/plugins/codecall/skills/codecall/references/backend-map/auth.md +97 -0
  11. package/plugins/codecall/skills/codecall/references/backend-map/caching.md +65 -0
  12. package/plugins/codecall/skills/codecall/references/backend-map/data.md +144 -0
  13. package/plugins/codecall/skills/codecall/references/backend-map/http.md +97 -0
  14. package/plugins/codecall/skills/codecall/references/backend-map/index.md +165 -0
  15. package/plugins/codecall/skills/codecall/references/backend-map/observability.md +65 -0
  16. package/plugins/codecall/skills/codecall/references/backend-map/ops.md +129 -0
  17. package/plugins/codecall/skills/codecall/references/backend-map/quality.md +81 -0
  18. package/plugins/codecall/skills/codecall/references/backend-map/realtime.md +65 -0
  19. package/plugins/codecall/skills/codecall/references/backend-map/reliability.md +65 -0
  20. package/plugins/codecall/skills/codecall/references/backend-map/scaling.md +81 -0
  21. package/plugins/codecall/skills/codecall/references/backend-map/security.md +97 -0
  22. package/plugins/codecall/skills/codecall/references/backend-map/storage.md +81 -0
  23. package/plugins/codecall/skills/codecall/references/receipt-template.html +7 -3
package/README.md CHANGED
@@ -94,6 +94,21 @@ ideas, nothing else: no time-on-task, no mastery claim, no guess at what you
94
94
  would have known anyway. Earlier receipts in `./codecall/` add a streak line
95
95
  (sessions this week, running explain rate) and nothing more.
96
96
 
97
+ ## Backend concept map
98
+
99
+ For server-side changes, codecall uses a small built-in map of about 80
100
+ backend concepts: HTTP, auth, databases, caching, queues, webhooks, real-time,
101
+ storage, testing and delivery. It gives concepts stable names, orders them by
102
+ what you need to know first, and supplies known misconceptions. You never see
103
+ the map. If a change matches nothing in it, codecall works exactly as before.
104
+
105
+ When you miss a mapped concept, the receipt adds one **Go deeper** line: the
106
+ matching chapter of Sriniously's free
107
+ [Backend from first principles](https://www.youtube.com/playlist?list=PLui3EUkuMTPgZcV0QhQrOcwMPcBCcd_Q1)
108
+ series, plus an official reference (MDN, OWASP, PostgreSQL and similar). The
109
+ map's wording is codecall's own and links to the videos; it does not copy
110
+ them. codecall is not affiliated with or endorsed by the series' creator.
111
+
97
112
  ## Privacy
98
113
 
99
114
  - No API key, no account, no signup, no network call.
@@ -128,6 +143,13 @@ npm test
128
143
  claude plugin validate ./plugins/codecall
129
144
  ```
130
145
 
146
+ The backend concept map lives in
147
+ `plugins/codecall/skills/codecall/references/backend-map/`, one Markdown file
148
+ per cluster. Edit those files, then run `node scripts/backend-map.mjs` to
149
+ validate them and regenerate `index.md`; `npm test` fails on a stale index, a
150
+ broken prerequisite, a timestamp that is not a real chapter start, or a link
151
+ outside the documentation allowlist.
152
+
131
153
  The canonical skill lives in `plugins/codecall/skills/codecall/`. The
132
154
  TypeScript runtime in `src/` is an offline, deterministic test harness for the
133
155
  session state machine, not the product; see [ARCHITECTURE.md](ARCHITECTURE.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codecall",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Implementation-grounded adaptive learning skills for Codex and Claude Code.",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codecall",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Adaptive implementation-grounded learning for Claude Code.",
5
5
  "author": {
6
6
  "name": "Gaurav",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codecall",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Adaptive implementation-grounded learning for Codex.",
5
5
  "author": {
6
6
  "name": "Gaurav",
@@ -40,6 +40,11 @@ your starting evidence — you were not part of the conversation that built it.
40
40
  anti-patterns, architecture decisions, and misconceptions distinct. Order by
41
41
  prerequisite. Do not reduce a multi-concept implementation to one
42
42
  vocabulary item.
43
+ 5. Only when the change touches server-side code, read the codecall skill's
44
+ `references/backend-map/index.md` and follow the skill's "Backend concept
45
+ map" rule: at most one map id per concept, only when it truly fits, never
46
+ forced. Order with the ids' `requires`. You may grep `./codecall/*.html`
47
+ for `codecall-concepts` meta tags to find ids missed in earlier sessions.
43
48
 
44
49
  ## Output
45
50
 
@@ -51,7 +56,7 @@ Judgment: recommend | optional | skip
51
56
  Reason: <evidence-backed reason, citing the specific file/decision/signal>
52
57
  Estimated minutes: <N>
53
58
  Plan:
54
- 1. <concept> — <one-line why this matters here> [prerequisite: none|<concept>]
59
+ 1. <concept> — <one-line why this matters here> [prerequisite: none|<concept>] [map: <id>|none]
55
60
  2. ...
56
61
  Evidence limits: <anything the given input didn't establish, if applicable>
57
62
  ```
@@ -36,6 +36,15 @@ metadata is not a background scheduler.
36
36
  uncommitted tree; never scan the whole repository. Judge by novelty,
37
37
  architecture impact, concept density, dependency depth, difficulty and
38
38
  real decisions, not line count.
39
+ - **Backend concept map:** only when the change touches server-side code
40
+ (handlers, auth, databases, caching, queues, webhooks, real-time,
41
+ storage, deployment), read
42
+ [references/backend-map/index.md](references/backend-map/index.md).
43
+ Give each planned concept at most one map id, and only when the id means
44
+ what this change actually does; leave the rest free-form and never bend a
45
+ concept to fit. Read only the cluster files holding the chosen ids. Use
46
+ their `requires` to order the plan. When earlier receipts' `codecall-concepts`
47
+ meta tags show a mapped id as missed and this change exercises it, prefer it.
39
48
  3. Show this and wait:
40
49
 
41
50
  ```text
@@ -59,7 +68,10 @@ metadata is not a background scheduler.
59
68
  3. Teach one concept: what it is, why this code needed it, what breaks without
60
69
  it, how it connects to the rest, one misconception, a concise mental model.
61
70
  Add one real-world analogy only when it clarifies, mapping each part back
62
- to the code. The analogy never replaces the technical explanation.
71
+ to the code. The analogy never replaces the technical explanation. For a
72
+ mapped concept, read its card in the cluster file first (the planner may
73
+ only have read the index); its idea, misconceptions and checks are raw
74
+ material: adapt them to this code, never recite the card.
63
75
  4. Ask two checks per concept, one at a time: first an analogy-to-code mapping,
64
76
  then an application, dependency, tradeoff or debugging question. Never a
65
77
  generic definition, never a batch.
@@ -83,13 +95,18 @@ One screen, big type, no external assets. It holds:
83
95
  - The implementation in one line, and the date.
84
96
  - Each concept marked ✅ could explain or ❌ could not, with one line of why.
85
97
  A concept counts as explained when both checks were answered correctly
86
- without a reteach.
98
+ without a reteach. A ❌ concept with a map id gets one "Go deeper" line
99
+ holding its card's links, exactly as written there; no other concept gets
100
+ links.
101
+ - A `codecall-concepts` meta tag listing each mapped concept as `id:ok` or
102
+ `id:no`, comma-separated; empty when nothing was mapped.
87
103
  - The score, `Could explain N of M`, where M is the number of concepts.
88
104
  - Three points to remember, grounded in this code.
89
105
  - The edge cases to check before reusing the pattern in another project.
90
106
  - When earlier receipts exist in `./codecall/`, one streak line read from
91
107
  their `codecall-score` and `codecall-date` meta tags: sessions in the last
92
- seven days and the running explain rate.
108
+ seven days and the running explain rate. When a concept an earlier receipt
109
+ marked `id:no` came back in this session, add its result to that line.
93
110
 
94
111
  Short identifiers only; never paste source code or conversation text into it.
95
112
 
@@ -0,0 +1,113 @@
1
+ # API design
2
+
3
+ ## routing-params
4
+
5
+ - **Name:** Routing, path and query parameters
6
+ - **Requires:** `http-messages`
7
+ - **Signals:** `/users/:id`, `req.params`, `req.query`, `r.PathValue(`, `@PathVariable`, `Query(`
8
+ - **Idea:** A router maps method plus path to a handler; path parameters identify the resource, query parameters refine the request, and both arrive as untrusted strings.
9
+ - **Misconceptions:**
10
+ - Route order never matters; many routers match first-registered, so `/users/:id` can swallow `/users/me` if declared first.
11
+ - Path params are already numbers; they are strings, so `"42"` and `42` compare differently until parsed.
12
+ - **Checks:**
13
+ - What does your handler do with `/items/abc` when it expects a numeric id, and which status reaches the client?
14
+ - Your endpoint accepts a filter; would it fit better as a path segment or a query parameter, and why?
15
+ - **Go deeper:**
16
+ - [Sriniously · Backend from first principles #6 · 4:28](https://www.youtube.com/watch?v=SubuU1iOC2s&t=268s)
17
+ - [MDN: What is a URL?](https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Web_mechanics/What_is_a_URL)
18
+
19
+ ## api-versioning
20
+
21
+ - **Name:** API versioning and deprecation
22
+ - **Requires:** `routing-params`
23
+ - **Signals:** `/api/v1/`, `/v2/`, `Deprecation` header, `Sunset` header, `Accept-Version`
24
+ - **Idea:** Versioning lets you ship breaking changes without breaking existing clients, and announced deprecation gives them time to migrate before the old version disappears.
25
+ - **Misconceptions:**
26
+ - Adding a field is breaking; additive changes are usually safe, while removing, renaming or retyping fields breaks clients.
27
+ - A new version means copying every handler; most code stays shared, with only the changed edges adapted.
28
+ - **Checks:**
29
+ - Does this change to your response shape force a new version, or can existing clients ignore it safely?
30
+ - How would your code tell old-version clients a sunset date, and how would you know who still calls it?
31
+ - **Go deeper:**
32
+ - [Sriniously · Backend from first principles #6 · 15:09](https://www.youtube.com/watch?v=SubuU1iOC2s&t=909s)
33
+ - [RFC 9745: The Deprecation HTTP Response Header Field](https://www.rfc-editor.org/rfc/rfc9745)
34
+
35
+ ## serialization-json
36
+
37
+ - **Name:** Serialization and JSON
38
+ - **Requires:** `http-messages`
39
+ - **Signals:** `JSON.stringify(`, `json.Marshal(`, `express.json()`, `@JsonProperty`, `model_dump(`, `toJSON`
40
+ - **Idea:** Serialization turns in-memory objects into bytes for the wire and back, and the chosen JSON shape becomes a public contract clients depend on.
41
+ - **Misconceptions:**
42
+ - Returning the ORM model directly is fine; it leaks internal fields like password hashes and couples clients to your schema.
43
+ - JSON numbers are safe for any integer; JavaScript loses precision above 2^53, so large ids should travel as strings.
44
+ - **Checks:**
45
+ - Which fields does your handler serialize, and could a new column added later leak to clients without anyone noticing?
46
+ - How does your code encode dates, money amounts and 64-bit ids, and would a JavaScript client read them exactly?
47
+ - **Go deeper:**
48
+ - [Sriniously · Backend from first principles #7 · 8:36](https://www.youtube.com/watch?v=vzg90tY3uM0&t=516s)
49
+ - [RFC 8259: The JSON Data Interchange Format](https://www.rfc-editor.org/rfc/rfc8259)
50
+
51
+ ## input-validation
52
+
53
+ - **Name:** Input validation and transformation
54
+ - **Requires:** `serialization-json`
55
+ - **Signals:** `z.object(`, `pydantic.BaseModel`, `@Valid`, `Joi.object(`, `validator.New(`, `class-validator`
56
+ - **Idea:** Validation checks untrusted input against an explicit schema at the boundary and transforms it into trusted types, so the rest of the code never handles malformed data.
57
+ - **Misconceptions:**
58
+ - Frontend validation is enough; anyone can call the API directly, so the server must validate everything again.
59
+ - Denylisting bad characters is safe; allowlisting expected shapes, types, lengths and ranges is far more reliable.
60
+ - **Checks:**
61
+ - If a client sends an extra `role: "admin"` field, does your validation strip it, reject it, or pass it into the database write?
62
+ - Where in your code does a string from the request become a typed value, and what happens on failure?
63
+ - **Go deeper:**
64
+ - [Sriniously · Backend from first principles #9 · 15:40](https://www.youtube.com/watch?v=qedj_JjjL-U&t=940s)
65
+ - [OWASP Cheat Sheet: Input Validation](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html)
66
+
67
+ ## rest-resource-design
68
+
69
+ - **Name:** REST resource design
70
+ - **Requires:** `routing-params`, `http-methods-idempotency`
71
+ - **Signals:** `GET /orders/{id}`, `POST /orders`, `/users/{id}/orders`, `Location` header, `201 Created`
72
+ - **Idea:** REST models the API as nouns (resources and collections) acted on by standard HTTP methods, so clients can predict endpoints and behavior without reading custom verbs.
73
+ - **Misconceptions:**
74
+ - Verbs in URLs like `/createOrder` are REST; the method carries the verb, the path names the resource.
75
+ - Every database table needs its own endpoint; resources model what clients need, not your storage layout.
76
+ - **Checks:**
77
+ - Your code adds an action like "cancel order"; would you model it as a state change on the resource or a sub-resource, and why?
78
+ - How deeply are your routes nested, and would a client need the parent id just to fetch a child it already knows?
79
+ - **Go deeper:**
80
+ - [Sriniously · Backend from first principles #11 · 52:10](https://www.youtube.com/watch?v=RG6q57DwV8Y&t=3130s)
81
+ - [Microsoft Learn: Web API design best practices](https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design)
82
+
83
+ ## pagination-filtering
84
+
85
+ - **Name:** Pagination, sorting and filtering
86
+ - **Requires:** `rest-resource-design`
87
+ - **Signals:** `LIMIT`, `OFFSET`, `?cursor=`, `starting_after`, `next_cursor`, `ORDER BY created_at, id`
88
+ - **Idea:** List endpoints return bounded pages with explicit sort and filter rules, so response size and query cost stay predictable as data grows.
89
+ - **Misconceptions:**
90
+ - OFFSET pagination scales; the database still reads and discards skipped rows, and concurrent inserts shift pages, so cursors suit large feeds.
91
+ - Sorting by a non-unique column is stable; without a tiebreaker like id, rows can repeat or vanish across pages.
92
+ - **Checks:**
93
+ - What stops a client calling your list endpoint with `limit=1000000`, and what would that do to your database?
94
+ - If a row is inserted while a client pages through your results, can it see a duplicate or miss an item?
95
+ - **Go deeper:**
96
+ - [Sriniously · Backend from first principles #11 · 1:10:18](https://www.youtube.com/watch?v=RG6q57DwV8Y&t=4218s)
97
+ - [Stripe docs: Pagination](https://docs.stripe.com/api/pagination)
98
+
99
+ ## openapi-contract
100
+
101
+ - **Name:** OpenAPI as the API contract
102
+ - **Requires:** `rest-resource-design`
103
+ - **Signals:** `openapi.yaml`, `openapi: 3.1`, `swagger-ui`, `@ApiOperation`, `components/schemas`, `/docs`
104
+ - **Idea:** An OpenAPI document describes endpoints, parameters and schemas in a machine-readable file that drives docs, client generation, mocks and contract tests from one source.
105
+ - **Misconceptions:**
106
+ - A spec written once stays accurate; unless it is generated from code or checked in CI, it drifts from the real behavior.
107
+ - OpenAPI is only documentation; the same file can validate requests and generate typed clients.
108
+ - **Checks:**
109
+ - If your handler changed a field today, what in your setup would catch that the OpenAPI spec no longer matches?
110
+ - Does your spec come from code annotations or is it written first, and what does that choice cost your team?
111
+ - **Go deeper:**
112
+ - [Sriniously · Backend from first principles #28 · 16:39](https://www.youtube.com/watch?v=CwKsU84jIWs&t=999s)
113
+ - [OpenAPI Initiative: Getting started](https://learn.openapis.org/)
@@ -0,0 +1,65 @@
1
+ # Request flow and layering
2
+
3
+ ## middleware-pipeline
4
+
5
+ - **Name:** Middleware pipeline
6
+ - **Requires:** `http-messages`
7
+ - **Signals:** `app.use(`, `next()`, `MIDDLEWARE = [`, `func(next http.Handler)`, `@app.middleware("http")`, `OncePerRequestFilter`
8
+ - **Idea:** Middleware are functions wrapped around every handler in a fixed order, handling cross-cutting work like logging, auth, parsing and error handling once instead of in each route.
9
+ - **Misconceptions:**
10
+ - Middleware order is cosmetic; auth after the router, or the error handler registered first, silently changes what runs and what is protected.
11
+ - A middleware that forgets to call `next()` just skips itself; the request hangs or ends there without reaching the handler.
12
+ - **Checks:**
13
+ - In your app, does the body parser or auth middleware run before the route that needs it, and what happens if the order flips?
14
+ - If a handler throws, which middleware in your chain turns it into a response, and does logging still record the request?
15
+ - **Go deeper:**
16
+ - [Sriniously · Backend from first principles #10 · 26:52](https://www.youtube.com/watch?v=hyc-7w3pee8&t=1612s)
17
+ - [Microsoft Learn: ASP.NET Core middleware](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/)
18
+
19
+ ## request-context
20
+
21
+ - **Name:** Request context
22
+ - **Requires:** `middleware-pipeline`
23
+ - **Signals:** `context.Context`, `r.Context()`, `res.locals`, `req.user`, `AsyncLocalStorage`, `contextvars`
24
+ - **Idea:** Request context carries per-request data such as user, request id and deadline through middleware and layers, and lets cancellation stop downstream work when the client disconnects.
25
+ - **Misconceptions:**
26
+ - Storing the current user in a module-level variable works; concurrent requests overwrite each other and one user sees another's data.
27
+ - Context is a bag for any parameter; it suits request-scoped metadata, while required business inputs belong in explicit arguments.
28
+ - **Checks:**
29
+ - If the client disconnects mid-request, does your code pass the context or signal into the database call so the query is cancelled?
30
+ - How does the request id set in your middleware reach the log lines your service and repository write?
31
+ - **Go deeper:**
32
+ - [Sriniously · Backend from first principles #10 · 50:57](https://www.youtube.com/watch?v=hyc-7w3pee8&t=3057s)
33
+ - [Go blog: Go Concurrency Patterns: Context](https://go.dev/blog/context)
34
+
35
+ ## handler-service-repository
36
+
37
+ - **Name:** Handler, service and repository layers
38
+ - **Requires:** `routing-params`
39
+ - **Signals:** `controllers/`, `services/`, `repositories/`, `@Service`, `@Repository`, `UserRepository`
40
+ - **Idea:** Handlers translate HTTP, services hold business rules, and repositories hide storage, so each layer changes for one reason and logic can run without HTTP or a database.
41
+ - **Misconceptions:**
42
+ - Layers are bureaucracy for small apps; even a thin service keeps rules out of handlers so a job or CLI can reuse them.
43
+ - Services can return HTTP status codes or take `req`; that leaks transport into business logic and blocks reuse.
44
+ - **Checks:**
45
+ - Which business rule in your handler would have to be copied if a background job needed the same behavior?
46
+ - Does your repository return database rows or domain objects, and what breaks in callers if a column is renamed?
47
+ - **Go deeper:**
48
+ - [Sriniously · Backend from first principles #10 · 18:23](https://www.youtube.com/watch?v=hyc-7w3pee8&t=1103s)
49
+ - [Martin Fowler: Presentation Domain Data Layering](https://martinfowler.com/bliki/PresentationDomainDataLayering.html)
50
+
51
+ ## dependency-injection
52
+
53
+ - **Name:** Dependency injection and a testable core
54
+ - **Requires:** `handler-service-repository`
55
+ - **Signals:** `constructor(private readonly`, `Depends(`, `@Autowired`, `@Injectable()`, `NewUserService(repo`, `wire.Build(`
56
+ - **Idea:** A component receives its collaborators (database, clock, HTTP client) from outside instead of creating them, so tests and other environments can swap in fakes without editing the code.
57
+ - **Misconceptions:**
58
+ - Dependency injection requires a framework or container; passing dependencies as constructor or function arguments is the whole idea.
59
+ - Mocking imports with patching is equivalent; it couples tests to file layout and hides how many dependencies a component really has.
60
+ - **Checks:**
61
+ - To unit test your service without a real database, what would you pass in, and does your code currently allow that?
62
+ - Where does your code call `new`, a global client, or the current time directly, and how would that make a test flaky?
63
+ - **Go deeper:**
64
+ - [Sriniously · Backend from first principles #26 · 37:24](https://www.youtube.com/watch?v=HgVrfHX92y4&t=2244s)
65
+ - [Martin Fowler: Inversion of Control Containers and the Dependency Injection pattern](https://martinfowler.com/articles/injection.html)
@@ -0,0 +1,81 @@
1
+ # Background work and webhooks
2
+
3
+ ## background-jobs
4
+
5
+ - **Name:** Background jobs and task queues
6
+ - **Requires:** none
7
+ - **Signals:** `BullMQ`, `@shared_task`, `perform_later`, `@Async`, `visibilityTimeout`, `dead-letter queue`
8
+ - **Idea:** Slow or failure-prone work like emails, image processing or third-party calls is put on a durable queue and run by separate workers, so the request returns quickly and the work survives crashes.
9
+ - **Misconceptions:**
10
+ - Firing an unawaited promise or thread from the request handler is a background job; it vanishes on a crash or deploy and nothing retries it.
11
+ - A queued job runs exactly once; brokers redeliver after visibility timeouts or worker crashes, so job handlers must be safe to run twice.
12
+ - **Checks:**
13
+ - If the worker dies halfway through this job in your code, what state is left behind and what happens when the job is retried?
14
+ - What does your code do with a job that fails every attempt, and how would anyone find out it is stuck?
15
+ - **Go deeper:**
16
+ - [Sriniously · Backend from first principles #14 · 9:17](https://www.youtube.com/watch?v=r-nQsyguU1Y&t=557s)
17
+ - [Microsoft Learn: Best practices for background jobs](https://learn.microsoft.com/en-us/azure/architecture/best-practices/background-jobs)
18
+
19
+ ## idempotency-keys
20
+
21
+ - **Name:** Idempotency keys and at-least-once delivery
22
+ - **Requires:** `http-methods-idempotency`
23
+ - **Signals:** `Idempotency-Key`, `ON CONFLICT DO NOTHING`, `processed_events` table, unique index on an event or request id
24
+ - **Idea:** When a sender may deliver the same request twice, a key the receiver remembers turns the second delivery into a no-op that returns the first result.
25
+ - **Misconceptions:**
26
+ - Checking "have I seen this id?" and then inserting is safe; two concurrent duplicates both pass the check unless a unique constraint decides.
27
+ - Retries are rare enough to ignore; every queue, webhook provider and mobile client on a flaky network retries by design.
28
+ - **Checks:**
29
+ - If two copies of the same event reach your handler at the same moment, which line in your code stops the second one from charging twice?
30
+ - How long does your code keep processed keys, and what happens to a duplicate that arrives after they expire?
31
+ - **Go deeper:**
32
+ - [Sriniously · Backend from first principles #29 · 27:39](https://www.youtube.com/watch?v=eWM0CVReP04&t=1659s)
33
+ - [Stripe docs: Idempotent requests](https://docs.stripe.com/api/idempotent_requests)
34
+
35
+ ## transactional-outbox
36
+
37
+ - **Name:** Transactional outbox
38
+ - **Requires:** `transactions-isolation`, `background-jobs`
39
+ - **Signals:** `outbox_events`, `INSERT INTO outbox`, `FOR UPDATE SKIP LOCKED`, `published_at IS NULL`, `Debezium`
40
+ - **Idea:** Save the event to an outbox table inside the same transaction as the data change, and let a relay publish it later, so a commit and its message can never disagree.
41
+ - **Misconceptions:**
42
+ - Publishing to the broker right after commit is close enough; a crash between the two loses the event, and publishing before commit announces data that may roll back.
43
+ - The outbox gives exactly-once delivery; the relay can publish a row and crash before marking it sent, so consumers still need deduplication.
44
+ - **Checks:**
45
+ - If your process crashes right after the database commit in this code, how does the event still reach the queue?
46
+ - How does your relay stop two instances publishing the same outbox row, and what keeps the outbox table from growing forever?
47
+ - **Go deeper:**
48
+ - [Sriniously · Backend from first principles #29 · 38:58](https://www.youtube.com/watch?v=eWM0CVReP04&t=2338s)
49
+ - [microservices.io: Pattern: Transactional outbox](https://microservices.io/patterns/data/transactional-outbox.html)
50
+
51
+ ## webhook-receiver
52
+
53
+ - **Name:** Receiving webhooks
54
+ - **Requires:** `idempotency-keys`
55
+ - **Signals:** `/webhooks/stripe`, `express.raw(`, `@csrf_exempt`, `switch (event.type)`, `event.id`
56
+ - **Idea:** A webhook is an HTTP request another service sends when something changes there, so your endpoint must acknowledge fast and handle events arriving late, twice or out of order.
57
+ - **Misconceptions:**
58
+ - Doing all the work inside the webhook request is fine; slow handlers hit the sender's timeout, get marked failed and are redelivered while still running.
59
+ - Events arrive in the order they happened; providers retry independently, so an older update can land after a newer one unless you compare timestamps or refetch state.
60
+ - **Checks:**
61
+ - If your handler crashes after updating the database but before returning 200, what happens when the provider redelivers the same event?
62
+ - Which status codes does your endpoint return for unknown event types and for internal errors, and how will the sender react to each?
63
+ - **Go deeper:**
64
+ - [Sriniously · Backend from first principles #29 · 9:56](https://www.youtube.com/watch?v=eWM0CVReP04&t=596s)
65
+ - [GitHub Docs: Best practices for using webhooks](https://docs.github.com/en/webhooks/using-webhooks/best-practices-for-using-webhooks)
66
+
67
+ ## webhook-signatures
68
+
69
+ - **Name:** Verifying webhook signatures (HMAC)
70
+ - **Requires:** `webhook-receiver`
71
+ - **Signals:** `Stripe-Signature`, `X-Hub-Signature-256`, `crypto.timingSafeEqual`, `hmac.compare_digest`, `hmac.Equal`, `constructEvent(`
72
+ - **Idea:** The sender signs the raw request body with a shared secret using HMAC, and your endpoint recomputes the signature to prove the request is genuine, unmodified and recent before trusting it.
73
+ - **Misconceptions:**
74
+ - Verifying against the parsed JSON works; re-serializing changes bytes and spacing, so the signature must be computed over the exact raw body received.
75
+ - Comparing signatures with `==` is fine; an ordinary string compare can leak timing information, so use a constant-time comparison function.
76
+ - **Checks:**
77
+ - Does your handler read the raw body before any JSON middleware parses it, and what happens to verification if that middleware order changes?
78
+ - How does your code reject a correctly signed request that was captured last week and replayed today?
79
+ - **Go deeper:**
80
+ - [Sriniously · Backend from first principles #29 · 16:52](https://www.youtube.com/watch?v=eWM0CVReP04&t=1012s)
81
+ - [GitHub Docs: Validating webhook deliveries](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries)
@@ -0,0 +1,97 @@
1
+ # Authentication and authorization
2
+
3
+ ## session-auth
4
+
5
+ - **Name:** Session-based authentication
6
+ - **Requires:** `http-messages`
7
+ - **Signals:** `express-session`, `req.session`, `SESSION_ENGINE`, `connect-redis`, `session_id` cookie, `regenerate(`
8
+ - **Idea:** After login the server stores session state and gives the browser an opaque random id in a cookie, so each request is authenticated by looking that id up server-side.
9
+ - **Misconceptions:**
10
+ - Keeping the same session id after login is fine; it enables session fixation, so regenerate the id whenever privilege changes.
11
+ - In-memory session stores work in production; they vanish on restart and are not shared across instances behind a load balancer.
12
+ - **Checks:**
13
+ - When a user logs out or changes their password, which line in your code destroys the server-side session, and do other sessions survive?
14
+ - With two instances of your app behind a load balancer, where do sessions live and does a request reaching the other instance still work?
15
+ - **Go deeper:**
16
+ - [Sriniously · Backend from first principles #8 · 18:26](https://www.youtube.com/watch?v=A95rliroC8Q&t=1106s)
17
+ - [OWASP Cheat Sheet: Session Management](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html)
18
+
19
+ ## cookie-security
20
+
21
+ - **Name:** Cookie security attributes
22
+ - **Requires:** `session-auth`
23
+ - **Signals:** `HttpOnly`, `Secure`, `SameSite=Lax`, `__Host-`, `res.cookie(`, `SESSION_COOKIE_SECURE`
24
+ - **Idea:** Cookie attributes control who can read a cookie and when browsers send it, limiting theft by scripts, exposure over plain HTTP and cross-site request forgery.
25
+ - **Misconceptions:**
26
+ - `HttpOnly` stops XSS; it only hides the cookie from JavaScript, while injected script can still send requests as the user.
27
+ - `SameSite=Lax` replaces all CSRF defenses; state-changing GET requests and same-site subdomains still need protection.
28
+ - **Checks:**
29
+ - Which attributes does your code set on the session cookie, and what could a script injected into your page do with it?
30
+ - If your frontend and API live on different sites, which `SameSite` value does your cookie need, and what CSRF defense must replace it?
31
+ - **Go deeper:**
32
+ - [Sriniously · Backend from first principles #20 · 1:07:17](https://www.youtube.com/watch?v=xB1C1xZZW4k&t=4037s)
33
+ - [MDN: Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies)
34
+
35
+ ## jwt-auth
36
+
37
+ - **Name:** JWTs and stateless authentication
38
+ - **Requires:** `http-messages`
39
+ - **Signals:** `Authorization: Bearer`, `jwt.verify(`, `jsonwebtoken`, `PyJWT`, `golang-jwt`, `exp` claim
40
+ - **Idea:** A JWT is a signed token carrying claims the server verifies with a key instead of a database lookup, trading easy horizontal scaling for harder revocation.
41
+ - **Misconceptions:**
42
+ - JWT payloads are encrypted; they are only base64url-encoded and signed, so anyone holding the token can read the claims.
43
+ - Logging out deletes the token; a stolen JWT stays valid until `exp` unless you keep short lifetimes or a denylist.
44
+ - **Checks:**
45
+ - Does your verification pin the expected algorithm and check `exp`, `iss` and `aud`, or does it trust whatever the token header says?
46
+ - If an account is disabled right now, how long can an already-issued token keep calling your API, and is that acceptable?
47
+ - **Go deeper:**
48
+ - [Sriniously · Backend from first principles #8 · 25:40](https://www.youtube.com/watch?v=A95rliroC8Q&t=1540s)
49
+ - [RFC 8725: JSON Web Token Best Current Practices](https://www.rfc-editor.org/rfc/rfc8725)
50
+
51
+ ## oauth2-oidc
52
+
53
+ - **Name:** OAuth 2.0 and OpenID Connect
54
+ - **Requires:** `jwt-auth`
55
+ - **Signals:** `/oauth/authorize`, `code_challenge`, `redirect_uri`, `id_token`, `openid profile email`, `passport-google-oauth20`
56
+ - **Idea:** OAuth 2.0 lets an app obtain scoped access tokens without seeing the user's password, and OpenID Connect adds an ID token so the app also learns who signed in.
57
+ - **Misconceptions:**
58
+ - OAuth is an authentication protocol; it grants access, and using an access token as proof of identity is unsafe without OIDC's ID token.
59
+ - PKCE is only for mobile apps; current best practice recommends it for every authorization code flow, including server-side apps.
60
+ - **Checks:**
61
+ - In your callback handler, which checks verify `state`, the ID token signature, `aud` and `nonce` before you create a local session?
62
+ - Which scopes does your code request, and what could an attacker do if the stored provider token leaked?
63
+ - **Go deeper:**
64
+ - [Sriniously · Backend from first principles #8 · 50:25](https://www.youtube.com/watch?v=A95rliroC8Q&t=3025s)
65
+ - [OAuth.net: OAuth 2.0](https://oauth.net/2/)
66
+
67
+ ## password-hashing
68
+
69
+ - **Name:** Password hashing
70
+ - **Requires:** none
71
+ - **Signals:** `argon2id`, `bcrypt.hash(`, `scrypt`, `PBKDF2`, `password_hash`, `check_password(`
72
+ - **Idea:** Passwords are stored as slow, salted one-way hashes, so a leaked database cannot be reversed cheaply and identical passwords produce different hashes.
73
+ - **Misconceptions:**
74
+ - SHA-256 with a salt is enough; fast hashes allow billions of guesses per second, so use Argon2id, scrypt or bcrypt.
75
+ - Encrypting passwords is safer than hashing; anyone holding the key decrypts every password, while hashes cannot be reversed.
76
+ - **Checks:**
77
+ - What work factor does your code use, and how would you upgrade old hashes when users next log in?
78
+ - Does your login handler take the same time and return the same error for an unknown email and a wrong password?
79
+ - **Go deeper:**
80
+ - [Sriniously · Backend from first principles #20 · 45:25](https://www.youtube.com/watch?v=xB1C1xZZW4k&t=2725s)
81
+ - [OWASP Cheat Sheet: Password Storage](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html)
82
+
83
+ ## rbac-authorization
84
+
85
+ - **Name:** Role-based authorization
86
+ - **Requires:** `session-auth`
87
+ - **Signals:** `@PreAuthorize`, `hasRole(`, `requireRole(`, `user.role ===`, `permission_required`, `WHERE owner_id =`
88
+ - **Idea:** Authorization decides what an authenticated user may do, typically by mapping roles to permissions and checking both the action and ownership of the specific resource on the server.
89
+ - **Misconceptions:**
90
+ - Hiding a button in the UI enforces permissions; the API must check every request because anyone can call it directly.
91
+ - A role check is enough; without verifying the user owns the record, changing an id in the URL exposes other users' data.
92
+ - **Checks:**
93
+ - If a regular user changes the id in this request to someone else's record, which line in your code stops them?
94
+ - Your code checks `role === "admin"` in several handlers; how would you add a new role without editing each of them?
95
+ - **Go deeper:**
96
+ - [Sriniously · Backend from first principles #8 · 1:19:10](https://www.youtube.com/watch?v=A95rliroC8Q&t=4750s)
97
+ - [OWASP Cheat Sheet: Authorization](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html)
@@ -0,0 +1,65 @@
1
+ # Caching
2
+
3
+ ## cache-aside
4
+
5
+ - **Name:** Cache-aside pattern
6
+ - **Requires:** none
7
+ - **Signals:** `redis.get(` then `redis.set(`, `cache.get_or_set(`, `@Cacheable`, `SETEX`, `if cached == nil`
8
+ - **Idea:** The application checks the cache first, and on a miss reads the database and stores the result, so repeated reads skip the slow store while the database stays the source of truth.
9
+ - **Misconceptions:**
10
+ - A cache miss is harmless; when a hot key expires, many requests miss together and stampede the database unless one request refills it.
11
+ - The cache is always available; if it is down or slow, your code should fall back to the database rather than fail the request.
12
+ - **Checks:**
13
+ - If the cached key for this endpoint expires during peak traffic, how many requests in your code hit the database at once to rebuild it?
14
+ - What does your handler do when the cache call times out, and does that turn a cache outage into a full outage?
15
+ - **Go deeper:**
16
+ - [Sriniously · Backend from first principles #21.1 · 1:17:35](https://www.youtube.com/watch?v=z7kt_p44rjs&t=4655s)
17
+ - [Microsoft Learn: Cache-Aside pattern](https://learn.microsoft.com/en-us/azure/architecture/patterns/cache-aside)
18
+
19
+ ## cache-invalidation
20
+
21
+ - **Name:** Cache invalidation
22
+ - **Requires:** `cache-aside`
23
+ - **Signals:** `redis.del(`, `cache.delete(`, `@CacheEvict`, `revalidateTag(`, `user:${id}` cache keys, `PUBLISH` invalidation event
24
+ - **Idea:** When the underlying data changes, cached copies must be deleted or updated, otherwise readers keep getting the old value until it happens to expire.
25
+ - **Misconceptions:**
26
+ - Deleting the key before the database write is safe; a concurrent reader can refill the old value in between, so update first, then delete.
27
+ - Invalidating one key is enough; lists, counts and aggregates that include the changed row are cached under other keys too.
28
+ - **Checks:**
29
+ - After your code updates this record, which cached keys still contain the old value, including lists or summaries that embed it?
30
+ - If the database write succeeds but your cache delete fails, how long does stale data survive, and what bounds it?
31
+ - **Go deeper:**
32
+ - [Sriniously · Backend from first principles #21.1 · 1:17:35](https://www.youtube.com/watch?v=z7kt_p44rjs&t=4655s)
33
+ - [Microsoft Learn: Caching guidance](https://learn.microsoft.com/en-us/azure/architecture/best-practices/caching)
34
+
35
+ ## cache-eviction-ttl
36
+
37
+ - **Name:** Eviction policies and TTLs
38
+ - **Requires:** `cache-aside`
39
+ - **Signals:** `EX 300`, `EXPIRE`, `maxmemory-policy`, `allkeys-lru`, `ttl=`, `lru-cache`
40
+ - **Idea:** A TTL bounds how stale an entry can get, while an eviction policy decides what to drop when memory fills, together keeping the cache fresh and within its size.
41
+ - **Misconceptions:**
42
+ - A longer TTL is always better; it raises the hit rate but also how long users see outdated data after a change.
43
+ - Unbounded in-process caches are fine; without a size limit they grow until the process runs out of memory.
44
+ - **Checks:**
45
+ - How stale can the data your code caches safely be, and does the TTL you chose match that rather than a round number?
46
+ - If many keys in your code share one TTL and were written together, what happens to the database when they all expire at once?
47
+ - **Go deeper:**
48
+ - [Sriniously · Backend from first principles #13 · 42:48](https://www.youtube.com/watch?v=estH64OkwxU&t=2568s)
49
+ - [Redis docs: Key eviction](https://redis.io/docs/latest/develop/reference/eviction/)
50
+
51
+ ## cdn-edge-caching
52
+
53
+ - **Name:** CDNs and edge caching
54
+ - **Requires:** `http-caching-headers`
55
+ - **Signals:** `Cache-Control: public, s-maxage=`, `Surrogate-Key`, `CDN-Cache-Control`, `Vary`, `CF-Cache-Status`, purge API call
56
+ - **Idea:** A CDN keeps copies of responses at servers near users, cutting latency and origin load, with your cache headers deciding what is shared and for how long.
57
+ - **Misconceptions:**
58
+ - Only static files belong on a CDN; public API responses can be edge-cached too with short `s-maxage` and stale-while-revalidate.
59
+ - Marking a response `public` is harmless; if it is personalized and the cache key ignores the cookie, the CDN serves one user's data to others.
60
+ - **Checks:**
61
+ - Does any response your code marks cacheable depend on the cookie, auth header or language, and how does the CDN key keep users apart?
62
+ - After your code updates content, how does the edge copy get refreshed: by TTL expiry, a purge call or a versioned URL?
63
+ - **Go deeper:**
64
+ - [Sriniously · Backend from first principles #21.2 · 51:22](https://www.youtube.com/watch?v=sOhAopEwjH4&t=3082s)
65
+ - [Cloudflare docs: Origin Cache Control](https://developers.cloudflare.com/cache/concepts/cache-control/)