agentsclimarketplace

Code conventions

Skill kennguyen887/agent-foundation/skills/code-conventions

Claude Code skills marketplace — backend & frontend engineering conventions + step-by-step third-party integration recipes: Stripe, Rapyd, CyberSource, UOB & wallet payments, Singpass/Keycloak OIDC & 3-D Secure, Twilio SMS, Docker & CI/CD. NestJS/TypeScript + React, language-flexible.

Install
npx -y skills add kennguyen887/agent-foundation --skill code-conventions

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

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.

SKILL.md

11.4 KB, as published. Nobody here has run it

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 gostructure-a-backend-service; service-internal patterns (queries, events, logging, tests) → write-service-code; unit testswrite-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

RoleCaseExample
File, folder, routekebab-caseyour-file.service.ts, /listing-photos
Class, module, enum, decoratorPascalCaseListingService, ListingStatus
Variable, method, functioncamelCasefirstName, getListingDetail
ConstantSCREAMING_SNAKE_CASEconst 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:

IntentFunctionWhat it does
keep a subsetfilternew array of the elements that pass the test
transform each elementmapnew array, each element run through the callback
collapse to one valuereducefolds the array into a single value via an accumulator
first element matchingfindthe first element that passes the test, else undefined
does any match?sometrue if at least one element passes
do all match?everytrue if every element passes
map then flatten one levelflatMapmap + one level of flattening
pure side effect, nothing else fitsforEachlast 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, not x = price * 1.07.
  • Negative conditionals → positive predicates. Define isOnline(...), not isNotOnline(...); read it as if (!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-level name. ▸ Good: toBase64(text): string returns 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. Use Record<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):

ConventionHome
Pipelines over for/while loopsglobal 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 asyncwrite-service-code §2
Private helpers below public methodswrite-service-code §4
Query performance — avoid N+1, upsert, select needed fields, single round-trip, joins, indexes/orderBywrite-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 modelsstructure-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 reviewCLAUDE.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

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.