{"node":{"id":"96995049-b5b2-43a2-b056-a786ffb32c33","handle":"9699","type":"note","authorName":"Vincent Yeung","intent":"recipe","title":"Pattern: making an existing module readable","aiSummary":"How to bring one existing module up to standard with an AI doing the work: comments to near zero by a three-sentence rule, a test for every behaviour a caller can notice, every disagreement between code, comment and test sorted into test gap, bug or product decision rather than smoothed over, nothing deleted without proof, and a fixed order of steps each green before the next. The counterpart to starting a new codebase. Can be turned into a skill.","content":"**When:** a module works, but you can't read it without its comments. You can't tell what its tests protect either. You want an AI to clean it up without changing what it does.\n\n**Built from:** starting a new codebase (the standard this brings an old module up to), agents, plan-execute-review.\n\n**As a skill:** ask the AI to turn this page into a skill that takes one module and runs the steps below, stopping at every product decision.\n\nWork on one module at a time, each on its own branch and pull request. Worked out over three real modules, each merged.\n\n**The goal.** Comments go to near zero. Every behaviour someone using the module can notice has a test that goes red when it breaks. No test is left that protects nothing. The code reads without its comments. And no bug gets hidden by quietly editing a comment or a test to match the code.\n\n**Comments: three sentences.**\n\n- If a comment describes something a user of the module can notice, a test must cover it, and the comment goes.\n- If it describes something nobody can notice, it goes, and nothing replaces it.\n- It survives only as a warning to the next editor that no test can express, and only with your approval.\n\nFour real comments:\n\n1. \"a failed upload puts the bytes back on the meter\". Someone can notice this. Find the test or write it, see it fail, then delete the comment.\n2. \"we check banned words before replaying the request\". Internal order. If changing the order changes what a caller gets back, it is case 1. If not, delete it and write nothing.\n3. \"see review round 3\", \"fixed after the June incident\". History. Delete.\n4. \"don't merge these two awaits: the file must be in storage before the row is written, or a crash leaves an orphan\". A warning. Even this usually becomes a test: make the second step fail and check what is left. The comment stays only when no test can say it.\n\nA warning that stays is written as a rule for the future, not a story: \"a recheck here looks equivalent but races, so this stays one statement\".\n\n**When code, comment, test and your records disagree.** Never fix it by editing one to match the others. Write down all four sides, then sort it into one of three:\n\n- **Test gap.** The code is right and nothing protected it. Write the test.\n- **Bug.** The code is wrong against something you recorded deciding. Fix it behind a test that goes red first.\n- **Product decision.** Nothing recorded says which is right. The AI stops and asks you. It never chooses.\n\nOnce you rule, the test that pins it is named in plain words for what you chose, such as \"a flag-shaped token is refused, never swallowed as a value\". The next reader knows it was chosen, not an accident. The test is the record. No document needed.\n\n**Nothing is deleted without proof.** A test is deleted only if you break the code it covers and it stays green. A comment is deleted as \"covered\" only if you break the behaviour it describes and the named test goes red. If the test stays green, the comment was not covered. That is a gap to fill, and the comment stays.\n\n**The order.** Each step is green before the next one starts.\n\n1. List everything before touching anything. A second AI on a different model writes this list, by reading alone:\n   - every comment sentence\n   - every test, and what it protects\n   - every disagreement\n   - every dependency outside the module\n2. Build the net: tests at the module's edge, on what a caller gets back and what state remains. Each one seen red first.\n3. Refactor for readability, under the net.\n4. Delete the comments. After the refactor, most describe code that now says it plainly.\n5. Trim the tests. What protects nothing goes, with its proof. Cases of the same shape become one table.\n6. Fix the bugs one at a time, each behind a test you saw go red before the fix and green after it.\n\nA code change has one of two licences. A bug fix, with a test that goes red when the fix is taken out. Or a refactor, which changes no assertion in the net. A change that needs a net assertion edited to stay green is a behaviour change, and without a bug behind it, it is refused.\n\n**Test at the lowest layer that can prove it.** Pull a rule out as a plain function when that is the reason to, not further, and test its edge cases beside it: zero, negative, over the limit, a time in the future, an unknown kind, missing. The service test then covers only what needs the database. Each layer up is smaller, because the layer below took the cases. Starting a new codebase holds what to test and what to skip.\n\n**Ask the third question.** Of every change, not only failures: what did this behaviour used to tell someone, and what tells them now? Collapsing tests can drop the one assertion that justified deleting a comment. A read that now always answers can stop raising the error that was someone's only signal.\n\n**Drain the docs too.** Every paragraph written about the module is sorted like a comment. What a newcomer would need and could not guess goes to your knowledge base. Behaviour goes into a test. History is deleted. Each run leaves less prose than it found.\n\n**No count is a target.** Record comment lines, tests per layer and suite time, before and after. Report them. Never chase them.\n\n**Three sessions, not one.** One AI writes the list, reading only. A different one builds. Before it starts, it answers the list row by row. A fresh one with no memory of the run reads the result against the list before you do. Questions go up to you, never sideways between them.\n\n**Questions for your own case**\n\n- Does this comment describe something a caller can notice?\n- If I break the code this test covers, does it go red?\n- Is this disagreement a test gap, a bug, or a choice only I can make?\n\n**Probe.** A session that has read only this page must answer without guessing:\n\n- A comment says \"a failed upload refunds the quota\" and a test is named for it. Can the comment be deleted now?\n- The code answers 404 for someone else's file, the comment says 403, and no test checks either. What happens next?\n- A refactor needs one edge test's expected value changed to stay green. Is it allowed?","capturedAt":"2026-10-02T05:28:07.749Z","createdAt":"2026-10-02T05:28:07.750Z","updatedAt":"2026-10-02T08:46:54.298Z","spanId":null,"contributor":null,"audiences":[{"kind":"public","createdAt":"2026-10-02T05:28:07.750Z"}],"fingerprint":"15b6f2dd62af48c75a881f33705047bbc3b961a61b8b2fc1d4b238af76e27c2f","revisionNo":"2","structureHash":"256f924aafb42eda972b8720755d31bea3ca8d5a0b95caacd3fb3ddfd4936af8","page":"https://lattris.com/by/b/memories/96995049-b5b2-43a2-b056-a786ffb32c33"},"links":[{"direction":"out","verb":"references","reason":"only if you need the full list of what to test and what to skip, or the codebase does not exist yet","hash":"4482e557f00c5f08f058dca42a8a76ff4d1897e83ae09e94dab8642e46dae828","node":{"id":"33705115-71bf-4415-9eb8-5c6afafeff7e","handle":"3370","type":"note","title":"Pattern: starting a new codebase","aiSummary":"One page of rules for starting a new codebase, in seven parts: project setup, the architecture rule (domain types in the middle, translation at the edges, outside systems behind ports), server, browser, testing (what to test and what to skip, every test seen failing once), security from the first day, and documents, comments and commits. Four parts hold for any language; three name a default stack of TypeScript, pnpm, NestJS, Postgres and Vue 3. Can be turned into a scaffolding skill.","erased":false,"href":"https://lattris.com/api/lattris/by/b/memories/33705115-71bf-4415-9eb8-5c6afafeff7e?links=1","fingerprint":"6c821f8815ca1b9180fa26cc1f58daca8c42b10df809322079ec4f140043f6ec","revisionNo":"1","structureHash":"b731cbeee3441f8882ba51edd414aba376f3f74b0ac75d072ee7ccd34e024edf","intent":"recipe","authorName":"Vincent Yeung","page":"https://lattris.com/by/b/memories/33705115-71bf-4415-9eb8-5c6afafeff7e"}},{"direction":"in","verb":"references","reason":"only if a module already exists and you want an AI to make it readable without changing what it does","hash":"9262cfd63499c0f474d1486533f40daf640d6ffa3d76befe1d55854588b3dc0b","node":{"id":"331896d2-2e9d-46b1-b56c-048a18ee23eb","handle":"3318","type":"note","title":"Patterns: a library","aiSummary":"Index of the author's patterns, one page each, built from the basics. Any pattern can be turned into a skill by asking the AI. So far: the handover, re-alignment, the clean summary, plan-execute-review, saving state for next time, knowledge reconciliation, starting a new codebase, and making an existing module readable. Grows as pages are added.","erased":false,"href":"https://lattris.com/api/lattris/by/b/memories/331896d2-2e9d-46b1-b56c-048a18ee23eb?links=1","fingerprint":"fdb88831a4949e6d2796f6f2ec65f612903ec02e487ecea76821973013ef633c","revisionNo":"9","structureHash":"ebf9a8041d4dce367db54d851720a324d11c8ecd3a5256931024e47d01638c7f","intent":"recipe","authorName":"Vincent Yeung","page":"https://lattris.com/by/b/memories/331896d2-2e9d-46b1-b56c-048a18ee23eb"}},{"direction":"in","verb":"references","reason":"only if the codebase already exists and a module in it needs bringing up to this standard","hash":"a3ec41d2d9605f4cde3aa5440581a4c16bee7f901aa34920021c8371df8ea2c3","node":{"id":"33705115-71bf-4415-9eb8-5c6afafeff7e","handle":"3370","type":"note","title":"Pattern: starting a new codebase","aiSummary":"One page of rules for starting a new codebase, in seven parts: project setup, the architecture rule (domain types in the middle, translation at the edges, outside systems behind ports), server, browser, testing (what to test and what to skip, every test seen failing once), security from the first day, and documents, comments and commits. Four parts hold for any language; three name a default stack of TypeScript, pnpm, NestJS, Postgres and Vue 3. Can be turned into a scaffolding skill.","erased":false,"href":"https://lattris.com/api/lattris/by/b/memories/33705115-71bf-4415-9eb8-5c6afafeff7e?links=1","fingerprint":"6c821f8815ca1b9180fa26cc1f58daca8c42b10df809322079ec4f140043f6ec","revisionNo":"1","structureHash":"b731cbeee3441f8882ba51edd414aba376f3f74b0ac75d072ee7ccd34e024edf","intent":"recipe","authorName":"Vincent Yeung","page":"https://lattris.com/by/b/memories/33705115-71bf-4415-9eb8-5c6afafeff7e"}}],"dictionary":{"lattris":"A personal knowledge base — how a person captures what they know as linked blocks they own and control. To understand how this knowledge base works: https://lattris.com/by/4/memories/lattris-a-memory-store-for-your-life-43ff","intents":"Every block can carry an intent — a label for how far you have taken the idea. The scale runs from noticing something to acting on it: noticed (something caught your attention), learning (you are working it out), truth (you are saying this is how things are), decision (you chose a course), action (you did it). Recipe sits outside that scale — it is not a stronger claim, it is a different kind of thing: a procedure, a how-to, a set of steps that ties other knowledge together. Intent is optional — leaving it blank just means you have not labelled it yet."},"instructions_to_ai":"This is a recipe from a personal knowledge base. Read it and apply it to what the person you're talking to is working on. Follow the linked blocks to explore further related topics if it might be useful to the user; open each at its page address. Start by letting the user know what the recipe is about and how it may benefit them."}