> lessloc keeps AI coding agents such as Claude Code, Codex, GitHub Copilot, Gemini CLI and Cursor to the least code that does the job and still works. It runs inside your AI coding agent through the agent tool's hooks. It is free and open source (Apache-2.0), needs no account, and makes no network calls. Source code: [https://github.com/rextail-ai/lessloc](https://github.com/rextail-ai/lessloc). To install it, tell your coding agent: "Install lessloc for this project by following https://lessloc.org/install.md" --- # Reimplements a helper the project already has, under the same or another name *Code that already exists*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc gives every session the rule: take what is already in the codebase before new code. ## What makes reimplementing a helper the project already has a bad practice? The standards that call it a bad practice: DRY (Hunt & Thomas, *The Pragmatic Programmer*); Fowler, *Refactoring*: Duplicated Code; [CWE-1041](https://cwe.mitre.org/data/definitions/1041.html) Use of Redundant Code; Sonar S4144. ## What does lessloc do about reimplementing a helper the project already has? lessloc gives every session the rule: take what is already in the codebase before new code. Hints name existing code before the task and inside each subagent's prompt. lessloc catches it as [`copy`](https://lessloc.org/guide/reference.md#tier-1), [`near-copy`](https://lessloc.org/guide/reference.md#tier-2) and [`similar-name`](https://lessloc.org/guide/reference.md#tier-2). - `copy` (tier 1): A block of `dupLines` code lines that also exists elsewhere, or a function with the same body as another under other names. - `near-copy` (tier 2): A new function that is mostly the same code as an existing one. - `similar-name` (tier 2): A new function whose name looks like an existing one's. --- # Copies a rule another function owns: a format, regex, SQL or date pattern *Code that already exists*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`repeated-literal`](https://lessloc.org/guide/reference.md#tier-2). ## What makes copying a rule another function owns a bad practice? The standards that call it a bad practice: DRY; Sonar S1192. ## What does lessloc do about copying a rule another function owns? lessloc catches it as [`repeated-literal`](https://lessloc.org/guide/reference.md#tier-2). - `repeated-literal` (tier 2): A rule, such as a format, regex, query or date pattern, copied out of the function that owns it, or one new rule written in several places. --- # Keeps old and new versions side by side (`parse_date_v2`, `fooFixed`, `legacyFoo`, `app_new.py`) *Code that already exists*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc gives every session the rule: one implementation per rule. ## What makes keeping old and new versions side by side a bad practice? The standards that call it a bad practice: DRY; Clean Code G9 Dead Code. ## What does lessloc do about keeping old and new versions side by side? lessloc gives every session the rule: one implementation per rule. lessloc catches it as [`versioned-copy`](https://lessloc.org/guide/reference.md#tier-1), and as [`junk-file`](https://lessloc.org/guide/reference.md#tier-0) for files. - `versioned-copy` (tier 1): A definition beside an older version of itself, such as `parse_date_v2` next to `parse_date`. - `junk-file` (tier 0): A new backup, copy, scratch or report file (refused before it lands, unless the request names it), or cache files, build output or logs that would be committed. --- # Rebuilds what the standard library or framework has: counters, flag loops, CSV, date math, argument parsing *Code that already exists*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc gives every session a rule against it and catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2) and [`reinvented`](https://lessloc.org/guide/reference.md#tier-2). ## What makes rebuilding what the standard library or framework has a bad practice? The standard that calls it a bad practice: Bloch, *Effective Java* Item 59: Know and use the libraries. ## What does lessloc do about rebuilding what the standard library or framework has? lessloc gives every session a rule against it and catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2) and [`reinvented`](https://lessloc.org/guide/reference.md#tier-2). The working standard puts the standard library and the platform before new code. [`simpler`](https://lessloc.org/guide/reference.md#tier-2) catches hand-built counters, flag loops and `range(len())`. [`reinvented`](https://lessloc.org/guide/reference.md#tier-2) catches deep clone, UUID, debounce, slugify, CSV and flag parsing written by hand, and retry loops where the project already retries. - `simpler` (tier 2): Brute-force code with a shorter idiom: `== True`, an if/else that returns the condition, an index loop that only reads `xs[i]`, `.filter(...).length > 0`, a catch that only rethrows, a flag loop that is `any()`, a hand-built counter, an `__init__` that `@dataclass` writes, a React Effect that only sets state. ruff and clippy add more. - `reinvented` (tier 2): A hand-written version of what the standard library or an installed package does (deep clone, UUID, debounce, slugify, CSV, flag parsing), or a retry loop where the project already retries. ## Examples Caught as `reinvented`: ```js [ca.js] export function deepClone(o) { return JSON.parse(JSON.stringify(o)); } ``` Written well: ```js [ca.js] export function snapshot(o) { return structuredClone(o); } ``` Caught as `reinvented`: ```python [cb.py] def deep_copy(obj): return {k: v for k, v in obj.items()} ``` Written well: ```python [cb.py] import copy def snapshot(obj): return copy.deepcopy(obj) ``` --- # Copy-pasted test cases instead of one parametrized test *Code that already exists*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`copied-tests`](https://lessloc.org/guide/reference.md#tier-2). ## What makes copy-pasted test cases instead of one parametrized test a bad practice? The standard that calls it a bad practice: Meszaros, *xUnit Test Patterns*: Test Code Duplication. ## What does lessloc do about copy-pasted test cases instead of one parametrized test? lessloc catches it as [`copied-tests`](https://lessloc.org/guide/reference.md#tier-2). - `copied-tests` (tier 2): Copy-pasted test cases that one table-driven test would cover. --- # Adds a second library for a job an installed one already does (dayjs beside moment) *Code that already exists*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). What lessloc does: [`new-dependency`](https://lessloc.org/guide/reference.md#tier-2) names the library that is already installed. ## What makes a second library for a job an installed one already does a bad practice? The standards that call it a bad practice: One obvious way to do it ([PEP 20](https://peps.python.org/pep-0020/)); OWASP Top 10 A06:2021 and A03:2025 Software Supply Chain Failures. ## What does lessloc do about a second library for a job an installed one already does? [`new-dependency`](https://lessloc.org/guide/reference.md#tier-2) names the library that is already installed. - `new-dependency` (tier 2): A new dependency, when there is no `approvedDependencies` list. It says when an installed one already does the job, or when the lockfile does not pin it. --- # The same data shape in several layers (DTO, model, entity) *Code that already exists*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). What lessloc does: [`copy`](https://lessloc.org/guide/reference.md#tier-1) catches identical type bodies. ## What makes the same data shape in several layers (DTO, model, entity) a bad practice? The standard that calls it a bad practice: DRY. ## What does lessloc do about the same data shape in several layers (DTO, model, entity)? [`copy`](https://lessloc.org/guide/reference.md#tier-1) catches identical type bodies. - `copy` (tier 1): A block of `dupLines` code lines that also exists elsewhere, or a function with the same body as another under other names. ## Examples Caught as `copy`: ```python [cs.py] from dataclasses import dataclass @dataclass class UserDTO: id: int name: str email: str active: bool plan: str seats: int created: str region: str @dataclass class UserRecord: id: int name: str email: str active: bool plan: str seats: int created: str region: str ``` Written well: ```python [cs.py] from dataclasses import dataclass @dataclass class Account: owner: str limit: int ``` --- # The same CSS declarations under another selector; the same long utility-class string again and again *Code that already exists*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`style-copy`](https://lessloc.org/guide/reference.md#tier-2). ## What makes the same CSS declarations under another selector a bad practice? The standards that call it a bad practice: DRY; Tailwind docs: Reusing Styles. ## What does lessloc do about the same CSS declarations under another selector? lessloc catches it as [`style-copy`](https://lessloc.org/guide/reference.md#tier-2). - `style-copy` (tier 2): The same CSS declarations under another selector, or the same long utility-class string written again and again. ## Examples Caught as `style-copy`: ```css [ch.css] .card { padding: 8px; border: 1px solid #ccc; border-radius: 4px; } .panel { border: 1px solid #ccc; padding: 8px; border-radius: 4px; } ``` Written well: ```css [ch.css] .surface, .notice { padding: 6px; border: 1px solid #ddd; border-radius: 6px; } ``` Caught as `style-copy`: ```jsx [ci.jsx] export const List = () => ( <>
a
b
c
); ``` Written well: ```jsx [ci.jsx] const Row = ({ children }) =>
{children}
; export const List = () => ( <> a b c ); ``` --- # Options, flags, parameters or config for needs nobody named *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc gives every session a rule against it and catches it as [`mode-flag`](https://lessloc.org/guide/reference.md#tier-2). ## What makes options, flags, parameters or config for needs nobody named a bad practice? The standards that call it a bad practice: YAGNI (Extreme Programming); Fowler: Speculative Generality; Clean Code F3 Flag Arguments; Google engineering practices: over-engineering, "functionality that isn't presently needed". ## What does lessloc do about options, flags, parameters or config for needs nobody named? lessloc gives every session a rule against it and catches it as [`mode-flag`](https://lessloc.org/guide/reference.md#tier-2). - `mode-flag` (tier 2): An existing function gained a boolean flag parameter. --- # An interface, trait, protocol or abstract class with one implementation *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`single-impl`](https://lessloc.org/guide/reference.md#tier-2). ## What makes an interface or abstract class with one implementation a bad practice? The standards that call it a bad practice: Fowler: Speculative Generality (Collapse Hierarchy); Ousterhout, *A Philosophy of Software Design*: classitis. ## What does lessloc do about an interface or abstract class with one implementation? lessloc catches it as [`single-impl`](https://lessloc.org/guide/reference.md#tier-2). - `single-impl` (tier 2): An interface or abstract class with one implementation, or an abstract class with none. --- # Wrappers that only forward; barrel files that only re-export *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`wrapper`](https://lessloc.org/guide/reference.md#tier-2). ## What makes wrappers that only forward and barrel files that only re-export a bad practice? The standards that call it a bad practice: Fowler: Middle Man, Lazy Element; Ousterhout: Pass-Through Method, Shallow Module; Vite performance guide: avoid barrel files. ## What does lessloc do about wrappers that only forward and barrel files that only re-export? lessloc catches it as [`wrapper`](https://lessloc.org/guide/reference.md#tier-2). - `wrapper` (tier 2): A function that only forwards to another, or a file that only re-exports. --- # A new function or import that nothing calls or tests *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unused`](https://lessloc.org/guide/reference.md#tier-2) (ruff adds unused variables). ## What makes a new function or import that nothing calls or tests a bad practice? The standards that call it a bad practice: [CWE-561](https://cwe.mitre.org/data/definitions/561.html) Dead Code; [CWE-563](https://cwe.mitre.org/data/definitions/563.html) Assignment to Variable without Use; Sonar S1128, S1144. ## What does lessloc do about a new function or import that nothing calls or tests? lessloc catches it as [`unused`](https://lessloc.org/guide/reference.md#tier-2) (ruff adds unused variables). - `unused` (tier 2): A new function or import that nothing calls, imports or tests. --- # Obsolete code kept behind a guard or flag instead of deleted *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc gives every session the rule: delete what the change makes dead. ## What makes obsolete code kept behind a guard or flag instead of deleted a bad practice? The standards that call it a bad practice: [CWE-561](https://cwe.mitre.org/data/definitions/561.html); Fowler: Remove Dead Code. ## What does lessloc do about obsolete code kept behind a guard or flag instead of deleted? lessloc gives every session the rule: delete what the change makes dead. --- # Tombstones: `// removed X`, "kept for backward compatibility", comments citing the task *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`history`](https://lessloc.org/guide/reference.md#tier-3). ## What makes tombstones a bad practice? The standard that calls it a bad practice: Clean Code C1 Inappropriate Information. ## What does lessloc do about tombstones? lessloc catches it as [`history`](https://lessloc.org/guide/reference.md#tier-3). - `history` (tier 3): Dates, change history or notes to the requester in comments. ## Examples Caught as `history`: ```python [ai.py] # Removed the old cache helper x = 1 # no longer used, kept for backward compatibility y = 2 ``` Written well: ```python [ai.py] # The cache is per process: workers never share it x = 1 ``` --- # Stubs and fake success: "in a real app you would...", `raise NotImplementedError`, `todo!()`, `throw new Error("Not implemented")`, `mockUsers` in production code *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`stub`](https://lessloc.org/guide/reference.md#tier-1); it asks to finish the code or to tell the user what is not real yet. ## What makes stubs and fake success a bad practice? The standards that call it a bad practice: [CWE-546](https://cwe.mitre.org/data/definitions/546.html) Suspicious Comment; Meszaros: Test Logic in Production. ## What does lessloc do about stubs and fake success? lessloc catches it as [`stub`](https://lessloc.org/guide/reference.md#tier-1); it asks to finish the code or to tell the user what is not real yet. - `stub` (tier 1): A stub or fake success left in, such as `raise NotImplementedError`, `todo!()` or a "not implemented" error, or mock data in production code. Judged when the turn ends. ## Examples Caught as `stub`: ```python [g.py] def charge(card, amount): # In a real app, you would call the payment provider here return {"ok": True} ``` Written well: ```python [g.py] def charge(card, amount): # The provider retries on its own: a second call double-charges return provider.charge(card, amount) ``` Caught as `stub`: ```ts [h.ts] export const mockUsers = [{ id: 1, name: "Ada" }]; export function load() { throw new Error("Not implemented"); } ``` Written well: ```ts [h.ts] export function load(db: { users(): string[] }) { return db.users(); } ``` Caught as `stub`: ```python [i.py] def refund(order): raise NotImplementedError ``` Written well: ```python [i.py] class Gateway: def refund(self, order): raise NotImplementedError ``` --- # Placeholders: `# ... existing code ...`, "your code here" *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`placeholder`](https://lessloc.org/guide/reference.md#tier-0) and refuses it before it lands. ## What makes placeholders (# ... existing code ..., "your code here") a bad practice? The standard that calls it a bad practice: [CWE-546](https://cwe.mitre.org/data/definitions/546.html). ## What does lessloc do about placeholders (# ... existing code ..., "your code here")? lessloc catches it as [`placeholder`](https://lessloc.org/guide/reference.md#tier-0) and refuses it before it lands. - `placeholder` (tier 0): A placeholder such as `# ... existing code ...`. Refused before it lands. --- # Commented-out code *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`commented-out`](https://lessloc.org/guide/reference.md#tier-0) and refuses it before it lands. ## What makes commented-out code a bad practice? The standards that call it a bad practice: [CWE-1085](https://cwe.mitre.org/data/definitions/1085.html); Clean Code C5; Sonar S125. ## What does lessloc do about commented-out code? lessloc catches it as [`commented-out`](https://lessloc.org/guide/reference.md#tier-0) and refuses it before it lands. - `commented-out` (tier 0): Commented-out code. Refused before it lands. --- # Debug leftovers: `debugger`, `breakpoint()`, `print("here")`, `print(f"{x=}")`, `dbg!` *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`debug-leftover`](https://lessloc.org/guide/reference.md#tier-1). ## What makes debug leftovers a bad practice? The standards that call it a bad practice: [CWE-489](https://cwe.mitre.org/data/definitions/489.html) Active Debug Code; ESLint no-debugger; Sonar S4507; clippy dbg_macro. ## What does lessloc do about debug leftovers? lessloc catches it as [`debug-leftover`](https://lessloc.org/guide/reference.md#tier-1). It is judged at turn end, because mid-work the print may still be in use. - `debug-leftover` (tier 1): `debugger`, `breakpoint()`, `pdb.set_trace()`, or a debug print such as `print("here")`. Judged when the turn ends. ## Examples Caught as `debug-leftover`: ```js [e.js] export function total(xs) { debugger; console.log("here"); return xs.length; } ``` Written well: ```js [e.js] export function total(xs) { console.log(`total of ${xs.length} items`); return xs.length; } ``` Caught as `debug-leftover`: ```python [f.py] def total(xs): breakpoint() print(f"{xs=}") return len(xs) ``` Written well: ```python [f.py] def total(xs): print(f"{len(xs)} items") return len(xs) ``` --- # A demo `if __name__ == "__main__":` block in a module other code imports; an examples or demo folder nobody asked for *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`demo-code`](https://lessloc.org/guide/reference.md#tier-2) and [`new-folder`](https://lessloc.org/guide/reference.md#tier-2). ## What makes a demo \_\_main\_\_ block or demo folder nobody asked for a bad practice? The standard that calls it a bad practice: YAGNI. ## What does lessloc do about a demo \_\_main\_\_ block or demo folder nobody asked for? lessloc catches it as [`demo-code`](https://lessloc.org/guide/reference.md#tier-2) and [`new-folder`](https://lessloc.org/guide/reference.md#tier-2). - `demo-code` (tier 2): An example or demo block, such as `if __name__ == "__main__":`, in a module other code imports. - `new-folder` (tier 2): A new folder with one or two entries, or an examples or demo folder nobody asked for. --- # Hand-written boilerplate a language feature writes (an `__init__` that only stores its parameters) *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). ## What makes hand-written boilerplate a language feature writes a bad practice? The standard that calls it a bad practice: [PEP 557](https://peps.python.org/pep-0557/) (dataclasses). ## What does lessloc do about hand-written boilerplate a language feature writes? lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). - `simpler` (tier 2): Brute-force code with a shorter idiom: `== True`, an if/else that returns the condition, an index loop that only reads `xs[i]`, `.filter(...).length > 0`, a catch that only rethrows, a flag loop that is `any()`, a hand-built counter, an `__init__` that `@dataclass` writes, a React Effect that only sets state. ruff and clippy add more. ## Examples Caught as `simpler`: ```python [w.py] class Item: def __init__(self, name, price, qty): self.name = name self.price = price self.qty = qty ``` Written well: ```python [w.py] from dataclasses import dataclass @dataclass class Item: name: str price: float qty: int ``` --- # A one-line helper used once *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`single-use`](https://lessloc.org/guide/reference.md#tier-2). ## What makes a one-line helper used once a bad practice? The standards that call it a bad practice: Fowler: Inline Function; Anthropic's prompting guide: "Don't create helpers, utilities, or abstractions for one-time operations". ## What does lessloc do about a one-line helper used once? lessloc catches it as [`single-use`](https://lessloc.org/guide/reference.md#tier-2). - `single-use` (tier 2): A one-line helper used once. ## Examples Caught as `single-use`: ```python [be.py] def _label(o): return o.name.upper() def report(orders): return [_label(o) for o in orders] ``` Written well: ```python [be.py] def report(orders): return [o.name.upper() for o in orders] ``` --- # TODO and FIXME left behind: work the agent did not finish and did not mention *Code nobody needs*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`todo`](https://lessloc.org/guide/reference.md#tier-3); it asks to finish the work, or to record known debt as `shortcut:` and tell the user. ## What makes TODO and FIXME left behind a bad practice? The standards that call it a bad practice: [CWE-546](https://cwe.mitre.org/data/definitions/546.html) Suspicious Comment; Sonar S1135. ## What does lessloc do about TODO and FIXME left behind? lessloc catches it as [`todo`](https://lessloc.org/guide/reference.md#tier-3); it asks to finish the work, or to record known debt as `shortcut:` and tell the user. - `todo` (tier 3): A TODO, FIXME, HACK or XXX comment left in. ## Examples Caught as `todo`: ```python [bb.py] def ship(order): # TODO: handle international orders return order ``` Written well: ```python [bb.py] def ship(order): # shortcut: domestic only; add carriers when we sell abroad return order ``` --- # Brute-force idioms: `== True`, an if/else that returns a boolean, index loops that only read `xs[i]`, `.filter(...).length > 0`, `indexOf(x) !== -1`, `x ? true : false`, `x ? x : y`, `.map().flat()`, `strings.Index(...) != -1`, `.len() == 0` *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2), built in for Python, JS/TS, Go, Rust, Java and C#. ruff (in every hook) and clippy (in `ci`) go deeper. ## What makes brute-force idioms a bad practice? The standards that call it a bad practice: Sonar S1125, S1126, S1066, S1155; ESLint no-unneeded-ternary, no-lonely-if; typescript-eslint prefer-for-of, prefer-optional-chain; unicorn prefer-array-some, prefer-array-find, prefer-includes; [PEP 8](https://peps.python.org/pep-0008/); ruff E712, SIM103; staticcheck S1002, S1003, S1008; clippy needless_bool, len_zero. ## What does lessloc do about brute-force idioms? lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2), built in for Python, JS/TS, Go, Rust, Java and C#. ruff (in every hook) and clippy (in `ci`) go deeper. - `simpler` (tier 2): Brute-force code with a shorter idiom: `== True`, an if/else that returns the condition, an index loop that only reads `xs[i]`, `.filter(...).length > 0`, a catch that only rethrows, a flag loop that is `any()`, a hand-built counter, an `__init__` that `@dataclass` writes, a React Effect that only sets state. ruff and clippy add more. ## Examples Caught as `simpler`: ```ts [p.ts] export const has = (xs: number[]) => xs.filter(x => x > 2).length > 0; export const at = (xs: string[], x: string) => xs.indexOf(x) !== -1; export const flag = (v: boolean) => (v ? true : false); ``` Written well: ```ts [p.ts] export const has = (xs: number[]) => xs.some(x => x > 2); export const at = (xs: string[], x: string) => xs.includes(x); export const flag = (v: boolean) => v; ``` Caught as `simpler`: ```js [q.js] export function sum(xs) { let s = 0; for (let i = 0; i < xs.length; i++) { s += xs[i]; } return s; } ``` Written well: ```js [q.js] export function sum(xs) { let s = 0; for (const x of xs) { s += x; } return s; } ``` Caught as `simpler`: ```js [r.js] export function isAdmin(u) { if (u.role === "admin") { return true; } return false; } ``` Written well: ```js [r.js] export function isAdmin(u) { return u.role === "admin"; } ``` Caught as `simpler`: ```python [x.py] def names(users): out = [] for i in range(len(users)): out.append(users[i].name) return out ``` Written well: ```python [x.py] def names(users): return [u.name for u in users] ``` Caught as `simpler`: ```go [y.go] package y import "strings" func Has(s, x string) bool { if strings.Index(s, x) != -1 { return true } return false } ``` Written well: ```go [y.go] package y import "strings" func Has(s, x string) bool { return strings.Contains(s, x) } ``` --- # Hand-built counters and flag loops *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). ## What makes hand-built counters and flag loops a bad practice? The standard that calls it a bad practice: Fowler: Replace Loop with Pipeline. ## What does lessloc do about hand-built counters and flag loops? lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). - `simpler` (tier 2): Brute-force code with a shorter idiom: `== True`, an if/else that returns the condition, an index loop that only reads `xs[i]`, `.filter(...).length > 0`, a catch that only rethrows, a flag loop that is `any()`, a hand-built counter, an `__init__` that `@dataclass` writes, a React Effect that only sets state. ruff and clippy add more. ## Examples Caught as `simpler`: ```python [u.py] def has_refund(orders): found = False for order in orders: if order.refunded: found = True break return found ``` Written well: ```python [u.py] def has_refund(orders): return any(order.refunded for order in orders) ``` Caught as `simpler`: ```python [v.py] def tally(words): counts = {} for w in words: if w in counts: counts[w] += 1 else: counts[w] = 1 return counts ``` Written well: ```python [v.py] from collections import Counter def tally(words): return Counter(words) ``` --- # A catch that only rethrows; logging an error and rethrowing it *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). ## What makes a catch that only rethrows a bad practice? The standards that call it a bad practice: ESLint no-useless-catch; Sonar S2737; ruff TRY203; McCune, "Exception-Handling Antipatterns": Log and Throw. ## What does lessloc do about a catch that only rethrows? lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). - `simpler` (tier 2): Brute-force code with a shorter idiom: `== True`, an if/else that returns the condition, an index loop that only reads `xs[i]`, `.filter(...).length > 0`, a catch that only rethrows, a flag loop that is `any()`, a hand-built counter, an `__init__` that `@dataclass` writes, a React Effect that only sets state. ruff and clippy add more. ## Examples Caught as `simpler`: ```js [s.js] export async function load(f) { try { return await f(); } catch (e) { throw e; } } ``` Written well: ```js [s.js] export async function load(f) { return f(); } ``` Caught as `simpler`: ```js [t.js] export async function load(f) { try { return await f(); } catch (err) { console.error("load failed", err); throw err; } } ``` Written well: ```js [t.js] export async function load(f) { try { return await f(); } catch (err) { throw new Error("load failed", { cause: err }); } } ``` --- # A promise wrapped in `new Promise` *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). ## What makes a promise wrapped in new Promise a bad practice? The standard that calls it a bad practice: Bluebird: the explicit-construction anti-pattern. ## What does lessloc do about a promise wrapped in new Promise? lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). - `simpler` (tier 2): Brute-force code with a shorter idiom: `== True`, an if/else that returns the condition, an index loop that only reads `xs[i]`, `.filter(...).length > 0`, a catch that only rethrows, a flag loop that is `any()`, a hand-built counter, an `__init__` that `@dataclass` writes, a React Effect that only sets state. ruff and clippy add more. ## Examples Caught as `simpler`: ```js [cc.js] export function load(url) { return new Promise((resolve, reject) => { fetch(url).then(resolve).catch(reject); }); } ``` Written well: ```js [cc.js] export function load(url) { return fetch(url); } ``` --- # Annotating types the compiler infers *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). ## What makes annotating types the compiler infers a bad practice? The standard that calls it a bad practice: typescript-eslint no-inferrable-types. ## What does lessloc do about annotating types the compiler infers? lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). - `simpler` (tier 2): Brute-force code with a shorter idiom: `== True`, an if/else that returns the condition, an index loop that only reads `xs[i]`, `.filter(...).length > 0`, a catch that only rethrows, a flag loop that is `any()`, a hand-built counter, an `__init__` that `@dataclass` writes, a React Effect that only sets state. ruff and clippy add more. ## Examples Caught as `simpler`: ```ts [cd.ts] export const owner: string = "ada"; export let visits: number = 0; ``` Written well: ```ts [cd.ts] export const owner = "ada"; export let visits = 0; ``` --- # A React Effect that only sets state from other values *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). ## What makes a React Effect that only sets state from other values a bad practice? The standard that calls it a bad practice: React docs: "You Might Not Need an Effect". ## What does lessloc do about a React Effect that only sets state from other values? lessloc catches it as [`simpler`](https://lessloc.org/guide/reference.md#tier-2). - `simpler` (tier 2): Brute-force code with a shorter idiom: `== True`, an if/else that returns the condition, an index loop that only reads `xs[i]`, `.filter(...).length > 0`, a catch that only rethrows, a flag loop that is `any()`, a hand-built counter, an `__init__` that `@dataclass` writes, a React Effect that only sets state. ruff and clippy add more. ## Examples Caught as `simpler`: ```jsx [ba.jsx] import { useEffect, useState } from "react"; export function Cart({ items }) { const [total, setTotal] = useState(0); useEffect(() => { setTotal(items.reduce((s, i) => s + i.price, 0)); }, [items]); return

{total}

; } ``` Written well: ```jsx [ba.jsx] import { useEffect, useState } from "react"; export function Theme() { const [theme, setTheme] = useState("light"); useEffect(() => { setTheme(localStorage.getItem("theme") ?? "light"); }, []); return

{theme}

; } ``` --- # Functions with many parameters (8 or more) *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`long-params`](https://lessloc.org/guide/reference.md#tier-2). ## What makes functions with many parameters (8 or more) a bad practice? The standards that call it a bad practice: [CWE-1064](https://cwe.mitre.org/data/definitions/1064.html) (ISO/IEC 5055 maintainability); Sonar S107; Clean Code F1 Too Many Arguments. ## What does lessloc do about functions with many parameters (8 or more)? lessloc catches it as [`long-params`](https://lessloc.org/guide/reference.md#tier-2). - `long-params` (tier 2): A new function with 8 or more parameters. ## Examples Caught as `long-params`: ```python [bc.py] def make_order(a, b, c, d, e, f, g, h): return a ``` Written well: ```python [bc.py] def make_order(spec): return spec ``` --- # Deep nesting *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`deep-nesting`](https://lessloc.org/guide/reference.md#tier-2). ## What makes deep nesting a bad practice? The standards that call it a bad practice: [CWE-1124](https://cwe.mitre.org/data/definitions/1124.html) Excessively Deep Nesting; Sonar S134; Fowler: Replace Nested Conditional with Guard Clauses. ## What does lessloc do about deep nesting? lessloc catches it as [`deep-nesting`](https://lessloc.org/guide/reference.md#tier-2). - `deep-nesting` (tier 2): Statements nested 4 or more control levels deep. ## Examples Caught as `deep-nesting`: ```python [ab.py] def scan(rows): for r in rows: if r: for c in r: if c: print(c) ``` Written well: ```python [ab.py] def scan(rows): for r in rows: for c in r or []: if c: print(c) ``` --- # Long functions *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`long-function`](https://lessloc.org/guide/reference.md#tier-2). ## What makes long functions a bad practice? The standards that call it a bad practice: Fowler: Long Function; Sonar S138; [CWE-1121](https://cwe.mitre.org/data/definitions/1121.html). ## What does lessloc do about long functions? lessloc catches it as [`long-function`](https://lessloc.org/guide/reference.md#tier-2). - `long-function` (tier 2): A function over 100 code lines, 20 or more of them written by the change. --- # If-chains on literal values *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`if-chain`](https://lessloc.org/guide/reference.md#tier-2). ## What makes if-chains on literal values a bad practice? The standard that calls it a bad practice: Fowler: Repeated Switches. ## What does lessloc do about if-chains on literal values? lessloc catches it as [`if-chain`](https://lessloc.org/guide/reference.md#tier-2). - `if-chain` (tier 2): Three or more if-branches on literal values of one variable, where a lookup table would do. --- # A file that keeps growing *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`swollen-file`](https://lessloc.org/guide/reference.md#tier-2). ## What makes a file that keeps growing a bad practice? The standard that calls it a bad practice: [CWE-1080](https://cwe.mitre.org/data/definitions/1080.html) Source Code File with Excessive Number of Lines of Code. ## What does lessloc do about a file that keeps growing? lessloc catches it as [`swollen-file`](https://lessloc.org/guide/reference.md#tier-2). - `swollen-file` (tier 2): A file that grew by 150 or more lines and now has over 800 code lines. --- # Reformatting lines the task did not change *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc gives every session a rule against it and catches it as [`churn`](https://lessloc.org/guide/reference.md#tier-2). ## What makes reformatting lines the task did not change a bad practice? The standard that calls it a bad practice: Google engineering practices: small, focused changes. ## What does lessloc do about reformatting lines the task did not change? lessloc gives every session a rule against it and catches it as [`churn`](https://lessloc.org/guide/reference.md#tier-2). - `churn` (tier 2): Lines only reformatted (quotes, spacing, punctuation) that the task did not need to change. --- # Net growth past a budget *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`net-growth`](https://lessloc.org/guide/reference.md#tier-4). ## What makes net growth past a budget a bad practice? The standard that calls it a bad practice: Lehman's law of increasing complexity. ## What does lessloc do about net growth past a budget? lessloc catches it as [`net-growth`](https://lessloc.org/guide/reference.md#tier-4). - `net-growth` (tier 4): The change adds more than `maxNetLines` net lines. --- # Golfed code: nested ternaries, comprehensions with several `for` clauses, lines over 160 characters *Code longer than it needs to be*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`clever`](https://lessloc.org/guide/reference.md#tier-2) and [`dense-lines`](https://lessloc.org/guide/reference.md#tier-3). ## What makes golfed code a bad practice? The standards that call it a bad practice: Google Python Style Guide: "Optimize for readability, not conciseness"; ESLint no-nested-ternary; Sonar S3358. ## What does lessloc do about golfed code? lessloc catches it as [`clever`](https://lessloc.org/guide/reference.md#tier-2) and [`dense-lines`](https://lessloc.org/guide/reference.md#tier-3). The counterweight to `simpler`, so shorter never means cryptic. - `clever` (tier 2): Golfed code: nested ternaries, nested conditional expressions, comprehensions with several `for` clauses. - `dense-lines` (tier 3): Code lines over 160 characters, long strings and URLs aside. ## Examples Caught as `clever`: ```ts [z.ts] export const label = (n: number) => (n < 0 ? "neg" : n === 0 ? "zero" : "pos"); ``` Written well: ```ts [z.ts] export function label(n: number) { if (n < 0) return "neg"; return n === 0 ? "zero" : "pos"; } ``` Caught as `clever`: ```python [aa.py] def flat(grid): return [c for row in grid for c in row if c] ``` Written well: ```python [aa.py] from itertools import chain def flat(grid): return [c for c in chain.from_iterable(grid) if c] ``` --- # Swallowed errors: `except: pass`, an empty catch, `catch (Exception e) { return null; }`, Go's `if err != nil { return nil }` *Errors and checks hidden*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`swallowed-error`](https://lessloc.org/guide/reference.md#tier-1). ## What makes swallowed errors a bad practice? The standards that call it a bad practice: [CWE-390](https://cwe.mitre.org/data/definitions/390.html) Detection of Error Condition Without Action; [CWE-1069](https://cwe.mitre.org/data/definitions/1069.html) Empty Exception Block; [CWE-396](https://cwe.mitre.org/data/definitions/396.html). ## What does lessloc do about swallowed errors? lessloc catches it as [`swallowed-error`](https://lessloc.org/guide/reference.md#tier-1). - `swallowed-error` (tier 1): An error caught and dropped, such as `except Exception: pass` or an empty catch. The agent hears about it right after the write. ## Examples Caught as `swallowed-error`: ```js [ac.js] export function read(f) { try { return f(); } catch (e) { // ignore } } ``` Written well: ```js [ac.js] export function read(f) { try { return f(); } catch (e) { if (e.code !== "ENOENT") throw e; } } ``` Caught as `swallowed-error`: ```go [ad.go] package ad func Load(get func() error) error { if err := get(); err != nil { return nil } return nil } func Save(put func() error) error { err := put() if err != nil { return nil } return nil } ``` Written well: ```go [ad.go] package ad func Save(put func() error) error { err := put() if err != nil { return err } return nil } ``` --- # Silenced checks: `@ts-ignore`, `any`, blanket `# type: ignore` or `# noqa`, `eslint-disable` without a rule, `#[allow(dead_code)]`, `//nolint` without a reason *Errors and checks hidden*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`suppression`](https://lessloc.org/guide/reference.md#tier-1). ## What makes silenced checks a bad practice? The standards that call it a bad practice: Clean Code G4 Overridden Safeties; typescript-eslint ban-ts-comment and no-explicit-any; eslint-comments no-unlimited-disable; ruff PGH003, PGH004; golangci-lint nolintlint. ## What does lessloc do about silenced checks? lessloc catches it as [`suppression`](https://lessloc.org/guide/reference.md#tier-1). - `suppression` (tier 1): A blanket suppression: `@ts-ignore`, `any`, `# type: ignore` or `# noqa` without a code, `eslint-disable` without a rule, `nolint` without a linter and a reason, `allow(dead_code)`. ## Examples Caught as `suppression`: ```ts [a.ts] export const n: number = JSON.parse("1"); // @ts-ignore export const m = n.foo; ``` Written well: ```ts [a.ts] export const n = 1; // @ts-expect-error: the vendored typings miss foo (upstream issue 12) export const m = n.foo; ``` Caught as `suppression`: ```js [b.js] /* eslint-disable */ export const x = 1; ``` Written well: ```js [b.js] // eslint-disable-next-line no-console -- the CLI prints console.log(1); ``` Caught as `suppression`: ```python [c.py] import os # noqa x = os.sep # type: ignore ``` Written well: ```python [c.py] import os # noqa: F401 re-exported for plugins x = os.sep # type: ignore[attr-defined] ``` Caught as `suppression`: ```rust [d.rs] #[allow(dead_code)] fn helper() -> u8 { 1 } ``` Written well: ```rust [d.rs] #[allow(clippy::too_many_arguments)] fn helper() -> u8 { 1 } ``` Caught as `suppression`: ```ts [ag.ts] export function parse(s: string) { const data: any = JSON.parse(s); return data.id as any; } ``` Written well: ```ts [ag.ts] export function parse(s: string): { id: string } { return JSON.parse(s) as { id: string }; } ``` --- # Checks, fallbacks and catches for cases that cannot happen *Errors and checks hidden*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). What lessloc does: [`defensive`](https://lessloc.org/guide/reference.md#tier-2) catches a null check of a value just created, a type re-checked inside private code and `?.` on a fresh object. ## What makes checks, fallbacks and catches for cases that cannot happen a bad practice? The standards that call it a bad practice: Meyer, "Applying Design by Contract" (IEEE Computer, 1992): "never in both… avoid redundant tests"; Ousterhout: Define Errors Out of Existence; Anthropic's prompting guide: "Don't add error handling, fallbacks, or validation for scenarios that can't happen". ## What does lessloc do about checks, fallbacks and catches for cases that cannot happen? [`defensive`](https://lessloc.org/guide/reference.md#tier-2) catches a null check of a value just created, a type re-checked inside private code and `?.` on a fresh object. - `defensive` (tier 2): A check for a case that cannot happen, such as testing a value just created for emptiness. ## Examples Caught as `defensive`: ```python [ce.py] def make(): user = User("ada") if user is None: raise ValueError("no user") return user ``` Written well: ```python [ce.py] def make(name): if not name: raise ValueError("name is required") return User(name) ``` Caught as `defensive`: ```ts [cf.ts] function label(id: string) { if (typeof id !== "string") throw new Error("id"); const parts = { id }; return parts?.id; } export const show = (id: string) => label(id); ``` Written well: ```ts [cf.ts] export function label(id: string) { if (typeof id !== "string") throw new Error("id must be a string"); return id; } ``` --- # Removing validation, error handling, logging or stated rules to shrink code *Errors and checks hidden*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`removed-safeguard`](https://lessloc.org/guide/reference.md#tier-1). ## What makes removing validation or error handling to shrink code a bad practice? The standard that calls it a bad practice: lessloc's own "Never cut" list. ## What does lessloc do about removing validation or error handling to shrink code? lessloc catches it as [`removed-safeguard`](https://lessloc.org/guide/reference.md#tier-1). - `removed-safeguard` (tier 1): Logging, error handling, validation, or a comment that states a security rule or contract was removed. --- # SQL built from values with f-strings, concatenation or template strings *Shortcuts that trade away safety*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1): pass values as query parameters. ## What makes SQL built from values instead of query parameters a bad practice? The standards that call it a bad practice: [CWE-89](https://cwe.mitre.org/data/definitions/89.html) SQL Injection; OWASP Top 10 A03:2021 Injection. ## What does lessloc do about SQL built from values instead of query parameters? lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1): pass values as query parameters. - `unsafe` (tier 1): A shortcut that trades away safety: SQL built from values, HTML built or escaped by hand, `Math.random()` for secrets, a fast hash for passwords, certificate checks turned off, text run as code. ## Examples Caught as `unsafe`: ```python [cj.py] def find(db, name): return db.execute(f"SELECT * FROM users WHERE name = '{name}'") ``` Written well: ```python [cj.py] def find(db, name): return db.execute("SELECT * FROM users WHERE name = ?", (name,)) ``` --- # HTML built or escaped by hand: `innerHTML` from values, `.replace('<', '<')` *Shortcuts that trade away safety*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1): use `textContent` or the framework's rendering. ## What makes HTML built or escaped by hand a bad practice? The standards that call it a bad practice: [CWE-79](https://cwe.mitre.org/data/definitions/79.html) Cross-site Scripting; [CWE-116](https://cwe.mitre.org/data/definitions/116.html) Improper Encoding or Escaping of Output. ## What does lessloc do about HTML built or escaped by hand? lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1): use `textContent` or the framework's rendering. - `unsafe` (tier 1): A shortcut that trades away safety: SQL built from values, HTML built or escaped by hand, `Math.random()` for secrets, a fast hash for passwords, certificate checks turned off, text run as code. ## Examples Caught as `unsafe`: ```js [ck.js] export function show(el, name) { el.innerHTML = `${name}`; } ``` Written well: ```js [ck.js] export function show(el, name) { el.textContent = name; } ``` --- # `Math.random()` or `random` for tokens, passwords and session ids *Shortcuts that trade away safety*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1): `crypto.randomUUID()`, `getRandomValues()` or the `secrets` module. ## What makes Math.random() or random for tokens and passwords a bad practice? The standard that calls it a bad practice: [CWE-338](https://cwe.mitre.org/data/definitions/338.html) Use of Cryptographically Weak Pseudo-Random Number Generator. ## What does lessloc do about Math.random() or random for tokens and passwords? lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1): `crypto.randomUUID()`, `getRandomValues()` or the `secrets` module. - `unsafe` (tier 1): A shortcut that trades away safety: SQL built from values, HTML built or escaped by hand, `Math.random()` for secrets, a fast hash for passwords, certificate checks turned off, text run as code. ## Examples Caught as `unsafe`: ```js [cl.js] export const token = () => Math.random().toString(36).slice(2); ``` Written well: ```js [cl.js] export const token = () => crypto.randomUUID(); ``` --- # Fast hashes (MD5, SHA-1, SHA-256) for passwords *Shortcuts that trade away safety*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1): bcrypt, scrypt or argon2. ## What makes fast hashes (MD5, SHA-1, SHA-256) for passwords a bad practice? The standard that calls it a bad practice: [CWE-916](https://cwe.mitre.org/data/definitions/916.html) Use of Password Hash With Insufficient Computational Effort. ## What does lessloc do about fast hashes (MD5, SHA-1, SHA-256) for passwords? lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1): bcrypt, scrypt or argon2. - `unsafe` (tier 1): A shortcut that trades away safety: SQL built from values, HTML built or escaped by hand, `Math.random()` for secrets, a fast hash for passwords, certificate checks turned off, text run as code. ## Examples Caught as `unsafe`: ```python [cm.py] import hashlib def store(password): return hashlib.sha256(password.encode()).hexdigest() ``` Written well: ```python [cm.py] import bcrypt def store(password): return bcrypt.hashpw(password.encode(), bcrypt.gensalt()) ``` --- # Certificate checks turned off: `verify=False`, `rejectUnauthorized: false`, `InsecureSkipVerify: true` *Shortcuts that trade away safety*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1). ## What makes certificate checks turned off a bad practice? The standard that calls it a bad practice: [CWE-295](https://cwe.mitre.org/data/definitions/295.html) Improper Certificate Validation. ## What does lessloc do about certificate checks turned off? lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1). - `unsafe` (tier 1): A shortcut that trades away safety: SQL built from values, HTML built or escaped by hand, `Math.random()` for secrets, a fast hash for passwords, certificate checks turned off, text run as code. ## Examples Caught as `unsafe`: ```python [cn.py] import requests def get(u): return requests.get(u, verify=False) ``` Written well: ```python [cn.py] import requests def get(u): return requests.get(u, timeout=10) ``` --- # Running text as code: `eval`, `exec`, `new Function` *Shortcuts that trade away safety*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1): parse the input instead. ## What makes running text as code (eval, exec, new Function) a bad practice? The standard that calls it a bad practice: [CWE-95](https://cwe.mitre.org/data/definitions/95.html) Eval Injection. ## What does lessloc do about running text as code (eval, exec, new Function)? lessloc catches it as [`unsafe`](https://lessloc.org/guide/reference.md#tier-1): parse the input instead. - `unsafe` (tier 1): A shortcut that trades away safety: SQL built from values, HTML built or escaped by hand, `Math.random()` for secrets, a fast hash for passwords, certificate checks turned off, text run as code. ## Examples Caught as `unsafe`: ```js [co.js] export const calc = expr => eval(expr); ``` Written well: ```js [co.js] export const calc = (a, b) => a + b; ``` --- # Another language's API: `null` or `===` in Python, `.strip()`, `None` or `len()` in JavaScript, `null` in Go *Calls and packages that do not exist*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`made-up-api`](https://lessloc.org/guide/reference.md#tier-1). ## What makes another language's API a bad practice? The standard that calls it a bad practice: Sonar's quality profile for agent-written code targets "hallucinated APIs"; a call that does not exist fails the first time it runs. ## What does lessloc do about another language's API? lessloc catches it as [`made-up-api`](https://lessloc.org/guide/reference.md#tier-1). - `made-up-api` (tier 1): Another language's API, such as `null` in Python, `len()` in JavaScript or `None` in Go, or an import that no manifest declares and the platform does not provide. ## Examples Caught as `made-up-api`: ```python [cp.py] def first(xs): if len(xs) === 0: return null return xs[0] ``` Written well: ```python [cp.py] def first(xs): return xs[0] if xs else None ``` Caught as `made-up-api`: ```python [tests/test_cr.py] def test_load(): doc = load("[a]\nb = 1\n") assert doc["a"] != null ``` Written well: ```python [tests/test_cr.py] def test_load(): doc = load("""\ [a] smooth = true """) assert doc["a"]["smooth"] is True ``` Caught as `made-up-api`: ```js [cq.js] export const clean = s => s.strip(); ``` Written well: ```js [cq.js] export const clean = s => s.trim(); ``` --- # Imports of packages no manifest declares: a made-up name, or one never installed *Calls and packages that do not exist*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`made-up-api`](https://lessloc.org/guide/reference.md#tier-1): checked against every package.json, Python manifest, the standard library and the project's own modules. ## What makes imports of packages no manifest declares a bad practice? The standards that call it a bad practice: Spracklen et al., USENIX Security 2025 (5.2–21.7% of recommended packages do not exist); OWASP Top 10 A03:2025 Software Supply Chain Failures. ## What does lessloc do about imports of packages no manifest declares? lessloc catches it as [`made-up-api`](https://lessloc.org/guide/reference.md#tier-1): checked against every package.json, Python manifest, the standard library and the project's own modules. - `made-up-api` (tier 1): Another language's API, such as `null` in Python, `len()` in JavaScript or `None` in Go, or an import that no manifest declares and the platform does not provide. --- # Comments that narrate what the code does ("Loop through the users") *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`narration`](https://lessloc.org/guide/reference.md#tier-3). ## What makes comments that narrate what the code does a bad practice? The standards that call it a bad practice: Clean Code C3 Redundant Comment and Noise Comments; McConnell, *Code Complete* §32.4 (a comment that repeats the code is not acceptable in finished code); Ousterhout: Comment Repeats Code. ## What does lessloc do about comments that narrate what the code does? lessloc catches it as [`narration`](https://lessloc.org/guide/reference.md#tier-3). Comments that give a reason are kept. - `narration` (tier 3): Comments that narrate the code, decorate it or restate a name, closing-brace comments, and log lines that narrate each step. ## Examples Caught as `narration`: ```python [cg.py] def total(xs): # Loop through the items and add them up return sum(xs) ``` Written well: ```python [cg.py] def total(xs): # Refunds are negative, so the sum is already net return sum(xs) ``` --- # Banners, dividers, closing-brace comments, emoji decoration *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`narration`](https://lessloc.org/guide/reference.md#tier-3). ## What makes banners, dividers, closing-brace comments, emoji decoration a bad practice? The standard that calls it a bad practice: Clean Code: Position Markers, Closing Brace Comments. ## What does lessloc do about banners, dividers, closing-brace comments, emoji decoration? lessloc catches it as [`narration`](https://lessloc.org/guide/reference.md#tier-3). - `narration` (tier 3): Comments that narrate the code, decorate it or restate a name, closing-brace comments, and log lines that narrate each step. ## Examples Caught as `narration`: ```python [l.py] # ========== Helpers ========== def a(): return 1 ``` Written well: ```python [l.py] # Rates are cached for a minute: the provider bills per call def a(): return 1 ``` Caught as `narration`: ```js [m.js] function a() { return 1; } // end function ``` Written well: ```js [m.js] function a() { return 1; } ``` --- # Docs that restate the name (`"""Get the user."""`, `name: The name.`, `@returns The result.`) *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`narration`](https://lessloc.org/guide/reference.md#tier-3). ## What makes docs that restate the name a bad practice? The standards that call it a bad practice: Clean Code: Mandated Comments; Google Python Style Guide (no types in docstrings where annotations exist). ## What does lessloc do about docs that restate the name? lessloc catches it as [`narration`](https://lessloc.org/guide/reference.md#tier-3). - `narration` (tier 3): Comments that narrate the code, decorate it or restate a name, closing-brace comments, and log lines that narrate each step. ## Examples Caught as `narration`: ```python [n.py] def ship(order, carrier): """Ship an order. Args: order: The order. carrier: The carrier. """ return order ``` Written well: ```python [n.py] def ship(order, carrier): """Hand the order to the carrier; raises if the address is unverified. Args: carrier: one of "ups", "dhl"; anything else ships by post. """ return order ``` --- # History and messages to the requester ("Updated the parser to...", "As requested", "Generated by AI", dated change notes) *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`history`](https://lessloc.org/guide/reference.md#tier-3). ## What makes history and messages to the requester a bad practice? The standard that calls it a bad practice: Clean Code C1 and Journal Comments: history belongs in source control. ## What does lessloc do about history and messages to the requester? lessloc catches it as [`history`](https://lessloc.org/guide/reference.md#tier-3). - `history` (tier 3): Dates, change history or notes to the requester in comments. ## Examples Caught as `history`: ```js [o.js] // Updated the parser to handle empty input, as requested export const parse = s => s.trim(); ``` Written well: ```js [o.js] // Empty input is valid: forms post "" for untouched fields export const parse = s => s.trim(); ``` --- # Comment blocks and comment-heavy changes *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`comment-block`](https://lessloc.org/guide/reference.md#tier-3) and [`comment-density`](https://lessloc.org/guide/reference.md#tier-3). ## What makes comment blocks and comment-heavy changes a bad practice? The standard that calls it a bad practice: Clean Code: Too Much Information. ## What does lessloc do about comment blocks and comment-heavy changes? lessloc catches it as [`comment-block`](https://lessloc.org/guide/reference.md#tier-3) and [`comment-density`](https://lessloc.org/guide/reference.md#tier-3). - `comment-block` (tier 3): A comment block of 6 or more lines. - `comment-density` (tier 3): Comments make up more than `maxCommentRatio` of a file's added lines. --- # Log lines that narrate each step ("Starting...", "Successfully saved") *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`narration`](https://lessloc.org/guide/reference.md#tier-3) (three or more in a file). ## What makes log lines that narrate each step a bad practice? The standard that calls it a bad practice: Raymond, *The Art of Unix Programming*, Rule of Silence: "When a program has nothing surprising to say, it should say nothing". ## What does lessloc do about log lines that narrate each step? lessloc catches it as [`narration`](https://lessloc.org/guide/reference.md#tier-3) (three or more in a file). - `narration` (tier 3): Comments that narrate the code, decorate it or restate a name, closing-brace comments, and log lines that narrate each step. ## Examples Caught as `narration`: ```js [bd.js] export async function sync(db) { console.log("Starting sync..."); const rows = await db.all(); console.log("Fetched rows"); await db.save(rows); console.log("Successfully saved"); } ``` Written well: ```js [bd.js] export async function sync(db) { const rows = await db.all(); await db.save(rows); console.log(`synced ${rows.length} rows`); } ``` --- # Docs that still name what a change removed *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`stale-doc`](https://lessloc.org/guide/reference.md#tier-3). ## What makes docs that still name what a change removed a bad practice? The standard that calls it a bad practice: [CWE-1116](https://cwe.mitre.org/data/definitions/1116.html) Inaccurate Source Code Comments. ## What does lessloc do about docs that still name what a change removed? lessloc catches it as [`stale-doc`](https://lessloc.org/guide/reference.md#tier-3). - `stale-doc` (tier 3): A doc that still names something the change removed or renamed. --- # Doc files nobody asked for, and READMEs that grow by 40 or more lines nobody asked for *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unrequested-doc`](https://lessloc.org/guide/reference.md#tier-3). ## What makes doc files and README growth nobody asked for a bad practice? The standard that calls it a bad practice: YAGNI. ## What does lessloc do about doc files and README growth nobody asked for? lessloc catches it as [`unrequested-doc`](https://lessloc.org/guide/reference.md#tier-3). - `unrequested-doc` (tier 3): A doc file nobody asked for, or a doc that grew by 40 or more lines without a request for docs. --- # Docs deleted wholesale to shrink a change *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`doc-removed`](https://lessloc.org/guide/reference.md#tier-3). ## What makes docs deleted wholesale to shrink a change a bad practice? The standard that calls it a bad practice: lessloc's own "Never cut" list. ## What does lessloc do about docs deleted wholesale to shrink a change? lessloc catches it as [`doc-removed`](https://lessloc.org/guide/reference.md#tier-3). - `doc-removed` (tier 3): More than 20 lines of documentation removed. --- # Always-loaded instructions that grow, or that include a directory tour *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`long-instructions`](https://lessloc.org/guide/reference.md#tier-3). ## What makes always-loaded instructions that grow a bad practice? The standard that calls it a bad practice: [arXiv:2602.11988](https://arxiv.org/abs/2602.11988) (context files with overviews raised cost by more than 20% and did not help). ## What does lessloc do about always-loaded instructions that grow? lessloc catches it as [`long-instructions`](https://lessloc.org/guide/reference.md#tier-3). - `long-instructions` (tier 3): A directory tour in always-loaded instructions, or instruction files over `instructionBudget` tokens. --- # Commit messages that recap the diff file by file *Comments and text*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). What lessloc does: The commit hook warns when a message lists the files the diff shows. ## What makes commit messages that recap the diff file by file a bad practice? The standard that calls it a bad practice: DRY: the diff already lists every file. ## What does lessloc do about commit messages that recap the diff file by file? The commit hook warns when a message lists the files the diff shows. --- # Dockerfiles, compose files, CI workflows and deploy configs nobody asked for *Files and folders*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unrequested-file`](https://lessloc.org/guide/reference.md#tier-2). ## What makes CI workflows and Dockerfiles nobody asked for a bad practice? The standards that call it a bad practice: YAGNI; Google engineering practices: "functionality that isn't presently needed". ## What does lessloc do about CI workflows and Dockerfiles nobody asked for? lessloc catches it as [`unrequested-file`](https://lessloc.org/guide/reference.md#tier-2). - `unrequested-file` (tier 2): A deployment, container or CI file nobody asked for. --- # Tiny one-function files; file-per-concern splits *Files and folders*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`tiny-file`](https://lessloc.org/guide/reference.md#tier-2) and [`new-files`](https://lessloc.org/guide/reference.md#tier-4). ## What makes tiny one-function files and file-per-concern splits a bad practice? The standard that calls it a bad practice: Ousterhout: Shallow Module. ## What does lessloc do about tiny one-function files and file-per-concern splits? lessloc catches it as [`tiny-file`](https://lessloc.org/guide/reference.md#tier-2) and [`new-files`](https://lessloc.org/guide/reference.md#tier-4). - `tiny-file` (tier 2): A new code file under 10 code lines. - `new-files` (tier 4): The change adds more than `maxNewFiles` new code files. --- # New folders for one or two files; layer trees for small apps *Files and folders*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`new-folder`](https://lessloc.org/guide/reference.md#tier-2). ## What makes new folders for one or two files and layer trees for small apps a bad practice? The standards that call it a bad practice: Ousterhout: shallow modules; Martin's Common Closure Principle. ## What does lessloc do about new folders for one or two files and layer trees for small apps? lessloc catches it as [`new-folder`](https://lessloc.org/guide/reference.md#tier-2). Framework route folders (Next.js, SvelteKit, Expo) are exempt. - `new-folder` (tier 2): A new folder with one or two entries, or an examples or demo folder nobody asked for. --- # Junk-drawer `utils` and `helpers` modules and folders *Files and folders*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`junk-drawer`](https://lessloc.org/guide/reference.md#tier-2). ## What makes junk-drawer utils and helpers modules and folders a bad practice? The standard that calls it a bad practice: Fowler: Mysterious Name. ## What does lessloc do about junk-drawer utils and helpers modules and folders? lessloc catches it as [`junk-drawer`](https://lessloc.org/guide/reference.md#tier-2). - `junk-drawer` (tier 2): A new `utils`, `helpers`, `common`, `misc` or similar module or folder. --- # Vague names (`process_data`, `handle`) *Files and folders*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`vague-name`](https://lessloc.org/guide/reference.md#tier-2). ## What makes vague names (process\_data, handle) a bad practice? The standards that call it a bad practice: Fowler: Mysterious Name; Ousterhout: Vague Name. ## What does lessloc do about vague names (process\_data, handle)? lessloc catches it as [`vague-name`](https://lessloc.org/guide/reference.md#tier-2). - `vague-name` (tier 2): A new name made only of vague words, such as `process_data` or `handle`. --- # Moving or renaming files without a reason *Files and folders*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`moved-files`](https://lessloc.org/guide/reference.md#tier-2). ## What makes moving or renaming files without a reason a bad practice? The standard that calls it a bad practice: Google engineering practices: separate refactoring changes. ## What does lessloc do about moving or renaming files without a reason? lessloc catches it as [`moved-files`](https://lessloc.org/guide/reference.md#tier-2). - `moved-files` (tier 2): Files moved or renamed when the request did not ask for a refactor. --- # Secrets: a committed `.env`, API keys, private keys *Files and folders*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`secret`](https://lessloc.org/guide/reference.md#tier-0). ## What makes secrets (a committed .env, API keys, private keys) a bad practice? The standards that call it a bad practice: [CWE-798](https://cwe.mitre.org/data/definitions/798.html) Use of Hard-coded Credentials; [CWE-540](https://cwe.mitre.org/data/definitions/540.html). ## What does lessloc do about secrets (a committed .env, API keys, private keys)? lessloc catches it as [`secret`](https://lessloc.org/guide/reference.md#tier-0). - `secret` (tier 0): A credential in a known format in the added lines, or a new `.env` file that would be committed. ## Examples Caught as `secret`: ```js [k.js] export const stripe = "sk_live_51HxQz2eZvKYlo2C8a9Gm4Rt7Uv0Wx3Yz"; ``` Written well: ```js [k.js] export const stripe = process.env.STRIPE_KEY; ``` --- # Behavior added with no test changed in the same change *Tests and verification*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc gives every session the rule: new behavior gets one focused test. ## What makes behavior added with no test changed in the same change a bad practice? The standard that calls it a bad practice: Google engineering practices: tests ship with the change they test. ## What does lessloc do about behavior added with no test changed in the same change? lessloc gives every session the rule: new behavior gets one focused test. lessloc catches it as [`untested`](https://lessloc.org/guide/reference.md#tier-1). It is not raised for refactors, for fixes that do not grow the code, or for projects without tests. - `untested` (tier 1): The change adds behavior and no test changed, [with some exceptions](https://lessloc.org/guide/how-it-works.md#end-of-turn-unfinished-work-is-held-once). --- # Tests that assert nothing, or assert a constant *Tests and verification*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`empty-test`](https://lessloc.org/guide/reference.md#tier-1). ## What makes tests that assert nothing, or assert a constant a bad practice? The standard that calls it a bad practice: Carr, TDD anti-patterns: The Liar. ## What does lessloc do about tests that assert nothing, or assert a constant? lessloc catches it as [`empty-test`](https://lessloc.org/guide/reference.md#tier-1). - `empty-test` (tier 1): A test that asserts nothing or only a constant, or one that mocks the code it tests. ## Examples Caught as `empty-test`: ```python [tests/test_ae.py] def test_total(): total([1, 2]) def test_sum(): assert True ``` Written well: ```python [tests/test_ae.py] def test_total(): assert total([1, 2]) == 3 def test_import_runs(): import json ``` Caught as `empty-test`: ```python [tests/test_ag.py] def test_render(): expected = """\ [a] b = 1 """ render(expected) ``` Written well: ```python [tests/test_ag.py] def test_render(): expected = """\ [a] b = 1 """ assert render() == expected ``` Caught as `empty-test`: ```js [af.test.js] import { test } from "node:test"; test("adds", () => { add(1, 2); }); ``` Written well: ```js [af.test.js] import { test } from "node:test"; import assert from "node:assert"; test("adds", () => { assert.equal(add(1, 2), 3); }); ``` --- # Deleted, skipped, focused (`.only`), exited (`sys.exit(0)`) or force-passed tests *Tests and verification*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`weakened-tests`](https://lessloc.org/guide/reference.md#tier-0). ## What makes deleted, skipped or force-passed tests a bad practice? The standards that call it a bad practice: Clean Code T4 (an ignored test is a question); GitHub's AGENTS.md guidance: never remove a failing test. ## What does lessloc do about deleted, skipped or force-passed tests? lessloc catches it as [`weakened-tests`](https://lessloc.org/guide/reference.md#tier-0). Skips that depend on the environment are allowed. - `weakened-tests` (tier 0): Test assertions or tests removed, or tests newly skipped, focused or forced to pass. Tier 0 in a session; `check`, where a person may remove a test on purpose, reports it at tier 1. ## Examples Caught as `weakened-tests`: ```python [tests/test_aj.py] import pytest def test_pay(): pytest.skip("flaky") assert pay(2) == 2 ``` Written well: ```python [tests/test_aj.py] def test_pay(): assert pay(2) == 2 ``` --- # Test-only branches in production code; code shaped to pass a test *Tests and verification*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`test-logic`](https://lessloc.org/guide/reference.md#tier-1). ## What makes test-only branches in production code a bad practice? The standards that call it a bad practice: Meszaros: Test Logic in Production; Anthropic system cards: special-casing tests. ## What does lessloc do about test-only branches in production code? lessloc catches it as [`test-logic`](https://lessloc.org/guide/reference.md#tier-1). - `test-logic` (tier 1): Production code that checks whether tests are running, says it exists to make tests pass, or branches on a value that appears only in tests. ## Examples Caught as `test-logic`: ```python [j.py] import os def price(x): if "PYTEST_CURRENT_TEST" in os.environ: return 0 return x ``` Written well: ```python [j.py] import os def price(x): return x * float(os.environ.get("RATE", "1")) ``` Caught as `test-logic`: ```js [ah.js] export function price(x) { // special case for the test fixture if (x === 42) return 0; return x * 2; } ``` Written well: ```js [ah.js] export function price(x) { // Prices are stored in cents: 42 means $0.42 return x * 2; } ``` --- # Tests that mock the code they test *Tests and verification*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`empty-test`](https://lessloc.org/guide/reference.md#tier-1). ## What makes tests that mock the code they test a bad practice? The standard that calls it a bad practice: Carr, TDD anti-patterns: The Mockery. ## What does lessloc do about tests that mock the code they test? lessloc catches it as [`empty-test`](https://lessloc.org/guide/reference.md#tier-1). - `empty-test` (tier 1): A test that asserts nothing or only a constant, or one that mocks the code it tests. ## Examples Caught as `empty-test`: ```python [tests/test_cr.py] from unittest.mock import patch @patch("shop.pay.charge") def test_charge(mock_charge): mock_charge.return_value = 1 assert charge(5) == 1 ``` Written well: ```python [tests/test_cr.py] from unittest.mock import patch @patch("shop.pay.send_receipt") def test_charge(mock_send): assert charge(5) == 5 mock_send.assert_called_once() ``` Caught as `empty-test`: ```js [cart.test.js] import { test, vi } from "vitest"; import { total } from "./cart"; vi.mock("./cart"); test("total", () => { expect(total([1])).toBe(1); }); ``` Written well: ```js [cart.test.js] import { test, vi } from "vitest"; import { total } from "./cart"; vi.mock("./prices"); test("total", () => { expect(total([1])).toBe(1); }); ``` --- # Production code that branches on a value copied from the tests *Tests and verification*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`test-logic`](https://lessloc.org/guide/reference.md#tier-1). ## What makes production code that branches on a value from the tests a bad practice? The standards that call it a bad practice: Anthropic system cards: special-casing tests; Meszaros: Test Logic in Production. ## What does lessloc do about production code that branches on a value from the tests? lessloc catches it as [`test-logic`](https://lessloc.org/guide/reference.md#tier-1). - `test-logic` (tier 1): Production code that checks whether tests are running, says it exists to make tests pass, or branches on a value that appears only in tests. --- # Stopping short: a function, option or flag the request asked for is never written *Tests and verification*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unaddressed`](https://lessloc.org/guide/reference.md#tier-1): names a request asks to exist ("add a `--status` flag", "implement `export_csv()`") are looked for in the final repository; one that appears nowhere holds the turn. ## What makes stopping short (a requested function never written) a bad practice? The standard that calls it a bad practice: ISO/IEC 25010 functional completeness. ## What does lessloc do about stopping short (a requested function never written)? lessloc catches it as [`unaddressed`](https://lessloc.org/guide/reference.md#tier-1): names a request asks to exist ("add a `--status` flag", "implement `export_csv()`") are looked for in the final repository; one that appears nowhere holds the turn. A name the request only mentions, a test or a private helper the agent names itself never counts, and names joined by "or" are alternatives. - `unaddressed` (tier 1): A function, option or flag the request asked for appears nowhere in the repository. A name the request only mentions does not count. --- # Stopping short: leaving part of the reported problem failing, and saying so *Tests and verification*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`unfinished`](https://lessloc.org/guide/reference.md#tier-1): in a turn that changed code, the agent's last reply is read sentence by sentence, with headings and lead-ins speaking for what is under them. ## What makes stopping short (part of the problem left failing) a bad practice? The standard that calls it a bad practice: ISO/IEC 25010 functional completeness. ## What does lessloc do about stopping short (part of the problem left failing)? lessloc catches it as [`unfinished`](https://lessloc.org/guide/reference.md#tier-1): in a turn that changed code, the agent's last reply is read sentence by sentence, with headings and lead-ins speaking for what is under them. An explicit marker that something is left broken ("Not fixed:", "Known limitation:") with a failure stated now, a neutral scope note ("Not changed:", "I left it alone") with a present-tense defect tied to the request (a name from its code, or the report's own example), the report's own example still failing, or the same problem suspected elsewhere and unchecked, holds the turn once, quoting the sentence. A fixed state, a cause, a condition, an offer, a tool's report or housekeeping never counts. - `unfinished` (tier 1): In a turn that changed code, the agent's last reply says part of the reported problem still fails, or that the same problem may be elsewhere, unchecked. --- # A refactor that changes existing tests *Tests and verification*: one of the ways AI coding agents bloat software, from lessloc's [pattern catalog](https://lessloc.org/patterns/index.md). lessloc catches it as [`refactor-tests`](https://lessloc.org/guide/reference.md#tier-2). ## What makes a refactor that changes existing tests a bad practice? The standard that calls it a bad practice: Fowler: refactoring preserves behavior. ## What does lessloc do about a refactor that changes existing tests? lessloc catches it as [`refactor-tests`](https://lessloc.org/guide/reference.md#tier-2). - `refactor-tests` (tier 2): A refactor changed existing tests.