@coralai/sps-cli 0.41.2 → 0.43.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 +34 -3
- package/dist/commands/cardAdd.d.ts +1 -1
- package/dist/commands/cardAdd.d.ts.map +1 -1
- package/dist/commands/cardAdd.js +16 -6
- package/dist/commands/cardAdd.js.map +1 -1
- package/dist/commands/cardDashboard.js +1 -1
- package/dist/commands/cardDashboard.js.map +1 -1
- package/dist/commands/doctor.d.ts +9 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +3 -314
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/hookCommand.d.ts.map +1 -1
- package/dist/commands/hookCommand.js +6 -7
- package/dist/commands/hookCommand.js.map +1 -1
- package/dist/commands/pmCommand.js +1 -1
- package/dist/commands/pmCommand.js.map +1 -1
- package/dist/commands/projectInit.d.ts.map +1 -1
- package/dist/commands/projectInit.js +60 -37
- package/dist/commands/projectInit.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +3 -30
- 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/commands/tick.js +1 -1
- package/dist/commands/tick.js.map +1 -1
- package/dist/core/checklist.d.ts +22 -0
- package/dist/core/checklist.d.ts.map +1 -0
- package/dist/core/checklist.js +38 -0
- package/dist/core/checklist.js.map +1 -0
- package/dist/core/checklist.test.d.ts +2 -0
- package/dist/core/checklist.test.d.ts.map +1 -0
- package/dist/core/checklist.test.js +74 -0
- package/dist/core/checklist.test.js.map +1 -0
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/config.test.js +7 -4
- package/dist/core/config.test.js.map +1 -1
- package/dist/core/context.d.ts +1 -1
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/skillStore.d.ts +46 -0
- package/dist/core/skillStore.d.ts.map +1 -0
- package/dist/core/skillStore.js +197 -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 +190 -0
- package/dist/core/skillStore.test.js.map +1 -0
- package/dist/engines/EventHandler.test.js +3 -3
- package/dist/engines/EventHandler.test.js.map +1 -1
- package/dist/engines/MonitorEngine.js +2 -2
- package/dist/engines/MonitorEngine.js.map +1 -1
- package/dist/engines/SchedulerEngine.js +1 -1
- package/dist/engines/SchedulerEngine.js.map +1 -1
- package/dist/engines/StageEngine.js +3 -3
- package/dist/engines/StageEngine.js.map +1 -1
- package/dist/engines/engine-pipeline-adapter.test.js +2 -2
- package/dist/engines/engine-pipeline-adapter.test.js.map +1 -1
- package/dist/interfaces/TaskBackend.d.ts +3 -1
- package/dist/interfaces/TaskBackend.d.ts.map +1 -1
- package/dist/main.js +19 -17
- package/dist/main.js.map +1 -1
- package/dist/models/types.d.ts +16 -1
- package/dist/models/types.d.ts.map +1 -1
- package/dist/providers/MarkdownTaskBackend.d.ts +2 -1
- package/dist/providers/MarkdownTaskBackend.d.ts.map +1 -1
- package/dist/providers/MarkdownTaskBackend.js +28 -5
- package/dist/providers/MarkdownTaskBackend.js.map +1 -1
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +5 -7
- package/dist/providers/registry.js.map +1 -1
- package/package.json +1 -1
- package/project-template/.claude/hooks/start.sh +44 -0
- package/project-template/.claude/settings.json +1 -1
- 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,191 @@
|
|
|
1
|
+
# Schema
|
|
2
|
+
|
|
3
|
+
Normalization, keys, constraints, types.
|
|
4
|
+
|
|
5
|
+
## Start normalized
|
|
6
|
+
|
|
7
|
+
3rd Normal Form (3NF) is the sweet spot for most OLTP apps:
|
|
8
|
+
- Each table represents one concept.
|
|
9
|
+
- Every non-key column depends on the key, the whole key, and nothing but the key.
|
|
10
|
+
- No repeating groups (prefer child tables over `phone1 / phone2 / phone3`).
|
|
11
|
+
|
|
12
|
+
Denormalize later, with measurement, for reporting / read-heavy paths. Denormalized by default breeds data-consistency bugs.
|
|
13
|
+
|
|
14
|
+
## Primary keys
|
|
15
|
+
|
|
16
|
+
Every table has one. Options:
|
|
17
|
+
|
|
18
|
+
| Type | Pros | Cons |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| Auto-increment integer | Compact, fast, ordered | Leaks count; awkward in distributed systems |
|
|
21
|
+
| UUID v4 | Globally unique, generated anywhere | 16 bytes, random → index fragmentation |
|
|
22
|
+
| UUID v7 / ULID / KSUID | Time-ordered, unique, generated anywhere | Slightly newer; check lib support |
|
|
23
|
+
| Composite key | Natural uniqueness (e.g., `(order_id, line_no)`) | Join and FK complexity |
|
|
24
|
+
|
|
25
|
+
Recommendation: **UUID v7 / ULID** for new systems. Still sortable by time, no central generator needed, no count leakage. Store as `UUID` (Postgres) or `BINARY(16)` (MySQL) — don't use `VARCHAR(36)` (3× the storage).
|
|
26
|
+
|
|
27
|
+
## Foreign keys
|
|
28
|
+
|
|
29
|
+
Declare them:
|
|
30
|
+
|
|
31
|
+
```sql
|
|
32
|
+
CREATE TABLE orders (
|
|
33
|
+
id UUID PRIMARY KEY,
|
|
34
|
+
user_id UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
|
|
35
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
36
|
+
);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`ON DELETE`:
|
|
40
|
+
- `RESTRICT` / `NO ACTION` — safe default; forces explicit cleanup.
|
|
41
|
+
- `CASCADE` — dangerous; one delete can wipe large subgraphs. Use carefully.
|
|
42
|
+
- `SET NULL` — when the relationship is optional.
|
|
43
|
+
|
|
44
|
+
Don't skip FKs "for performance". The integrity guarantee is worth a lot more than the microseconds.
|
|
45
|
+
|
|
46
|
+
## Constraints over application logic
|
|
47
|
+
|
|
48
|
+
Let the DB enforce invariants that must always hold.
|
|
49
|
+
|
|
50
|
+
```sql
|
|
51
|
+
-- Invariants
|
|
52
|
+
email TEXT NOT NULL UNIQUE,
|
|
53
|
+
age INT CHECK (age >= 0 AND age <= 150),
|
|
54
|
+
status TEXT NOT NULL CHECK (status IN ('pending','active','banned')),
|
|
55
|
+
balance NUMERIC NOT NULL CHECK (balance >= 0),
|
|
56
|
+
|
|
57
|
+
-- Uniqueness across multiple columns
|
|
58
|
+
CONSTRAINT uq_org_email UNIQUE (org_id, email)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Application validation is belt; DB constraints are suspenders. You want both.
|
|
62
|
+
|
|
63
|
+
## Column types
|
|
64
|
+
|
|
65
|
+
### Text
|
|
66
|
+
|
|
67
|
+
- `TEXT` in Postgres — no length limit, same storage as `VARCHAR`.
|
|
68
|
+
- `VARCHAR(n)` when the limit is a real business rule (e.g., phone max 20). `VARCHAR(255)` by habit is noise.
|
|
69
|
+
- `CHAR(n)` — almost never the right answer (pads with spaces).
|
|
70
|
+
|
|
71
|
+
### Numbers
|
|
72
|
+
|
|
73
|
+
- Integers: `INTEGER` (32-bit) or `BIGINT` (64-bit). Pick based on range.
|
|
74
|
+
- **Money**: `NUMERIC(12, 2)` or **cents as integer** — never `FLOAT` / `DOUBLE` (binary floats lose pennies).
|
|
75
|
+
- `REAL` / `DOUBLE PRECISION` only for scientific / measurement data where precision loss is OK.
|
|
76
|
+
|
|
77
|
+
### Time
|
|
78
|
+
|
|
79
|
+
- `TIMESTAMPTZ` (Postgres with timezone) or UTC `TIMESTAMP` — always store in UTC. Convert in the app.
|
|
80
|
+
- `DATE` for dates without time-of-day.
|
|
81
|
+
- Never store time as `TEXT` or milliseconds-since-epoch as `BIGINT` unless you genuinely need it for external APIs.
|
|
82
|
+
|
|
83
|
+
### Boolean
|
|
84
|
+
|
|
85
|
+
- `BOOLEAN` where supported.
|
|
86
|
+
- MySQL older versions: `TINYINT(1)` as a workaround.
|
|
87
|
+
|
|
88
|
+
### Enum-like
|
|
89
|
+
|
|
90
|
+
Three options:
|
|
91
|
+
|
|
92
|
+
| Approach | Pros | Cons |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| Native ENUM (Postgres, MySQL) | Type-checked at DB level | Hard to add values without ALTER |
|
|
95
|
+
| CHECK constraint with string | Easy to extend | String fragility |
|
|
96
|
+
| Lookup table + FK | Flexible, self-documenting | Join on common queries |
|
|
97
|
+
|
|
98
|
+
For small fixed sets (status = active / pending / banned), CHECK on a TEXT column is the pragmatic default.
|
|
99
|
+
|
|
100
|
+
### JSON
|
|
101
|
+
|
|
102
|
+
- `JSONB` (Postgres) — indexable, queryable.
|
|
103
|
+
- Use for schemaless attributes, user-defined fields, optional metadata.
|
|
104
|
+
- **Don't** use as the primary way to represent structured data. If you're going to query `raw->>'email'` everywhere, promote `email` to a column.
|
|
105
|
+
|
|
106
|
+
## Naming
|
|
107
|
+
|
|
108
|
+
Pick a convention, stay consistent.
|
|
109
|
+
|
|
110
|
+
- **Tables**: plural (`users`, `orders`) or singular (`user`, `order`). Most teams pick plural.
|
|
111
|
+
- **Columns**: `snake_case`. Match the language's ORM convention.
|
|
112
|
+
- **Primary key**: `id`.
|
|
113
|
+
- **Foreign keys**: `<table_singular>_id`. `user_id` references `users.id`.
|
|
114
|
+
- **Timestamps**: `created_at`, `updated_at`, `deleted_at`.
|
|
115
|
+
- **Booleans**: `is_active`, `has_verified_email`.
|
|
116
|
+
- **Indexes**: `ix_<table>_<columns>`. Unique: `uq_<table>_<columns>`.
|
|
117
|
+
|
|
118
|
+
## Timestamps on every table
|
|
119
|
+
|
|
120
|
+
```sql
|
|
121
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
122
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`updated_at` via trigger OR app responsibility. Auditing, debugging, backfills — all need these.
|
|
126
|
+
|
|
127
|
+
## Soft delete vs. hard delete
|
|
128
|
+
|
|
129
|
+
Default: **hard delete**. Reclaims space, simplifies queries, respects privacy requests.
|
|
130
|
+
|
|
131
|
+
Soft delete (`deleted_at TIMESTAMP NULL`) when:
|
|
132
|
+
- You need a recovery window.
|
|
133
|
+
- Historical referencing matters (keep the row for audit but hide from listings).
|
|
134
|
+
|
|
135
|
+
Trade-off: every query now filters `WHERE deleted_at IS NULL`. Forgetting is a bug class. Consider a `users_active` view for day-to-day use.
|
|
136
|
+
|
|
137
|
+
If you need audit history, an `events` / `audit_log` table is usually cleaner than soft-delete everywhere.
|
|
138
|
+
|
|
139
|
+
## Partitioning
|
|
140
|
+
|
|
141
|
+
For very large tables (hundreds of millions of rows), partition by time, tenant, or region. Postgres declarative partitioning:
|
|
142
|
+
|
|
143
|
+
```sql
|
|
144
|
+
CREATE TABLE events (
|
|
145
|
+
id UUID PRIMARY KEY,
|
|
146
|
+
tenant_id UUID NOT NULL,
|
|
147
|
+
created_at TIMESTAMPTZ NOT NULL,
|
|
148
|
+
payload JSONB
|
|
149
|
+
) PARTITION BY RANGE (created_at);
|
|
150
|
+
|
|
151
|
+
CREATE TABLE events_2026_04 PARTITION OF events
|
|
152
|
+
FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Benefits: small indexes per partition, easy to drop old data (`DROP PARTITION`), parallel queries.
|
|
156
|
+
|
|
157
|
+
Don't partition speculatively. The operational overhead is real.
|
|
158
|
+
|
|
159
|
+
## Multi-tenancy
|
|
160
|
+
|
|
161
|
+
Three shapes:
|
|
162
|
+
|
|
163
|
+
| Shape | Pros | Cons |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| Separate DBs per tenant | Full isolation, easy backup per tenant | Operational overhead at scale |
|
|
166
|
+
| Shared DB, separate schemas | Middle ground | Connection per schema can be clunky |
|
|
167
|
+
| Shared DB, shared schema + tenant_id column | Simplest, scales to many tenants | Every query MUST filter tenant_id |
|
|
168
|
+
|
|
169
|
+
Shared schema + `tenant_id`: enforce via row-level security (Postgres RLS) if available, or framework middleware that injects the filter. Forgetting is a catastrophic data leak.
|
|
170
|
+
|
|
171
|
+
## Referential design patterns
|
|
172
|
+
|
|
173
|
+
- **Associations**: join table `order_items (order_id, line_no, product_id, qty, price_cents)` with composite PK.
|
|
174
|
+
- **Hierarchies**: `parent_id` + recursive CTE, or `ltree` (Postgres), or nested sets (read-heavy).
|
|
175
|
+
- **Tags / many-to-many**: `post_tags (post_id, tag_id)`.
|
|
176
|
+
- **Audit**: separate `audit_logs` table with immutable rows.
|
|
177
|
+
|
|
178
|
+
## Anti-patterns
|
|
179
|
+
|
|
180
|
+
| Anti-pattern | Fix |
|
|
181
|
+
|---|---|
|
|
182
|
+
| Primary key = `VARCHAR(36)` UUID | Use native `UUID` / `BINARY(16)` |
|
|
183
|
+
| `VARCHAR(255)` reflex | Use `TEXT` or a justified length |
|
|
184
|
+
| One table "users_and_admins" with a role column and many nullable fields | Normalize or use type-specific tables |
|
|
185
|
+
| Store JSON with the same shape in every row | Promote to columns |
|
|
186
|
+
| Mutable history columns (`last_email_change_at`) spread across user table | Consider an audit log |
|
|
187
|
+
| Money as `FLOAT` | Never |
|
|
188
|
+
| `bool` stored as `Y/N` strings | Native `BOOLEAN` |
|
|
189
|
+
| Different timestamps in different timezones | UTC everywhere |
|
|
190
|
+
| `NULL` for "unknown" AND "not applicable" | Two different columns, or a CHECK'd enum |
|
|
191
|
+
| Massive tables with no partitioning plan | Partition or archive when they get big |
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: database-optimizer
|
|
3
|
+
description: Persona skill — debug and tune SQL / DB performance like a specialist. Read plans, spot missing indexes, size pools. Overlay on top of `database`. For patterns, load `database`.
|
|
4
|
+
origin: original
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Database Optimizer
|
|
8
|
+
|
|
9
|
+
Diagnose slow queries. Design indexes that earn their keep. Keep the DB quietly fast.
|
|
10
|
+
|
|
11
|
+
## When to load
|
|
12
|
+
|
|
13
|
+
- A query is slow
|
|
14
|
+
- A hot table keeps timing out
|
|
15
|
+
- Planning a schema change with performance implications
|
|
16
|
+
- Reviewing an ORM-generated query
|
|
17
|
+
- Sizing a connection pool, memory, autovacuum
|
|
18
|
+
|
|
19
|
+
## The posture
|
|
20
|
+
|
|
21
|
+
1. **Numbers, not hunches.** `EXPLAIN (ANALYZE, BUFFERS)` beats "this should be fast."
|
|
22
|
+
2. **The right index > three wrong ones.** Over-indexing is its own perf bug (writes, storage).
|
|
23
|
+
3. **Most "DB problems" are query problems.** Bad SQL, N+1, unnecessary ORDER BY. Fix the query before scaling hardware.
|
|
24
|
+
4. **The plan is the truth.** If it says seq scan and you expected index scan, figure out why.
|
|
25
|
+
5. **Stats are often stale.** `ANALYZE` / `ANALYZE TABLE` before believing the plan.
|
|
26
|
+
6. **Think in rows scanned, not rows returned.** Scanning 1M rows to return 10 is the bug.
|
|
27
|
+
|
|
28
|
+
## The diagnostic flow
|
|
29
|
+
|
|
30
|
+
When "the query is slow":
|
|
31
|
+
|
|
32
|
+
1. **Get the query.** Exact SQL as executed, with real parameters.
|
|
33
|
+
2. **Run `EXPLAIN (ANALYZE, BUFFERS)`** (Postgres) / `EXPLAIN ANALYZE` (MySQL). Read the actual cost and row counts.
|
|
34
|
+
3. **Look for the big number.** One node dominates. Start there.
|
|
35
|
+
4. **Compare actual vs. estimated rows.** Orders of magnitude off → stats are stale or skewed.
|
|
36
|
+
5. **Look for seq scan with a selective filter.** Missing / unused index.
|
|
37
|
+
6. **Look for sort spilling to disk.** Under-sized work_mem or wrong index order.
|
|
38
|
+
7. **Look for nested loop on a large inner.** Missing join index or bad cardinality estimate.
|
|
39
|
+
8. **Propose the fix.** Add index / rewrite query / update stats / partition / cache.
|
|
40
|
+
9. **Measure.** Run again with the change. Quote before/after timings.
|
|
41
|
+
|
|
42
|
+
Don't propose fixes blind. Every fix you ship without a measured before/after is a guess.
|
|
43
|
+
|
|
44
|
+
## Questions you always ask
|
|
45
|
+
|
|
46
|
+
- **Is this the exact SQL production runs?** (ORMs lie; check with pg_stat_statements or query log.)
|
|
47
|
+
- **What's the selectivity?** How many rows does the filter return out of the total?
|
|
48
|
+
- **Is there an index that covers this?** Check `pg_stat_user_indexes` for usage.
|
|
49
|
+
- **What's the cardinality estimate vs. actual?**
|
|
50
|
+
- **Is the query sargable?** (`WHERE lower(email) = ?` without an expression index isn't.)
|
|
51
|
+
- **Are statistics fresh?** When was the last `ANALYZE`?
|
|
52
|
+
- **Is this part of an N+1?** Small query run N times is bigger than one large query.
|
|
53
|
+
- **How does this scale?** At 10× data, what happens?
|
|
54
|
+
|
|
55
|
+
## Common patterns you recognize
|
|
56
|
+
|
|
57
|
+
### Missing index
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
Seq Scan on orders (cost=0.00..25000.00 rows=1000 width=48)
|
|
61
|
+
Filter: (status = 'pending')
|
|
62
|
+
Rows Removed by Filter: 499000
|
|
63
|
+
Actual Rows: 1000
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Scanned 500K, kept 1K. Index on `status` (partial index if 'pending' is rare) fixes it.
|
|
67
|
+
|
|
68
|
+
### Wrong composite order
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
-- Index: (user_id, created_at)
|
|
72
|
+
EXPLAIN SELECT * FROM orders WHERE created_at > now() - '1 day' AND user_id = ?;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If the plan is a seq scan, the index order may be wrong for the planner's needs. Reorder or add a second index.
|
|
76
|
+
|
|
77
|
+
### Stale stats
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
Estimated Rows: 50
|
|
81
|
+
Actual Rows: 500000
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
10 000× off. The planner used the wrong join strategy. `ANALYZE` the table.
|
|
85
|
+
|
|
86
|
+
### N+1
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
for order in orders: # 1 query
|
|
90
|
+
user = User.get(order.user_id) # N queries
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Fix: batch (`User.get_by_ids([...])`) or eager-load in the ORM (`select_related`, `with_eager` etc.).
|
|
94
|
+
|
|
95
|
+
### Unneeded ORDER BY
|
|
96
|
+
|
|
97
|
+
```sql
|
|
98
|
+
SELECT * FROM events WHERE tenant_id = ? ORDER BY created_at DESC LIMIT 10;
|
|
99
|
+
-- No index on (tenant_id, created_at) → sorts everything first
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Composite index matching filter + sort makes this an index scan + early termination.
|
|
103
|
+
|
|
104
|
+
### Bloat / dead tuples
|
|
105
|
+
|
|
106
|
+
Postgres UPDATE / DELETE leaves dead tuples. Autovacuum cleans up. If it's not keeping up:
|
|
107
|
+
|
|
108
|
+
```sql
|
|
109
|
+
SELECT relname, n_dead_tup, last_vacuum, last_autovacuum
|
|
110
|
+
FROM pg_stat_user_tables WHERE n_dead_tup > 10000
|
|
111
|
+
ORDER BY n_dead_tup DESC;
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Tune autovacuum thresholds on hot tables.
|
|
115
|
+
|
|
116
|
+
## Index recommendations
|
|
117
|
+
|
|
118
|
+
- **First index on a hot table**: the one that serves the dominant query.
|
|
119
|
+
- **Composite**: columns in order of `WHERE = ?` first, then `WHERE > ?` (range), then `ORDER BY`.
|
|
120
|
+
- **Partial**: when the query filters on a rare value.
|
|
121
|
+
- **Expression**: when `WHERE lower(col) = ?` / `WHERE date_trunc('day', col) = ?`.
|
|
122
|
+
- **Covering (INCLUDE)**: when the query reads a few extra columns on top of the indexed ones.
|
|
123
|
+
|
|
124
|
+
Prune: drop indexes with zero scans in the last 30 days (after verifying usage across envs).
|
|
125
|
+
|
|
126
|
+
## Pool / memory sizing
|
|
127
|
+
|
|
128
|
+
When the DB is healthy but the app times out:
|
|
129
|
+
|
|
130
|
+
- **Connection saturation**: check pool wait times in the app. Likely `max_size` too small.
|
|
131
|
+
- **DB max_connections**: Postgres default ~100. Total app replicas × pool size must leave headroom for admin.
|
|
132
|
+
- **work_mem** (Postgres): per-operation; if queries spill to disk, consider raising (but test — memory multiplies per connection).
|
|
133
|
+
- **shared_buffers**: typically 25% of available RAM for a dedicated DB host.
|
|
134
|
+
|
|
135
|
+
## Anti-patterns you always flag
|
|
136
|
+
|
|
137
|
+
- `SELECT *` in production app code.
|
|
138
|
+
- Adding an index on every column "just in case".
|
|
139
|
+
- `WHERE function(col) = ?` without a matching expression index.
|
|
140
|
+
- `LIKE '%x%'` on a big table (non-indexable wildcard).
|
|
141
|
+
- `ORDER BY RANDOM()` on large tables.
|
|
142
|
+
- Business logic implemented in triggers without ADR.
|
|
143
|
+
- Read-then-update for an upsert (race condition).
|
|
144
|
+
- One giant transaction wrapping a batch import.
|
|
145
|
+
- UUID v4 primary keys on tables heavily sorted/paginated by PK (use UUID v7 / ULID).
|
|
146
|
+
- Migration that takes a long lock during peak traffic.
|
|
147
|
+
|
|
148
|
+
## Tradeoffs you name
|
|
149
|
+
|
|
150
|
+
- **Index count vs. write speed.** Every index is a write tax.
|
|
151
|
+
- **Normalization vs. read speed.** Denormalize only where measured.
|
|
152
|
+
- **Consistency vs. throughput.** RR / SI / SR per workload.
|
|
153
|
+
- **Read from replica vs. primary.** Staleness vs. primary load.
|
|
154
|
+
|
|
155
|
+
## Forbidden patterns
|
|
156
|
+
|
|
157
|
+
- Proposing performance fixes without an EXPLAIN
|
|
158
|
+
- "Let's just scale up" before diagnosing
|
|
159
|
+
- Adding an index without naming the query it serves
|
|
160
|
+
- Ignoring migration lock impact ("it's a small change")
|
|
161
|
+
- Running heavy analytical queries against the primary
|
|
162
|
+
- Changing indexes without measuring before/after
|
|
163
|
+
|
|
164
|
+
## Pair with
|
|
165
|
+
|
|
166
|
+
- [`database`](../database/SKILL.md) — the patterns and the vocabulary.
|
|
167
|
+
- [`backend/references/data-access.md`](../backend/references/data-access.md) — where SQL meets app code.
|
|
168
|
+
- [`devops/references/observability.md`](../devops/references/observability.md) — DB dashboards and alerts.
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debugging-workflow
|
|
3
|
+
description: Workflow skill — systematic debugging. Reproduce, isolate, hypothesize, verify. Works for bugs, performance issues, and live incidents.
|
|
4
|
+
origin: original
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Debugging Workflow
|
|
8
|
+
|
|
9
|
+
A method, not a ritual. Works for "user says it's broken" bugs, performance regressions, and live incidents.
|
|
10
|
+
|
|
11
|
+
## When to load
|
|
12
|
+
|
|
13
|
+
- Something is broken and you don't yet know why
|
|
14
|
+
- A test started failing and you don't know which change broke it
|
|
15
|
+
- A performance regression appeared
|
|
16
|
+
- You're on-call and an alert fired
|
|
17
|
+
- Reviewing someone's debug session to teach or coach
|
|
18
|
+
|
|
19
|
+
## The posture
|
|
20
|
+
|
|
21
|
+
1. **Change one variable at a time.** If you flip three things and it works, you don't know which one fixed it.
|
|
22
|
+
2. **Reproduce first, diagnose second, fix third.** Fixing without reproducing is guessing.
|
|
23
|
+
3. **Trust the data over the story.** Bug reports are leads, not proofs.
|
|
24
|
+
4. **Read the error, all of it.** Stack trace, message, timestamp, request id.
|
|
25
|
+
5. **When stuck, lower the abstraction.** Go one layer down until the mechanism is visible.
|
|
26
|
+
6. **Stop when stumped. Sleep. Reset.** Fresh eyes find bugs that tired eyes write bugs for.
|
|
27
|
+
|
|
28
|
+
## The flow
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
Reproduce ──▶ Isolate ──▶ Hypothesize ──▶ Test ──▶ Fix ──▶ Verify
|
|
32
|
+
▲ │
|
|
33
|
+
└──────────── disconfirm? back up ──────┘
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 1. Reproduce
|
|
37
|
+
|
|
38
|
+
You cannot debug what you can't reproduce. Turn the bug into a command.
|
|
39
|
+
|
|
40
|
+
- From a user report: collect the steps, the exact time, the user id, the device.
|
|
41
|
+
- From logs / metrics: narrow to the failing request or batch; get a request id.
|
|
42
|
+
- From a test: `cargo test --test my_test`, `pytest -k my_test`.
|
|
43
|
+
|
|
44
|
+
Goal: the smallest reproducible case. A 20-step manual reproduction is a lead; a 3-line test is evidence.
|
|
45
|
+
|
|
46
|
+
If you cannot reproduce:
|
|
47
|
+
- Add logging around the suspected area, ship a canary, wait for recurrence.
|
|
48
|
+
- Check if it's environment-specific (timezone, locale, OS, version).
|
|
49
|
+
- Check if it's data-specific (a particular record triggers it).
|
|
50
|
+
- Consider whether the bug is the bug report (user confused, different issue).
|
|
51
|
+
|
|
52
|
+
### 2. Isolate
|
|
53
|
+
|
|
54
|
+
Shrink the reproduction. Remove pieces one at a time. The last piece you remove is the bug's home.
|
|
55
|
+
|
|
56
|
+
Techniques:
|
|
57
|
+
- **Binary search** the codebase: comment out half; reproduce; comment out the remaining half; repeat. `git bisect` if the bug is recent.
|
|
58
|
+
- **Minimize the input**: shorter string, fewer rows, simpler config.
|
|
59
|
+
- **Swap in fakes**: if the bug reproduces with a fake DB, the bug isn't in the real DB.
|
|
60
|
+
|
|
61
|
+
### 3. Hypothesize
|
|
62
|
+
|
|
63
|
+
State, out loud or in writing, what you think is happening. One sentence.
|
|
64
|
+
|
|
65
|
+
> "When the cart is empty, checkout is calling `items[0]` and crashing."
|
|
66
|
+
|
|
67
|
+
A good hypothesis:
|
|
68
|
+
- Is specific (names a function, a condition).
|
|
69
|
+
- Is disprovable (there's an experiment that would show it's wrong).
|
|
70
|
+
- Explains the observed symptom AND the variations you've seen.
|
|
71
|
+
|
|
72
|
+
### 4. Test the hypothesis
|
|
73
|
+
|
|
74
|
+
Design the test that would disprove it.
|
|
75
|
+
|
|
76
|
+
- Add a print/log at the suspected line.
|
|
77
|
+
- Run with the minimal reproduction.
|
|
78
|
+
- Observe: does reality match the hypothesis?
|
|
79
|
+
|
|
80
|
+
If yes → proceed to fix.
|
|
81
|
+
If no → the hypothesis is wrong. Go back to step 3.
|
|
82
|
+
|
|
83
|
+
Don't let a wrong hypothesis linger. "It almost fits" is how debug sessions become five-hour goose chases.
|
|
84
|
+
|
|
85
|
+
### 5. Fix
|
|
86
|
+
|
|
87
|
+
Smallest correct change that fixes the bug and doesn't break other things.
|
|
88
|
+
|
|
89
|
+
- Add a test that would have caught this.
|
|
90
|
+
- Make the test fail.
|
|
91
|
+
- Apply the fix.
|
|
92
|
+
- Test passes.
|
|
93
|
+
- Other tests still pass.
|
|
94
|
+
|
|
95
|
+
Commit fix + test together.
|
|
96
|
+
|
|
97
|
+
### 6. Verify
|
|
98
|
+
|
|
99
|
+
- Run the test.
|
|
100
|
+
- Run the minimal reproduction.
|
|
101
|
+
- Run the original user scenario (if different).
|
|
102
|
+
- For production bugs: deploy to staging and verify there before prod.
|
|
103
|
+
|
|
104
|
+
Don't close the ticket until you've verified on the system where the bug was reported.
|
|
105
|
+
|
|
106
|
+
## Tools by abstraction level
|
|
107
|
+
|
|
108
|
+
When the bug hides, drop a layer.
|
|
109
|
+
|
|
110
|
+
| Level | Tools |
|
|
111
|
+
|---|---|
|
|
112
|
+
| **Logs** | grep, structured-log viewer, APM log search |
|
|
113
|
+
| **Metrics / dashboards** | Grafana, Datadog, CloudWatch |
|
|
114
|
+
| **Traces** | Jaeger, Tempo, DD APM |
|
|
115
|
+
| **Debugger** | `pdb`, IDE debuggers, `dlv`, `lldb` |
|
|
116
|
+
| **Profiler** | `pprof`, py-spy, perf, Instruments |
|
|
117
|
+
| **Network** | `tcpdump`, Wireshark, browser DevTools Network, `curl -v` |
|
|
118
|
+
| **System calls** | `strace` (Linux), `dtruss` (macOS) |
|
|
119
|
+
| **Kernel / hardware** | `perf`, eBPF, `iostat`, `top` |
|
|
120
|
+
|
|
121
|
+
You usually won't go below "traces". When you do, the bug was worth the depth.
|
|
122
|
+
|
|
123
|
+
## The log reading discipline
|
|
124
|
+
|
|
125
|
+
Read the entire trace, not just the top line.
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
ValidationError: email required
|
|
129
|
+
at validate (validate.py:23)
|
|
130
|
+
at create (service.py:41)
|
|
131
|
+
at handler (app.py:15) ← where the request started
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- **Top**: the immediate cause.
|
|
135
|
+
- **Middle**: the path that got there.
|
|
136
|
+
- **Bottom**: the entry point.
|
|
137
|
+
|
|
138
|
+
For multi-service requests, trace by **request id** across services. If you can't — fix that first.
|
|
139
|
+
|
|
140
|
+
## Debugging performance
|
|
141
|
+
|
|
142
|
+
Different but structurally similar:
|
|
143
|
+
|
|
144
|
+
1. **Measure**. Don't optimize without a number. `ab`, `k6`, `wrk` for throughput; APM for p95/p99.
|
|
145
|
+
2. **Profile**. Flame graph reveals the hot function. Guessing reveals nothing.
|
|
146
|
+
3. **Hypothesize the bottleneck**. "The SQL is slow" vs. "JSON serialization is slow" vs. "We're blocking on the main thread."
|
|
147
|
+
4. **Test with EXPLAIN / flame graph / profiler output**.
|
|
148
|
+
5. **Fix the highest-yield bottleneck**. Ignore the rest until you've re-measured.
|
|
149
|
+
|
|
150
|
+
Rule: **never optimize the 2% case while the 60% case is still on the table**.
|
|
151
|
+
|
|
152
|
+
## Debugging flaky tests
|
|
153
|
+
|
|
154
|
+
A flaky test is a bug. Treat it.
|
|
155
|
+
|
|
156
|
+
- **Shared mutable state** between tests — reset in setup / use fresh fixtures.
|
|
157
|
+
- **Order dependency** — tests depend on other tests' side effects.
|
|
158
|
+
- **Timing** — tests that wait for "done" via sleep; flip to deterministic waits.
|
|
159
|
+
- **Randomness** — uncontrolled random input; seed it.
|
|
160
|
+
- **External dependencies** — real network / time / env; mock or inject.
|
|
161
|
+
|
|
162
|
+
If you can't fix the flake in a week, DELETE the test. A flake that lies about whether the code works is worse than no test.
|
|
163
|
+
|
|
164
|
+
## Live incident
|
|
165
|
+
|
|
166
|
+
Debugging with a fire lit:
|
|
167
|
+
|
|
168
|
+
1. **Stop the bleeding first.** Roll back, disable a feature flag, scale up, divert traffic. Diagnose later.
|
|
169
|
+
2. **Preserve evidence** — snapshot logs, heap, DB state before you mitigate; you'll need them for the postmortem.
|
|
170
|
+
3. **One driver, many helpers**. One person coordinating; others investigate. Avoid overlapping operations.
|
|
171
|
+
4. **Communicate every 15 min** even if nothing new: "still investigating DB side; rollback started at 14:03".
|
|
172
|
+
5. **Fix the immediate symptom. Plan the durable fix.** Different timescales.
|
|
173
|
+
6. **Write the postmortem.** Always. Blameless. Drive action items to completion.
|
|
174
|
+
|
|
175
|
+
## Rubber-ducking
|
|
176
|
+
|
|
177
|
+
Explaining the problem, in full, in plain words, to anyone or anything:
|
|
178
|
+
- A colleague.
|
|
179
|
+
- A rubber duck on your desk.
|
|
180
|
+
- A paragraph in a doc.
|
|
181
|
+
|
|
182
|
+
Making the explanation forces you to sequence the facts; the sequence often exposes the missing step.
|
|
183
|
+
|
|
184
|
+
Most "aha!" moments during rubber-ducking come at "okay so X happens, then Y, then — wait, does Y actually happen?"
|
|
185
|
+
|
|
186
|
+
## Pair debugging
|
|
187
|
+
|
|
188
|
+
Two people, one keyboard. One describes their mental model, the other asks questions. Costly in time; often pays for itself on nasty bugs.
|
|
189
|
+
|
|
190
|
+
## Warning signs in your own process
|
|
191
|
+
|
|
192
|
+
- You've tried four fixes. None landed.
|
|
193
|
+
- You're re-running the test hoping it passes.
|
|
194
|
+
- You're editing code to "see what happens" without a hypothesis.
|
|
195
|
+
- You've been on the same bug for 3+ hours with no progress.
|
|
196
|
+
|
|
197
|
+
All of these say: **stop, step away, reset**. Take a walk. Explain the bug to someone. Sleep on it. You'll come back cheaper and more effective.
|
|
198
|
+
|
|
199
|
+
## Bugs that turn out to be "not bugs"
|
|
200
|
+
|
|
201
|
+
Always worth checking:
|
|
202
|
+
- **Timezone / DST** — off-by-one-hour bugs.
|
|
203
|
+
- **Locale** — decimal separators, date order, sort order.
|
|
204
|
+
- **Unicode** — grapheme cluster length vs. byte length; RTL order.
|
|
205
|
+
- **Float precision** — 0.1 + 0.2 ≠ 0.3.
|
|
206
|
+
- **Integer overflow** — counters that wrap.
|
|
207
|
+
- **Caches** — serving a stale copy.
|
|
208
|
+
- **Config drift** — dev has flag X on, prod doesn't.
|
|
209
|
+
- **Env variables** — typos, unset, accidentally committed.
|
|
210
|
+
|
|
211
|
+
When the bug is "it only happens in prod", it's usually one of these.
|
|
212
|
+
|
|
213
|
+
## Fixing responsibly
|
|
214
|
+
|
|
215
|
+
- Write a regression test BEFORE merging the fix.
|
|
216
|
+
- Describe what the test proves in the commit message.
|
|
217
|
+
- Link the original bug report / ticket.
|
|
218
|
+
- If the fix has broader implications, write an ADR.
|
|
219
|
+
|
|
220
|
+
## Forbidden patterns
|
|
221
|
+
|
|
222
|
+
- "Just add a try/except around it so it doesn't crash"
|
|
223
|
+
- Closing a ticket without a reproduction-proving test
|
|
224
|
+
- Rolling out a fix to prod before verifying on staging
|
|
225
|
+
- Shipping a fix and "hoping" it works
|
|
226
|
+
- Saying "it works on my machine" as a closing line
|
|
227
|
+
- Removing a test that's failing "to unblock CI"
|
|
228
|
+
- Blaming a user without reproducing the bug first
|
|
229
|
+
- Fixing the symptom when you know where the root cause is
|
|
230
|
+
|
|
231
|
+
## The two-question close
|
|
232
|
+
|
|
233
|
+
Before declaring "fixed":
|
|
234
|
+
|
|
235
|
+
1. **Do I have a test that would have failed before this change?**
|
|
236
|
+
2. **Do I know what caused the bug, not just what suppresses it?**
|
|
237
|
+
|
|
238
|
+
If either is "no", you haven't finished.
|
|
239
|
+
|
|
240
|
+
## Pair with
|
|
241
|
+
|
|
242
|
+
- [`coding-standards/references/tdd.md`](../coding-standards/references/tdd.md) — for the test-first fix discipline.
|
|
243
|
+
- [`qa-tester`](../qa-tester/SKILL.md) — for edge-case intuition.
|
|
244
|
+
- [`devops/references/observability.md`](../devops/references/observability.md) — tools for finding what you can't guess.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devops
|
|
3
|
+
description: DevOps end skill — CI/CD, containers, infrastructure-as-code, secrets, observability. Tool-neutral (GitHub Actions / GitLab CI / Argo / Terraform patterns). Pair with `backend`, language skills, and `coding-standards`.
|
|
4
|
+
origin: ecc-fork + original (https://github.com/affaan-m/everything-claude-code, MIT)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# DevOps
|
|
8
|
+
|
|
9
|
+
CI/CD, containers, infra-as-code, secrets, observability. **Tool-neutral** — patterns apply across GitHub Actions / GitLab CI / CircleCI, Terraform / Pulumi / CDK, Docker / OCI, Kubernetes / ECS / Cloud Run.
|
|
10
|
+
|
|
11
|
+
## When to load
|
|
12
|
+
|
|
13
|
+
- Setting up or reviewing CI/CD
|
|
14
|
+
- Containerization, image builds, multi-stage Dockerfiles
|
|
15
|
+
- Infrastructure-as-code changes (Terraform, Pulumi, CloudFormation, CDK)
|
|
16
|
+
- Secret management, rotation, access control
|
|
17
|
+
- Observability at the platform level (metrics / logs / traces collection, alerting)
|
|
18
|
+
- Deploy strategies (blue-green, canary, progressive delivery)
|
|
19
|
+
|
|
20
|
+
## Core principles
|
|
21
|
+
|
|
22
|
+
1. **Everything as code.** Infra, CI, secret policy, dashboards, alerts — in the repo, reviewed, versioned.
|
|
23
|
+
2. **Immutable artifacts.** The build produces one artifact (image, binary); the same artifact promotes through envs unchanged.
|
|
24
|
+
3. **Dev / staging / prod parity.** Same tooling, same topology, smaller. Differences are explicit (size, scaling, data), not accidental.
|
|
25
|
+
4. **Automate the path to prod.** Merges to main trigger deploy (with gates); humans click "promote", not "run these commands".
|
|
26
|
+
5. **Ephemeral infra, persistent data.** Nodes, pods, VMs — replaceable. Data — backed up, versioned, migrated.
|
|
27
|
+
6. **Least privilege by default.** CI, services, humans all get scoped credentials. Root access is an event, not a default.
|
|
28
|
+
7. **Fast feedback.** Build < 10 min on typical change, < 3 min on type/lint. Slow CI loses its purpose.
|
|
29
|
+
8. **Observability before features.** You can't fix what you can't see.
|
|
30
|
+
|
|
31
|
+
## How to use references
|
|
32
|
+
|
|
33
|
+
| Reference | When to load |
|
|
34
|
+
|---|---|
|
|
35
|
+
| [`references/ci-cd.md`](references/ci-cd.md) | Pipelines, caching, parallelism, artifacts, gates, promotion |
|
|
36
|
+
| [`references/containers.md`](references/containers.md) | Dockerfile, multi-stage, size, rootless, base images, image signing |
|
|
37
|
+
| [`references/iac.md`](references/iac.md) | Terraform / Pulumi / CDK — structure, state, modules, reviews |
|
|
38
|
+
| [`references/secrets.md`](references/secrets.md) | Secret managers, rotation, access control, pre-commit scanning |
|
|
39
|
+
| [`references/deploy.md`](references/deploy.md) | Rolling / blue-green / canary / feature flags, rollback |
|
|
40
|
+
| [`references/observability.md`](references/observability.md) | Log/metric/trace pipelines, alerting, on-call, runbooks |
|
|
41
|
+
|
|
42
|
+
## Forbidden patterns (auto-reject)
|
|
43
|
+
|
|
44
|
+
- Secrets in code / Dockerfile / CI config / `.env` in git
|
|
45
|
+
- CI pipelines that skip tests with `|| true` / `--continue-on-error`
|
|
46
|
+
- Pushing latest tag only (no immutable version for rollback)
|
|
47
|
+
- `curl | bash` from the internet in Dockerfile / install script without pinning
|
|
48
|
+
- Running containers as root without documented reason
|
|
49
|
+
- Terraform state on a local dev machine (no remote, no locking)
|
|
50
|
+
- Manual prod changes (clicking in a console) not followed by IaC update
|
|
51
|
+
- Deploy scripts that don't know how to roll back
|
|
52
|
+
- Alerts that wake someone up without a runbook
|
|
53
|
+
- Public S3 buckets / databases without explicit review
|
|
54
|
+
- `:latest` base image tags in prod builds
|
|
55
|
+
- Writing logs to the container filesystem (lost on restart)
|