JavaScript
Specification
The words
,
,
,
,
,
,
,
,
, and
are interpreted as described in
RFC 2119.
Clarity is the highest JavaScript virtue. If your code requires a comment to explain its control flow, rewrite it.
JavaScript rewards explicit, readable code. You SHOULD prefer boring patterns that are easy to understand over clever
tricks that save characters.
References
- Functions, closures, composition → — Arrow function examples,
closure patterns, early return, parameter destructuring
- Async patterns, error handling, concurrency → — Promise.all/race/any
examples, cancellation, custom error classes, for-await
- Objects, arrays, iteration, Map/Set →
references/objects-and-arrays.md
— Iteration
decision table, destructuring patterns, immutable updates, generators
- ES modules, imports, barrel files → — Import ordering, barrel file
rationale, directory import pitfalls, dynamic imports
- JSDoc typing, full tag catalog → — Full tag reference (@callback,
@template, @enum), type assertions, class modifiers
- General JS idioms and edge cases → — Variable/naming examples,
equality coercion table, modern syntax patterns
Requests
- You are RECOMMENDED to use as the package manager unless told otherwise.
Variables and Declarations
- by default. You SHOULD use only when reassignment is required. You MUST NOT use .
- prevents reassignment, not mutation. Objects and arrays declared with can still be mutated.
- Block scope only. / are block-scoped; is function-scoped and hoists — this causes bugs in loops
and conditionals.
- One declaration per line. You MUST NOT chain .
- Group declarations. first, then .
Naming
| Entity | Style | Examples |
|---|
| Variables, functions | camelCase | , |
| Classes, constructors | PascalCase | , |
| True compile-time constants | SCREAMING_SNAKE_CASE | , |
| Private fields/methods | prefix (class) | , |
| Booleans | /// prefix | , |
| File names | kebab-case or camelCase | , |
- SCREAMING_SNAKE_CASE is for true constants only — values known at compile time, never computed at runtime. A
variable holding a function return value uses camelCase.
- Descriptive names. not . Short names (, ) only in tiny scopes (loop indices, simple arrow
callbacks).
- Accepted abbreviations: , , , , , — universally understood. Avoid all others.
- No redundant context. not .
- Consistent vocabulary. Use the same word for the same concept throughout a codebase — everywhere, not
/ / .
Equality and Safety
- Equality MUST use and . You MUST NOT use except for (checks both and ).
- over for defaults — treats , , as falsy.
- for optional access. Don't overuse — missing data you expect SHOULD throw, not silently return .
- Know the falsy values: , , , , , , , . Everything else is truthy,
including , , and .
Ternary Operator
-
One-liners or split-per-branch only. Ternaries are acceptable in two forms:
js
// OK — fits on one line
const label = isActive ? 'Active' : 'Inactive'
// OK — each branch on its own line
const label = isActive ? buildActiveLabel(user) : buildInactiveLabel(user)
Any ternary that doesn't fit one of these two patterns MUST be rewritten as
/
or early return.
-
Nested ternaries MUST NOT be used. Use
/
, early returns, or a lookup object.
Modern Syntax
- Template literals for string interpolation: . Don't use template literals for strings
without interpolation — use plain quotes.
- Spread for copies: and . You SHOULD NOT use .
- Rest parameters to collect remaining:
const { id, ...rest } = user
.
- Shorthand properties: not . Group shorthand properties at the top of
object literals.
- Computed property names: ${key}Date.
- Logical assignment operators: (assign if nullish), (assign if
falsy),
opts.handler &&= wrap(opts.handler)
(assign if truthy).
Functions
- Arrow functions for callbacks and anonymous functions. Use function declarations only when hoisting or
binding is needed.
- Prefer parentheses around arrow function parameters even for single params — smaller diffs when adding/removing
parameters.
- Implicit return for single expressions (no braces). Explicit return (braces) for multi-statement bodies.
- Arrow functions capture lexical — they do NOT have their own . You MUST NOT use arrow functions as object
methods or on prototypes.
- Destructured options for 3+ parameters. Self-documenting and order-independent.
- Default parameters over or manual checks. Defaults are evaluated left-to-right and can reference earlier
params.
- Rest parameters over object. is array-like, not a real Array.
- Early return. Guard clauses first, happy path flat. Reduce nesting.
- One function, one job. If the name contains "and", split it.
- Keep functions under ~30 lines. Extract helpers. Use composition over complex branching.
- Prefer pure functions (same input = same output, no side effects). Isolate side effects (DOM, network, logging) —
don't hide them inside data transformations.
- Closures retain references to outer variables, not copies. Be cautious with large objects captured unintentionally
— they won't be garbage collected until the closure is released.
Async
- / over chains for sequential operations.
- Promises MUST be awaited or explicitly handled. Missing = floating promise = silent failures.
- only inside blocks where you need to catch the awaited error. Otherwise just
— no need for wrapper.
- for independent parallel work. Rejects on first rejection.
- when all results matter regardless of individual failures.
- for timeouts. for fallbacks (rejects only when ALL reject).
- Avoid sequential awaits in loops. Use
Promise.all(items.map(...))
for parallel. Use a concurrency limiter (e.g.
) for large arrays.
- You MUST throw objects, not strings or plain objects — strings lose stack traces.
- Use when checking whether a caught value is an . Do not use
.
- Custom error classes when callers need to distinguish errors: extend , set , add context
properties.
- You MUST NOT swallow errors. Every MUST handle, rethrow, or report. Empty blocks hide bugs.
alone is not handling.
- Let errors propagate to a top-level handler when possible. Don't wrap every in / — only where
you need to handle at that level.
- Attach to non-awaited promise chains. Unhandled rejections crash Node.js. Fire-and-forget:
fetchData().catch(reportError)
.
- You SHOULD use only to wrap callback-based APIs. Most async code SHOULD compose existing promises with
/.
- for cancellable async operations: pass to and other APIs.
- for async iterables (streams, async generators).
Modules
- ES modules are REQUIRED for new code. / for all new code. CommonJS () is legacy — use only when runtime
requires it.
- Named exports over default exports. Default exports cause inconsistent naming across importers. Exception: default
exports acceptable when REQUIRED by framework convention (Next.js pages, Remix routes).
- Don't export mutable bindings. Export accessor functions instead:
export function getCount()
not
.
- Imports at the top, grouped with blank lines: built-in (), external (), internal ().
- Import paths MUST include file extensions — , not . Extensionless imports vary across
runtimes.
- No directory imports. Import from the file directly, not from a folder that resolves to .
- No barrel files in subdirectories. re-exports create indirection and hurt tree-shaking. Acceptable only
as a standalone package entry point where the runtime can enforce the boundary via .
- No circular dependencies. Extract shared code to a third module, merge tightly coupled modules, or use dependency
injection.
- No wildcard re-exports. Explicit re-exports only — wildcards bypass tree-shaking.
- Merge imports from the same module into a single statement.
- Namespace imports () for large modules (5+ items). Prefer named imports when importing fewer
than ~5 items.
- Dynamic imports () for code splitting and lazy loading: routes loaded on navigation, large conditional
dependencies, feature flags.
- One concern per module. If a module exports unrelated functionality, split it.
- Side-effect imports () SHOULD be rare. Document why.
Objects and Arrays
- Literal syntax. and , never / .
- Use method shorthand on objects: not .
- Spread for copies. and . Prefer over .
- Destructure to extract properties. Prefer parameter destructuring.
- Dot notation for static properties, brackets for dynamic: vs .
- instead of .
- Functional array methods (, , , , , , ) over imperative loops for
data transformation.
- Always return in , , callbacks.
- for array-like objects (not spread).
Array.from(iterable, mapFn)
instead of
— avoids intermediate array.
- Don't mutate inputs. Return new objects/arrays. Immutable update patterns: add , remove
, update .
- for side-effect loops. Never on arrays.
- Return objects for multiple values, not arrays — callers don't depend on order.
- Built-in prototypes MUST NOT be extended (, ). Use utility functions or subclasses.
Prefer
for side-effect loops,
methods (
,
,
,
,
,
) for data transforms,
for index-needed loops. See
references/objects-and-arrays.md
for the full iteration decision table.
Use
when keys aren't strings or are user-provided (avoids prototype pollution). Use
for dedup
(
). Use generators (
) for lazy sequences and deferred computation.
Classes
- ES syntax is REQUIRED. Do not use function constructors or prototype manipulation.
- fields for encapsulation. Not convention.
- Composition over inheritance. Use only for true "is-a" relationships.
- No empty constructors. If the constructor only calls , omit it.
- Static methods for operations that don't need instance state.
- Methods can return for fluent/chainable APIs.
- Don't force classes when plain functions and objects suffice. A class with one method is a function in disguise.
JSDoc Typing
For pure JavaScript projects that don't use TypeScript, use JSDoc annotations to provide type safety through editor
tooling. Enable
at file top or
in
.
Core tags:
,
,
,
(with
),
. See
for the full tag catalog including
,
, class modifiers, and
type import syntax.
JSDoc Best Practices
- Annotate public API boundaries — exported functions, classes, module-level variables. Internal code often needs
fewer annotations; types flow from context.
- Prefer inline TypeScript syntax in JSDoc types: over .
- Use for shared shapes — define once near file top or in .
- Don't annotate the obvious — if is clearly a number, skip .
Application
When writing JavaScript code:
- You MUST apply all conventions silently — don't narrate each rule being followed.
- If an existing codebase contradicts a convention, you MUST follow the codebase and flag the divergence once.
When reviewing JavaScript code:
- You MUST cite the specific violation and show the fix inline.
- You MUST NOT lecture or quote the rule — state what's wrong and how to fix it.
Bad review comment:
"According to best practices, use const instead of let
when the variable is never reassigned."
Good review comment:
"`let` -> `const` — `config` is never reassigned."
Code Navigation — LSP Required
A
typescript-language-server
LSP server is configured for all JS/TS file types (
,
,
,
,
,
,
,
).
You MUST use LSP tools for code navigation instead of Grep or Glob. LSP understands module
resolution, type inference, scope rules, and project boundaries — text search does not.
Tool Routing
- Find where a function/class/variable is defined → — Resolves imports, re-exports, aliases
- Find all usages of a symbol → — Scope-aware, no false positives from string matches
- Get type signature, docs, or return types → — Instant type info without reading source files
- List all exports/symbols in a file → — Structured output vs grepping for
//
- Find a symbol by name across the project → — Searches all modules
- Find implementations of an interface → — Knows the type system
- Find what calls a function → — Precise call graph across module boundaries
- Find what a function calls → — Structured dependency map
Grep/Glob remain appropriate for: text in comments, string literals, log messages, TODO markers, config values, env
vars, CSS classes, file name patterns, URLs, error message text.
When spawning subagents for JS/TS codebase exploration, you MUST instruct them to use LSP tools. Subagents have access to the
same LSP server.
Integration
The coding skill governs workflow; this skill governs JavaScript implementation choices. For TypeScript projects,
the typescript skill extends this one.