code-conventions
Skill1 starsChanged 5 months agoMITView on GitHub
Use when writing or reviewing code for general style conventions — style guide & linter, naming, file/function size, array functions, early return, SOLID/KISS, magic numbers, casts, side effects, deep copy, TS gotchas. Also indexes the full convention set. Language-agnostic, TS examples.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- precise-alloy/ritustheir own version, first seen Oct 11, 2026
- 0xkangl/skillstheir own version, first seen Sep 10, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- 0xkangl/skills★ 1same name
- precise-alloy/ritus★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository40 other files it gives its agents.
- authoring-project-skillsSkill
- background-jobs-and-cachingSkill
- containerize-and-ship-a-serviceSkill
- database-migrationsSkill
- design-an-error-modelSkill
- git-flowSkill
- handle-files-frontendSkill
- import-data-from-csvSkill
- integrate-external-servicesSkill
- integrate-identity-providersSkill
- integrate-internal-servicesSkill
- keep-service-memory-leanSkill
- operate-ecs-services-safelySkill
- prevent-secret-and-pii-leaksSkill
- release-safetySkill
- render-transactional-emailsSkill
- rules-backendSkill
- rules-code-styleSkill
- rules-debuggingSkill
- rules-gitSkill
- rules-infraSkill
- rules-pr-reviewSkill
- rules-shippingSkill
- rules-workflowSkill
- schedule-effective-dated-changesSkill
- secure-a-frontend-appSkill
- security-auditSkill
- send-slackSkill
- serve-realtime-with-websocketsSkill
- structure-a-backend-serviceSkill
- structure-a-frontend-appSkill
- structure-a-shared-backend-libSkill
- structure-a-shared-ui-libSkill
- tdd-http-firstSkill
- use-feature-flagsSkill
- write-cross-cutting-codeSkill
- write-frontend-codeSkill
- write-frontend-testsSkill
- write-service-codeSkill
- write-unit-testsSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 3 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 85
Keep nesting ≤ 2 levels. ▸ *Other stacks:* universal — guard clauses + early return everywhere.
- Distinctive: line 104
Keep callbacks pure (don't mutate the source array).
- Very specific: line 109
SOLID — single, clear responsibility: Before adding code ask: *what is this responsible for, where does it belong, what does it do?* One …
- Distinctive: line 111
KISS — simplest thing that works: Prefer simple, reusable, readable, maintainable code.
- Very specific: line 131
Deep-copy by value, not by alias: When you must not mutate the source, take a real deep copy.
- Very specific: line 137
No redundant casts or non-null assertions: If the type is already narrowed (e.g.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
When to use
Reach for this when writing or reviewing any code and you want the cross-cutting quality baseline. This file holds the general code-style conventions and indexes the rest of the set — the "Where the rest lives" table at the bottom maps every convention to its home, so this is the one place to see the whole picture.
To stay DRY, conventions detailed elsewhere are summarized + linked, not re-explained: where
files go → structure-a-backend-service; service-internal
patterns (queries, events, logging, tests) → write-service-code; unit
tests → write-unit-tests; workflow/release/config/DB →
CLAUDE.md.
Each rule is a portable principle with a ▸ TS example and ▸ Other stacks note. The TypeScript-only gotchas (§8) are skippable for non-TS repos.
Steps
1. Style guide & linter
- Follow the largest community style guide for the language rather than inventing one. For JS/TS that's Airbnb (https://github.com/airbnb/javascript). Required read: clean-code-javascript (https://github.com/ryanmcdermott/clean-code-javascript); optional: clean-code-typescript (https://github.com/labs42io/clean-code-typescript).
- Lint + format are enforced, not optional. ▸ TS: ESLint
extends: ['airbnb-base', 'prettier']. Think twice before disabling a rule on an ad-hoc block; think thrice before disabling it project-wide — and leave a comment saying why. ▸ Other stacks: adopt the de-facto linter+formatter (ruff/black, gofmt + golangci-lint, ktlint, RuboCop) and treat disables the same.
2. Naming — case by role
| Role | Case | Example |
| File, folder, route | kebab-case | your-file.service.ts, /listing-photos |
| Class, module, enum, decorator | PascalCase | ListingService, ListingStatus |
| Variable, method, function | camelCase | firstName, getListingDetail |
| Constant | SCREAMING_SNAKE_CASE | const DAYS_IN_WEEK = 7; |
(File & class casing for the layout is also in structure-a-backend-service §3.) ▸ Other stacks:
keep the same role→case mapping; switch only where the language's community standard differs (Python
files & functions snake_case; Go exports PascalCase, locals camelCase).
3. Size limits
- File ≤ ~500–600 lines; method/function ≤ ~20–30 lines. Past that, split by responsibility. This puts concrete numbers on the global Code Style — Function Size & Density rule ("reads top-to-bottom in one screenful; split a method covering 3+ concerns"). ▸ Other stacks: same ceilings — a long file/function is a missing module/function.
4. Early return — keep control flow flat
Handle the invalid/empty case first and return early, so the happy path stays un-indented
instead of buried in nested ifs.
// Bad — arrow of nested ifs
function handleClick(event) {
if (event.target.matches('.save-data')) {
const id = event.target.getAttribute('data-id');
if (id) {
const token = localStorage.getItem('token');
if (token) localStorage.setItem(`${token}_${id}`, true);
}
}
}
// Good — guard clauses, flat body
function handleClick(event) {
if (!event.target.matches('.save-data')) return;
const id = event.target.getAttribute('data-id');
if (!id) return;
const token = localStorage.getItem('token');
if (!token) return;
localStorage.setItem(`${token}_${id}`, true);
}Keep nesting ≤ 2 levels. ▸ Other stacks: universal — guard clauses + early return everywhere. (Applies in request handlers too — write-service-code §1.)
5. Pick the array function that states intent
Reaching for a manual loop to transform a collection is the smell (pipeline-over-loops is the global Iteration & Collections rule + write-service-code §1). Choose by intent:
| Intent | Function | What it does |
| keep a subset | filter | new array of the elements that pass the test |
| transform each element | map | new array, each element run through the callback |
| collapse to one value | reduce | folds the array into a single value via an accumulator |
| first element matching | find | the first element that passes the test, else undefined |
| does any match? | some | true if at least one element passes |
| do all match? | every | true if every element passes |
| map then flatten one level | flatMap | map + one level of flattening |
| pure side effect, nothing else fits | forEach | last resort — only when none of the above apply |
Keep callbacks pure (don't mutate the source array). ▸ Other stacks: the equivalents (comprehensions, LINQ, Go slices helpers, Kotlin/Java streams).
6. Principles — SOLID, KISS, SRP
- SOLID — single, clear responsibility. Before adding code ask: what is this responsible for, where does it belong, what does it do? One reason to change per function/class/module.
- KISS — simplest thing that works. Prefer simple, reusable, readable, maintainable code; review your own diff before asking others to. Add a comment only where the code is genuinely non-obvious.
- One responsibility per PR/MR — but a cohesive change is ONE PR, don't over-split. "One responsibility" means one logical change, not one file or one mechanical step. A feature that spans several steps (e.g. a layout migration + its barrel + the import alias, or a fix + its test) is one PR — use multiple commits to tell the story, not multiple PRs. Split into separate PRs only when the parts are genuinely independent (each reviews and reverts on its own and neither needs the other to make sense). Never build a deep stack of dependent PRs (#A→#B→#C→…): it's slower to review and a nightmare to merge/rebase — far worse than one well-described PR. When in doubt, default to one PR.
7. Traps to avoid
- Magic numbers → name them.
x = price * TAX_RATE, notx = price * 1.07. - Negative conditionals → positive predicates. Define
isOnline(...), notisNotOnline(...); read it asif (!isOnline(...)). Double negatives are hard to reason about. - Side effects → pure functions. A function should take its inputs and return its output, not
mutate shared/global state. ▸ Bad:
toBase64()reassigns a module-levelname. ▸ Good:toBase64(text): stringreturns the encoded value and touches nothing else. (Same reason pipeline callbacks must stay pure.) - Deep-copy by value, not by alias. When you must not mutate the source, take a real deep copy.
▸ TS/JS:
structuredClone(obj)(not a shallow{...obj}/Object.assign, which still shares nested refs). ▸ Other stacks: the language's deep-copy (copy.deepcopy, value semantics, etc.).
8. TypeScript-specific gotchas (skip for non-TS repos)
- No redundant casts or non-null assertions. If the type is already narrowed (e.g. inside
typeof x === 'string'),x as string/x!is noise that can hide real bugs. Let inference work.// Bad // Good console.log('name: ' + name!); console.log('name: ' + name); return (name as UserName).fullName; return name.fullName; // already narrowed - Don't append
!to a value you already guarded, and only use optional?./?where the value can truly be absent — not everywhere "just in case". If you checked the array isn't empty, drop the?after it. - Stop using
{}as a type.{}means "any non-null value" — strings, numbers, arrays, dates all satisfy it, so it catches nothing. UseRecord<string, unknown>(or{ [k: string]: unknown }) for an object bag.type Params = Record<string, unknown>; // not: function f(p: {})
Where the rest of the conventions live
The full set spans these docs — this file is the style baseline; the rest are detailed in their natural home (kept here as a map so nothing is lost):
| Convention | Home |
Pipelines over for/while loops | global Iteration & Collections + write-service-code §1 (§5 here = which function) |
null over undefined + API response defaults ([] for arrays, null otherwise) | write-service-code §3 |
Promise.all for independent async | write-service-code §2 |
| Private helpers below public methods | write-service-code §4 |
Query performance — avoid N+1, upsert, select needed fields, single round-trip, joins, indexes/orderBy | write-service-code §5 |
| Decimal lib for money, date lib for time (DecimalJs / Dayjs) | write-service-code §5 |
Events / SQS — domain events; don't throw in a consumer (extend AbstractEventHandler, logger.error + return) | write-service-code §6 |
| Structured logging (message + context object, mask PII, levels) | write-service-code §7 |
Testing — integration (AAA, factories, faker, it.each, matchers, coverage, real-DB through the boundary) | write-service-code §8 |
Testing — unit (mocked deps, createHandlerTestingModule, DTO validation, ≤300-line specs, clean per test) | write-unit-tests |
Folder/module layout, CQRS split, DTO index.ts barrels, domain entities vs models | structure-a-backend-service |
libs/ shared libraries (vendored, path-alias) | structure-a-backend-service §1 |
| Migrations (DDL) vs seeds (DML) | structure-a-backend-service §5 |
| Branching & release (develop→staging→master, tags, semver, hotfix) | git-flow |
| Workflow, release safety, config/env, DB rules, root-cause, PR review | CLAUDE.md |
Verification
- Lint/format clean under the community config (airbnb-base + prettier for TS); any inline disable carries a comment justifying it; no project-wide disables added casually.
- Names match the case table; no file > ~600 lines or method > ~30 lines without a reason.
- Flat control flow — guard clauses up top, ≤2 nesting levels; collection work uses the intent-matching array function, not a manual loop.
- No raw magic numbers, no negative-named predicates, no helper mutating shared/global state.
- (TS) no
as/!a guard already made redundant; no{}type;?only where a value can be absent.
Related
- structure-a-backend-service — folder/module/naming layout.
- write-service-code — control flow, async, queries, events, logging, integration tests.
- write-unit-tests — isolated unit tests.
- git-flow — branching & release workflow.
CLAUDE.md— the global engineering rules. This doc adds to them; it does not repeat them.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

