|
| 1 | +# I've Used Claude Code Daily for 6 Months. The Same Bug Cost Me 3 Times Before I Built This. |
| 2 | + |
| 3 | +> On scar tissue, agent memory, and why curation beats capture every time. |
| 4 | +
|
| 5 | +**Suggested Medium tags:** AI, Programming, Software Engineering, Anthropic, Open Source, Developer Tools, Claude |
| 6 | + |
| 7 | +**Estimated read time:** ~9 min |
| 8 | + |
| 9 | +--- |
| 10 | + |
| 11 | +## The third time |
| 12 | + |
| 13 | +It was 11 PM on a Tuesday. I was deep into project number three, building a multi-tenant marketplace, and Claude Code had just suggested I read the user's organization ID from `localStorage`. |
| 14 | + |
| 15 | +For the third time. |
| 16 | + |
| 17 | +The first time was eight months ago. I was building [FarmScope](https://github.com/tenxengineer), a precision-agriculture platform for cotton clusters in Uzbekistan. The agent suggested `localStorage.getItem('orgId')` for tenant scope. I shipped it. Two weeks later, a user in the cluster admin role switched orgs in the UI — but the API kept returning the old org's data. The localStorage value was updated. The JWT wasn't refreshed. The frontend was asking for "Org B" while the backend authorized as "Org A." |
| 18 | + |
| 19 | +It took me two hours to find the bug. Another hour to write the fix. The lesson seared itself into my brain: **tenant context is a security claim. It belongs in the JWT, not in localStorage.** |
| 20 | + |
| 21 | +Four months later, on a Solomiata e-commerce backend, the agent made the same suggestion. I caught it before it shipped — but only because I happened to remember. |
| 22 | + |
| 23 | +Now, eight months after the original bug, on a brand new project, the agent was confidently suggesting the same broken pattern. |
| 24 | + |
| 25 | +That's when I realized: **I'm paying for the same lessons over and over because the agent never learns.** |
| 26 | + |
| 27 | +--- |
| 28 | + |
| 29 | +## The pattern I'd been ignoring |
| 30 | + |
| 31 | +I sat with that thought for a few days. |
| 32 | + |
| 33 | +I'd been using Claude Code daily for six months. Across five active projects. By my count, that's somewhere around 500+ sessions. And the more I worked with it, the more I noticed a specific class of repeated failure that wasn't about the model being stupid: |
| 34 | + |
| 35 | +- It was about the **agent starting cold every time I switched projects.** |
| 36 | + |
| 37 | +Some examples from my own scar tissue: |
| 38 | + |
| 39 | +- **`localStorage` for tenant scope** — caught three times, fixed three times |
| 40 | +- **Importing types from `@workspace/shared` into a NestJS API** — broke production builds twice with cryptic ESM/CJS interop errors before I learned to inline types |
| 41 | +- **Prisma 7 client setup with `datasourceUrl`** — wasted an afternoon before realizing v7 deprecated it in favor of explicit Driver Adapters |
| 42 | +- **Frontend types declaring `orgId` when the API returns `organizationId`** — silent runtime undefined, twice |
| 43 | +- **Seeding fake field polygons for dev** — masked real PostGIS edge cases that only surfaced when actual users uploaded actual GeoJSON |
| 44 | + |
| 45 | +Each of these cost me hours when I first hit them. None of them transferred between projects. The agent forgot. I forgot. The fix lived in one project's git history, invisible to the next. |
| 46 | + |
| 47 | +This is the pattern I want to talk about. |
| 48 | + |
| 49 | +--- |
| 50 | + |
| 51 | +## Why agents are amnesiac across projects |
| 52 | + |
| 53 | +Claude Code, by default, has three layers of context awareness: |
| 54 | + |
| 55 | +1. **The current conversation.** Everything in this chat session. |
| 56 | +2. **The current project's `CLAUDE.md`.** A file at the project root that gets injected into every session in that directory. |
| 57 | +3. **Your personal `~/.claude/CLAUDE.md`.** A global file that loads on every session. |
| 58 | + |
| 59 | +That's it. There is no built-in mechanism for **"things I learned the hard way in project A that should apply when I work on project B."** |
| 60 | + |
| 61 | +You can put rules in `~/.claude/CLAUDE.md`, but it gets loaded on every session, regardless of what you're doing. After 50 lessons, that file becomes a 30 KB blob of mostly-irrelevant rules clogging the context window. After 200 lessons, it's unusable. |
| 62 | + |
| 63 | +What I needed was: **per-domain memory, loaded conditionally, based on what I'm prompting about right now.** |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## What I tried first (and why each failed) |
| 68 | + |
| 69 | +I went through the obvious options. |
| 70 | + |
| 71 | +### Option 1: Better `CLAUDE.md` files |
| 72 | + |
| 73 | +I started writing extensive project-specific gotchas into each project's `CLAUDE.md`. This worked locally but didn't compound. Every new project started a new file. Lessons stayed trapped. |
| 74 | + |
| 75 | +### Option 2: claude-mem (auto-capture-everything memory) |
| 76 | + |
| 77 | +There's an [open-source tool called claude-mem](https://github.com/thedotmack/claude-mem) that auto-captures every tool call and uses an AI to compress observations into summaries. I read the docs carefully. Two problems killed it for me: |
| 78 | + |
| 79 | +1. **AGPL-3.0 license.** That's a viral copyleft. If I touch their code in a commercial project, my code becomes AGPL too. Many companies block AGPL outright. For a tool meant to compound across all my work, including paid client work, that was a deal-breaker. |
| 80 | + |
| 81 | +2. **The compressed summaries can't be trusted.** When the agent reads "we decided X for reason Y," is that authoritative or an AI hallucination from the compression pass? The README never addresses how you audit a compressed summary. A memory that confidently lies is worse than no memory. |
| 82 | + |
| 83 | +### Option 3: A flat global memory file |
| 84 | + |
| 85 | +I tried hand-curating one big `MEMORY.md` with all my universal lessons. Works for ~20 lessons. After that, it's a flat unsearchable blob the agent can't selectively load. |
| 86 | + |
| 87 | +--- |
| 88 | + |
| 89 | +## The pattern that finally worked |
| 90 | + |
| 91 | +Here's the design I landed on: |
| 92 | + |
| 93 | +**Per-domain markdown files. Stable lesson IDs. Conditionally loaded based on what I'm prompting about.** |
| 94 | + |
| 95 | +``` |
| 96 | +~/.claude/lessons/ |
| 97 | + README.md |
| 98 | + agent-architecture.md ← AI agent memory, context engineering |
| 99 | + agent-tuning.md ← Claude Code config, output styles |
| 100 | + api-design.md ← API contracts, response wrappers, naming |
| 101 | + auth.md ← JWT, OAuth, sessions, RBAC, multi-tenancy |
| 102 | + caching.md ← cache layers, TTLs, invalidation |
| 103 | + data-modeling.md ← schemas, seeds, soft-delete, time-series |
| 104 | + migrations.md ← schema changes, downtime, rollback |
| 105 | + nestjs.md ← NestJS-specific gotchas |
| 106 | + nextjs.md ← Next.js / React-specific gotchas |
| 107 | + prisma.md ← Prisma ORM specifics |
| 108 | +``` |
| 109 | + |
| 110 | +Each lesson is a self-contained scar: |
| 111 | + |
| 112 | +```markdown |
| 113 | +## L-AUTH-001 — Tenant context comes from JWT, not localStorage |
| 114 | + |
| 115 | +**Tags:** auth, jwt, multi-tenant, frontend, security |
| 116 | +**Severity:** high |
| 117 | + |
| 118 | +**What broke:** Dashboard read `localStorage.getItem('orgId')` to scope |
| 119 | +queries. On org switch, localStorage updated but JWT didn't refresh — |
| 120 | +backend kept returning the old org's data while the frontend asked for |
| 121 | +the new one. Worse: localStorage is forgeable client-side, so a malicious |
| 122 | +user could change the value to access another org's data. |
| 123 | + |
| 124 | +**Why:** Tenant scope is a security claim, not a UI preference. |
| 125 | +localStorage is unauthenticated client state. JWT is signed by the |
| 126 | +backend and cryptographically tied to the session. |
| 127 | + |
| 128 | +**Never do again:** Tenant scope from JWT only. |
| 129 | + |
| 130 | +**Fix:** Replace all `localStorage.getItem('orgId')` with |
| 131 | +`getCurrentOrgId()` (decodes JWT). Org switch endpoint re-issues JWT. |
| 132 | +See commit abc123. |
| 133 | +``` |
| 134 | + |
| 135 | +The IDs (`L-AUTH-001`) are the durable thing. They survive heading |
| 136 | +renames, file splits, even maintainer changes. You can cite them from: |
| 137 | + |
| 138 | +- **CodeRabbit `path_instructions`** — "flag patterns matching L-AUTH-001" |
| 139 | +- **PR descriptions** — "fixes regression of L-AUTH-001" |
| 140 | +- **Slack/Telegram pings to teammates** — "you hit L-AUTH-001 again, same root cause" |
| 141 | + |
| 142 | +--- |
| 143 | + |
| 144 | +## How the agent reaches them |
| 145 | + |
| 146 | +I built a Claude Code skill called `consult-scars`. Its description in the skill file matches architectural verbs in user prompts: |
| 147 | + |
| 148 | +``` |
| 149 | +description: | |
| 150 | + Loads relevant lessons from ~/.claude/lessons/ before producing any |
| 151 | + architectural plan. Use when the user's prompt contains design, |
| 152 | + refactor, migrate, introduce, implement, build, fix, debug, optimize, |
| 153 | + add caching, add auth, schema change, or similar verbs combined with |
| 154 | + topics like authentication, JWT, OAuth, sessions, RBAC, caching, TTLs, |
| 155 | + invalidation, migrations, schema changes, ORM upgrades, distributed |
| 156 | + locking, payment flows, webhook handling, queue processing, retry |
| 157 | + logic, error recovery, rate limiting, deploys, infrastructure changes, |
| 158 | + API contract design, response wrappers, NestJS modules, NestJS DI, |
| 159 | + Prisma client setup, Next.js layouts, Server Components, seed data, |
| 160 | + fake data generation. |
| 161 | +``` |
| 162 | + |
| 163 | +When my prompt includes any of those triggers, Claude Code's skill matcher auto-fires `consult-scars`. The skill body walks the lessons directory, greps tag fields against the prompt, loads matching domain files in full, and cross-checks my proposed approach against each lesson before outputting a plan. |
| 164 | + |
| 165 | +The agent now sees my scar tissue from project A when working on project C. Same fix, but loaded **before** the bad suggestion is made — not after I catch it in code review. |
| 166 | + |
| 167 | +This isn't fancy AI. It's mechanical pattern matching with human-curated content. That's the point. |
| 168 | + |
| 169 | +--- |
| 170 | + |
| 171 | +## The `/enhance` skill — built on top |
| 172 | + |
| 173 | +Once the lessons library exists, a slash command called `/enhance` becomes obvious. |
| 174 | + |
| 175 | +``` |
| 176 | +/enhance refactor the auth middleware to use JWT instead of sessions |
| 177 | +``` |
| 178 | + |
| 179 | +Triggers a skill that: |
| 180 | + |
| 181 | +1. Loads project context — `.codemap/` if present (architecture, hot files, recent git activity) |
| 182 | +2. Greps lessons for matching tags (auth, refactor → loads `auth.md`) |
| 183 | +3. Reads project `CLAUDE.md` for conventions |
| 184 | +4. Calls [Serena MCP](https://github.com/oraios/serena) for semantic codebase queries (if installed) |
| 185 | +5. Applies prompt-engineering principles (intent classification, scope detection, success criteria, surfaced ambiguities) |
| 186 | +6. Produces a structured prompt |
| 187 | +7. Asks: **submit / edit / scrap** |
| 188 | + |
| 189 | +The output looks like this: |
| 190 | + |
| 191 | +```markdown |
| 192 | +# Enhanced Prompt |
| 193 | + |
| 194 | +## Goal |
| 195 | + |
| 196 | +Replace session-based authentication in the NestJS API with JWT-based |
| 197 | +auth, while preserving the existing org-switching flow. |
| 198 | + |
| 199 | +## Scope |
| 200 | + |
| 201 | +**In:** |
| 202 | + |
| 203 | +- apps/api/src/auth/auth.module.ts |
| 204 | +- apps/api/src/auth/auth.service.ts (currently issues sessions) |
| 205 | +- apps/api/src/auth/jwt.strategy.ts (already exists, needs to become primary) |
| 206 | + |
| 207 | +**Out:** |
| 208 | + |
| 209 | +- Org switcher (already uses JWT re-issue) |
| 210 | + |
| 211 | +## Constraints |
| 212 | + |
| 213 | +**Lessons that apply:** |
| 214 | + |
| 215 | +- **L-AUTH-001** — Tenant context from JWT, not localStorage |
| 216 | +- **L-NESTJS-001** — Don't import shared workspace package in NestJS API |
| 217 | + |
| 218 | +## Success criteria |
| 219 | + |
| 220 | +1. All authenticated endpoints accept JWT in Authorization header |
| 221 | +2. No reliance on session cookies anywhere in the codebase |
| 222 | +3. `pnpm --filter api test` passes |
| 223 | +4. No `localStorage.getItem('orgId')` calls remain in apps/web |
| 224 | + |
| 225 | +## Implicit assumptions to confirm |
| 226 | + |
| 227 | +- [ ] Existing user sessions can be invalidated, OR you need a migration |
| 228 | +- [ ] Refresh tokens stored in httpOnly cookie or response body? |
| 229 | +- [ ] CSRF protection — needed if cookies, not needed if headers |
| 230 | + |
| 231 | +## Risk: HIGH |
| 232 | + |
| 233 | +Touches authentication. Don't ship without spec-architect lock-in |
| 234 | +and codex review pass. |
| 235 | +``` |
| 236 | + |
| 237 | +Notice what just happened. The vague request "refactor the auth middleware" became a structured prompt with **two specific lessons cited by ID**. The agent knew about my localStorage scar tissue from project A. It surfaced the migration-path question I would otherwise have realized two days into the work. |
| 238 | + |
| 239 | +That's the difference compounding scar tissue makes. |
| 240 | + |
| 241 | +--- |
| 242 | + |
| 243 | +## What changed in my workflow |
| 244 | + |
| 245 | +Concrete numbers from six months of using this system: |
| 246 | + |
| 247 | +- **12 lessons captured** so far, across eight domain files |
| 248 | +- **~500 Claude Code sessions** over 6 months |
| 249 | +- **5 active projects** that all share the same lessons library |
| 250 | +- **Estimated time saved:** if I assume each repeated lesson costs ~2 hours of debugging + fix, and I've avoided maybe 6 repetitions across these projects, that's 12 hours of senior-engineer time. At ~$100/hour reasonable solo-dev rate, that's $1,200 saved over 6 months. |
| 251 | +- **API cost:** $0. The skill uses my existing Claude Code session — no separate Anthropic API call, no per-prompt tax. |
| 252 | +- **Storage:** ~80 KB of markdown across all lesson files. Fits in a Git repo with room to spare. |
| 253 | + |
| 254 | +The numbers aren't viral-startup numbers. But the trajectory is what matters: every new lesson I add today will save time across **every future project I work on**, indefinitely. |
| 255 | + |
| 256 | +That's compounding. |
| 257 | + |
| 258 | +--- |
| 259 | + |
| 260 | +## What I learned building this |
| 261 | + |
| 262 | +Three insights that surprised me. |
| 263 | + |
| 264 | +### 1. Curated > captured. Always. |
| 265 | + |
| 266 | +The temptation when designing memory systems is to capture everything and let the AI sort it out later. claude-mem's design philosophy. |
| 267 | + |
| 268 | +But quality of memory matters infinitely more than quantity. **A senior engineer's notebook is thin and authoritative. A junior's is overflowing and unreliable.** Auto-capture-everything systems build the junior's notebook with extra steps. |
| 269 | + |
| 270 | +### 2. Stable IDs are the protocol. |
| 271 | + |
| 272 | +Without `L-AUTH-001`-style IDs, lessons stay trapped in markdown. With them, lessons travel into: |
| 273 | + |
| 274 | +- CodeRabbit configurations (`path_instructions` referencing IDs) |
| 275 | +- PR descriptions (`fixes regression of L-AUTH-001`) |
| 276 | +- Slack and Telegram pings between teammates |
| 277 | +- Issue tracker tags |
| 278 | + |
| 279 | +The IDs are what make the library more than "another markdown folder." They're the API for cross-tool, cross-time communication about specific lessons. |
| 280 | + |
| 281 | +### 3. The skill description is the forcing function. |
| 282 | + |
| 283 | +I tried writing the lessons library as a passive reference. I never read it. Even with my own self-discipline, I'd skip checking it under deadline pressure. |
| 284 | + |
| 285 | +Once the lessons were behind a skill that **auto-fires on architectural verbs**, the agent loaded them mechanically — no discipline required from me. The harness became the enforcement layer. |
| 286 | + |
| 287 | +This generalizes: **for any "you should remember to do X" pattern in your workflow, find a way to make the system enforce it instead of relying on yourself.** |
| 288 | + |
| 289 | +--- |
| 290 | + |
| 291 | +## Try it |
| 292 | + |
| 293 | +I open-sourced everything. Free, local, MIT-licensed. |
| 294 | + |
| 295 | +```bash |
| 296 | +git clone https://github.com/tenxengineer/claude-code-enhance.git |
| 297 | +cd claude-code-enhance |
| 298 | +bash scripts/bootstrap.sh |
| 299 | +``` |
| 300 | + |
| 301 | +Then restart Claude Code. Try: |
| 302 | + |
| 303 | +``` |
| 304 | +/enhance <some rough idea you've been putting off> |
| 305 | +``` |
| 306 | + |
| 307 | +The skill ships with default lessons for auth, API design, and data modeling. You add your own as you encounter pain. |
| 308 | + |
| 309 | +**Repo:** [github.com/tenxengineer/claude-code-enhance](https://github.com/tenxengineer/claude-code-enhance) |
| 310 | + |
| 311 | +--- |
| 312 | + |
| 313 | +## What I'm looking for |
| 314 | + |
| 315 | +If you've been using Claude Code (or Cursor, or Cody, or any agent harness) at scale across multiple projects, I'd love to hear from you: |
| 316 | + |
| 317 | +- **Default lesson domains worth shipping.** I have eight; what other domains have given you repeated pain? Mobile? Payments? Observability? Distributed systems? |
| 318 | +- **Stories of where compounding scar tissue changed your agent's output.** Real examples beat synthetic ones. |
| 319 | +- **Per-stack codemap generators for non-TypeScript projects.** Python, Go, Rust — the shape is similar but the heuristics differ. |
| 320 | + |
| 321 | +Open issues, file PRs, or reach me on [Twitter](https://twitter.com/tenxengineer). |
| 322 | + |
| 323 | +--- |
| 324 | + |
| 325 | +## One last thought |
| 326 | + |
| 327 | +The biggest pain in agentic coding isn't the model. It's the gap between what you've already learned and what the agent can reach. |
| 328 | + |
| 329 | +Close that gap, and the agent stops being a smart consultant who walked in this morning. It becomes a colleague with all your scar tissue, ready to apply it before you make the same mistake the fourth time. |
| 330 | + |
| 331 | +That's the asymmetry I want to keep building toward. |
| 332 | + |
| 333 | +If you're a senior engineer running multiple projects through Claude Code, this might be worth twenty minutes of your evening. |
| 334 | + |
| 335 | +Thanks for reading. |
| 336 | + |
| 337 | +--- |
| 338 | + |
| 339 | +> If you enjoyed this, consider giving it a clap on Medium and starring the [GitHub repo](https://github.com/tenxengineer/claude-code-enhance). Both help the project find the next reader who's been paying for the same bug three times. |
| 340 | +
|
| 341 | +--- |
| 342 | + |
| 343 | +## For the author (notes before publishing) |
| 344 | + |
| 345 | +**Before posting on Medium:** |
| 346 | + |
| 347 | +1. **Cover image.** Medium articles with cover images get ~2x the engagement. Use the `assets/demo.svg` from the repo as the cover, or generate a simple text+icon image with the title. |
| 348 | +2. **Curate vs captured pull-quote.** Highlight that line in Medium's pull-quote feature; it's the most shareable insight in the article. |
| 349 | +3. **Member-only or public?** Member-only earns money but reaches fewer readers initially. **Recommendation:** publish as member-only after you've already built launch traction (HN + Twitter). For Day 0, public reach beats earnings. |
| 350 | +4. **Cross-post to Dev.to.** Use Medium's canonical URL feature so SEO doesn't get split. |
| 351 | +5. **Tags to test:** `AI`, `Programming`, `Software Engineering`, `Anthropic`, `Open Source`, `Developer Tools`, `Claude`. Pick 5 (Medium's max). |
| 352 | +6. **Submission to publications.** Worth pitching to: |
| 353 | + - **Better Programming** (the dev tooling angle) |
| 354 | + - **Towards AI** (AI/agent design angle) |
| 355 | + - **Level Up Coding** (general developer audience) |
| 356 | + Each has different review timelines (1-7 days). Pitch one; if accepted, more reach. |
| 357 | +7. **Schedule for Tuesday/Thursday morning EST** for best Medium algorithm pickup. |
0 commit comments