coding-standards
Coding Standards & Best Practices
⚠️ NON-NEGOTIABLE RULES (apply before anything else)
Rules are ordered by impact on readability, maintainability, comprehensibility, and evolvability — highest impact first.
- NEVER mutate objects or arrays — always use spread (or immutable methods). Mutations break predictability, debugging, and React’s model; they make refactors and evolution risky.
- NEVER use
any— define precise interfaces/types. Types are the main documentation and safety net;anyremoves both and makes the codebase hard to understand and change. - NO inline types — extract types/interfaces to named declarations. Single source of truth, reuse, and self-documenting code; changes stay in one place.
- NO deep nesting — use early returns. Flat control flow is the largest readability win; nested conditionals are hard to scan and maintain.
- Functions under 50 lines / Files under 800 lines — split when exceeded. Small units are scannable, testable, and keep a single responsibility; large blocks are the opposite.
- ALWAYS handle errors in async functions with try/catch — never swallow silently; add context to messages and rethrow when the caller must know. Unhandled or silent errors make behavior incomprehensible and bugs hard to fix.
- NO magic numbers or unexplained strings — extract as named constants. Names explain intent and centralize values for safe evolution.
- Prefer
??over||for null/undefined — nullish coalescing only replacesnull/undefined;||also replaces0,"", andfalse, which often causes subtle bugs. - ALWAYS use arrow functions at top level —
const fn = () => {}; nofunctionkeyword for module-level functions. Consistent style reduces cognitive load. - React: use setState updater when the next state depends on the previous —
setCount((prev) => prev + 1). Usingcountdirectly can be stale and cause wrong behavior. - React: explicit booleans in conditionals — e.g.
hasItems && <List />, notitems.length && <List />(avoids rendering0). Conditionals must be clearly boolean. - React: list keys from stable id — prefer
key={item.id}(or stable id); avoidkey={index}unless the list is static and not reordered. - useEffect: always return a cleanup when you set up subscriptions, intervals, or listeners — avoids leaks and updates after unmount.
- NO
console.login production code — use a logger with levels; logs left in code clutter output and can leak sensitive data.
Agent Instructions
After reading this skill:
- Apply ALL rules to every file you create or modify
- Run the Pre-output Validation checklist before returning any code
- If a rule conflicts with a user request, flag it explicitly and propose a compliant alternative
- Reference specific rule names when explaining choices (e.g., "Per the KISS principle, I simplified this by...")
- Load example files on demand — only read the relevant file for the task at hand
Code Quality Principles
| Principle | Rule |
|---|---|
| Readability First | Code is read more than written. Clear names > clever code. |
| KISS | Simplest solution that works. Avoid over-engineering. |
| DRY | Extract common logic. Create reusable components. |
| YAGNI | Don't build features before they're needed. |
| Immutability | Never mutate — always return new values. |
Sections & Example Files
TypeScript / JavaScript
| Topic | Example File | When to load |
|---|---|---|
| Variable & function naming (incl. boolean prefixes) | examples/typescript/naming.ts |
When naming anything (arrow functions only; booleans: is/has/should/can/will) |
| Immutability patterns | examples/typescript/immutability.ts |
When working with state/objects/arrays |
| Error handling | examples/typescript/error-handling.ts |
When writing async code |
| Async / Promise patterns | examples/typescript/async-patterns.ts |
When using await/Promise |
| Type safety | examples/typescript/type-safety.ts |
When defining interfaces/types (no inline types; no nested types; extract named types) |
| Control flow & readability | examples/typescript/control-flow.ts |
Early returns, const vs let, Array.includes/some, nullish coalescing, destructuring |
React
| Topic | Example File | When to load |
|---|---|---|
| Component structure | examples/react/component-structure.tsx |
When creating a component |
Testing
| Topic | Example File | When to load |
|---|---|---|
| Unit test patterns | examples/testing/unit-testing-patterns.tsx |
When writing Jest/RTL tests (AAA, screen, spyOn, it.each, getByRole, mock factory) |
Anti-patterns (read during code review)
| Topic | File |
|---|---|
| All BAD patterns grouped | anti-patterns/what-not-to-do.ts |
| Code smells detection | anti-patterns/code-smells.ts |
File Organization Rules
- 200–400 lines typical file length
- 800 lines absolute maximum
- One responsibility per file (high cohesion, low coupling)
- File names: always kebab-case (lowercase with hyphens). No PascalCase or camelCase in file or folder names.
components/button.tsx # kebab-case (not Button.tsx)
hooks/use-auth.ts # kebab-case (not useAuth.ts)
lib/format-date.ts # kebab-case (not formatDate.ts)
types/market.types.ts # kebab-case + optional .types / .utils / .store suffix
features/market-list/market-list-item.tsx
Components and hooks are still exported with PascalCase (components) or camelCase with use prefix (hooks); only the file name is kebab-case.
Pre-output Validation (MANDATORY)
Before returning any code, verify each point:
- No direct mutations → convert to spread/immutable pattern
- No
anytypes → replace with proper interfaces - No inline types → extract to named types/interfaces (DRY, reuse)
- No deep nesting (>4 levels) → refactor with early returns
- Every function under 50 lines / file under 800 lines → split if needed
- All async functions have try/catch (no silent swallow) → add if missing
- No magic numbers or unexplained strings → extract as named constants
- Prefer
??over||for null/undefined defaults - React:
setStateuses updater when next state depends on previous →setX((prev) => ...) - React: conditional rendering uses explicit booleans (e.g.
hasItems &&, notitems.length &&) - React: list keys use stable id (not index) when list can change
-
useEffectwith subscriptions/intervals/listeners returns cleanup → add if missing - No
console.logleft in production code → use logger - New file names are kebab-case (e.g.
market-list-item.tsx,use-auth.ts) → rename if not
More from lichens-innovation/skills
react-single-responsibility
Strategies to simplify components, hooks, and methods: decomposition order (utilities, hooks, sub-components), early returns, control flow, parameter design, and code smell fixes. Use when the user says: ungodify this method/function/component, simplify this method/function/component, make this method/function/component less complex; or when refactoring a large component, hook, or function, reducing complexity, applying single responsibility, or asking how to simplify a component, hook, or method.
30generate-pr-description
Generates pull request descriptions by comparing current branch with parent branch. Creates semantic commit-style PR titles and fills PR templates. Use when the user asks to generate PR description, prepare pull request, or create merge request description. The user may include ticket IDs in the request (e.g. tickets: NN-123, TB-456) from the company tracking system; treat those as the related issue IDs for the PR.
28review-staged-changes
Reviews staged git changes for code quality, maintainability, readability, and potential regressions. Verifies changes make sense in context, improve maintainability, enhance readability, and don't introduce side effects. Use when reviewing staged changes, examining git diffs, or when the user asks to review modifications before committing.
25hello-world
This skill welcomes users in ASCII art with OS information. Use when the user says 'hello' or 'hi'.
20react-coding-standards
Enforces internal React and TypeScript coding standards using avoid/prefer rules. Use when reviewing or refactoring React/TS code, applying company standards, or when the user asks to align code with coding standards.
13typescript-and-react-guidelines
|
10