Jesse A. Green / notebook

clean-code-javascript User Guide

Generated May 2026 from ryanmcdermott/clean-code-javascript at 5311f64

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Software engineering principles from Robert C. Martin's Clean Code, adapted for JavaScript.

clean-code-javascript is a single-document reference guide that distils the principles of Robert C. Martin's book Clean Code and re-expresses every example in modern JavaScript (ES2015/ES6, ES2017/ES8). It is not a style guide — automated formatters already solve spacing, quotes, and semicolons. It is a guide to writing readable, reusable, and refactorable JavaScript.

This User Guide shows you how to read the document, how to find the principle you need, and how to adopt it on a real project without getting into fights with your team.

Note

Every principle in clean-code-javascript is a guideline, not a rule. The README itself says so: "Not every principle herein has to be strictly followed, and even fewer will be universally agreed upon." Treat it as a touchstone, not a checklist.

1. Quick Start

There is nothing to install. clean-code-javascript is a README.md on GitHub. You read it, you absorb the patterns, you apply them to code you already write.

Read it online

Open the repository README in your browser:

https://github.com/ryanmcdermott/clean-code-javascript

Every principle is a level-3 heading with a "Bad" example and a "Good" example side by side.

Read it offline

Clone the repo so the guide travels with you:

git clone https://github.com/ryanmcdermott/clean-code-javascript.git
cd clean-code-javascript
Cloning into 'clean-code-javascript'...
remote: Enumerating objects: done.
Resolving deltas: 100% done.

Open README.md in any Markdown viewer (VS Code, Obsidian, Typora, glow, mdcat). Fenced JavaScript blocks render with syntax highlighting in every modern viewer.

Read it in your language

The guide has been translated into 20+ languages by the community. See Section 8. Translations for the full list with links.

Tip

The README has a clickable Table of Contents at the top and a "⬆ back to top" link after every principle, so it is designed for scan-reading rather than cover-to-cover reading. Use the table of contents to jump to the category you care about right now.

2. What the Guide Covers

The document is organised into eleven principle categories plus a list of community translations. The categories map directly to the chapters of Robert C. Martin's book.

# Category What it covers
1 Introduction What the guide is and is not
2 Variables Naming, defaulting, and contextual clarity
3 Functions Argument counts, single responsibility, side effects, composition
4 Objects and Data Structures Getters/setters, private members via closures
5 Classes ES6 classes, method chaining, composition over inheritance
6 SOLID SRP, OCP, LSP, ISP, DIP with JavaScript examples
7 Testing Single concept per test
8 Concurrency Promises over callbacks, async/await over Promises
9 Error Handling Don't swallow errors; handle rejected promises
10 Formatting Consistent capitalization; keep callers near callees
11 Comments Comment only business logic; no journal or commented-out code
12 Translation Community translations in 20+ languages

Each category contains between one and twenty individual principles. A principle is presented as:

  1. A short name (e.g. "Use searchable names").
  2. One or two paragraphs of prose explaining why.
  3. A Bad JavaScript example.
  4. A Good JavaScript example.
  5. A link back to the top of the document.

This is the only structure you need to remember: name → why → bad → good.

3. A Tour of the Eleven Categories

This section summarises the principles so you can orient yourself before reading the full text. It is a map, not a substitute — the real value of the guide is in the side-by-side examples.

3.1 Variables

Seven principles about naming and defaulting.

  • Use meaningful and pronounceable variable names — currentDate, not yyyymmdstr.
  • Use the same vocabulary for the same type of variable — pick getUser and stop switching between getUserInfo, getClientData, and getCustomerRecord.
  • Use searchable names — const MILLISECONDS_PER_DAY = 86_400_000, not a naked 86400000 buried in a setTimeout call.
  • Use explanatory variables — extract regex capture groups into named destructured variables instead of re-indexing address.match(...) twice.
  • Avoid mental mapping — location, not l, in a forEach body.
  • Don't add unneeded context — if the object is named Car, the property is color, not carColor.
  • Use default parameters instead of short-circuiting — prefer function f(name = "Hipster Brew Co.") over name = name || "Hipster Brew Co.".

3.2 Functions

The longest chapter. Twenty principles, and the one most people quote.

  • Function arguments (2 or fewer ideally) — three should be avoided; more than three should be a destructured options object.
  • Functions should do one thing — "by far the most important rule in software engineering."
  • Function names should say what they do — addMonthToDate(month, date) beats addToDate(date, month).
  • Functions should only be one level of abstraction — a parser function shouldn't both tokenize AND walk the AST.
  • Remove duplicate code — but only when the shared abstraction is correct; bad abstractions are worse than duplication.
  • Set default objects with Object.assign — merge caller config onto defaults in one expression.
  • Don't use flags as function parameters — split createFile(name, temp) into createFile(name) and createTempFile(name).
  • Avoid side effects (parts 1 and 2) — centralise I/O; clone before mutating caller-owned arrays and objects.
  • Don't write to global functions — extend via class X extends Array, not Array.prototype.diff = ....
  • Favor functional over imperative — reduce over for (let i = 0; ...).
  • Encapsulate conditionals — if (shouldShowSpinner(fsm, listNode)) beats an inlined conjunction.
  • Avoid negative conditionals — isDOMNodePresent(node), never !isDOMNodeNotPresent(node).
  • Avoid conditionals — use polymorphism where a switch on type would otherwise grow.
  • Avoid type-checking (parts 1 and 2) — rely on consistent APIs, or move to TypeScript if you really need static types.
  • Don't over-optimize — modern engines already cache list.length.
  • Remove dead code — version control remembers.

3.3 Objects and Data Structures

Two principles about access patterns.

  • Use getters and setters — they let you add validation, logging, and lazy-loading without rewriting every call site.
  • Make objects have private members — use closures (ES5 and below) or #privateField syntax (ES2022+) to prevent external deletion and mutation.

3.4 Classes

Three principles about when and how to use ES6 classes.

  • Prefer ES2015/ES6 classes over ES5 plain functions — cleaner inheritance, cleaner construction, cleaner method definitions.
  • Use method chaining — return this so callers can write new Car(...).setColor("pink").save().
  • Prefer composition over inheritance — Employee.setTaxData(...) that instantiates an EmployeeTaxData beats class EmployeeTaxData extends Employee.

3.5 SOLID

All five classical object-oriented principles, each with a JavaScript example.

Letter Principle Short form
S Single Responsibility A class should have one reason to change.
O Open/Closed Open for extension; closed for modification.
L Liskov Substitution Subtypes must be substitutable for their base types.
I Interface Segregation Don't force clients to depend on methods they don't use.
D Dependency Inversion Depend on abstractions, not concrete implementations.

Important

The ISP entry notes that JavaScript has no interface keyword, so ISP applies loosely via duck typing. The DIP entry notes that "abstractions" in JavaScript are implicit contracts — the shape of methods an object exposes, not a formal interface declaration.

3.6 Testing

One principle, loudly stated: "Testing is more important than shipping."

  • Single concept per test — one it(...) block, one behaviour. Multiple assertions are fine as long as they cover the same concept (e.g. "handles leap year").

3.7 Concurrency

Two principles for asynchronous JavaScript.

  • Use Promises, not callbacks — callbacks create nesting; Promises chain flatly.
  • Async/Await is even cleaner than Promises — try { await fn() } catch (err) { ... } reads top-to-bottom like synchronous code.

3.8 Error Handling

Two principles about not swallowing errors.

  • Don't ignore caught errors — console.log(error) in a catch is the anti-pattern; do at least one of: console.error, notifyUserOfError, reportErrorToService.
  • Don't ignore rejected promises — a .catch(error => console.log(error)) is the same anti-pattern as its synchronous twin.

3.9 Formatting

Two principles that survive automated formatters.

  • Use consistent capitalization — pick UPPER_SNAKE for constants and PascalCase for classes, then stick with it.
  • Function callers and callees should be close — code reads top to bottom like a newspaper, so caller goes above callee in the source file.

Tip

The guide is explicit that formatting debates are a waste of time. "DO NOT ARGUE over formatting. There are tons of tools to automate this. Use one!" Let Prettier/ESLint/standard pick, then move on.

3.10 Comments

Four principles that push you toward self-documenting code.

  • Only comment things that have business logic complexity — "Comments are an apology, not a requirement."
  • Don't leave commented-out code — Git remembers.
  • Don't have journal comments — git log is your journal.
  • Avoid positional markers — no //// SECTION //// banners; indentation and names already give structure.

4. Everyday Usage: Applying the Principles

4.1 Reading Order for New JavaScript Engineers

If you have less than two years of JavaScript experience, read the guide in this order. The payoff per minute of reading is highest at the top.

  • [ ] Functions (Section 3.2) — the longest and highest-impact chapter
  • [ ] Variables (Section 3.1) — short, immediately actionable
  • [ ] Error Handling (Section 3.8) — two principles, both prevent production bugs
  • [ ] Concurrency (Section 3.7) — needed for any Node.js or front-end work
  • [ ] Comments (Section 3.10) — teaches you what not to write
  • [ ] Objects and Data Structures (Section 3.3) and Classes (Section 3.4) — needed when you start writing larger modules
  • [ ] SOLID (Section 3.5) — read once you have written a handful of classes and have felt the pain these principles solve
  • [ ] Testing (Section 3.6), Formatting (Section 3.9) — short; absorb when convenient

4.2 Reading Order for Senior Engineers Doing Code Review

If you are an experienced engineer adopting the guide as a review lens, start with these two chapters, because they produce the largest behavioural change in a pull request:

  1. Functions → "Functions should do one thing" — every pull request is the right moment to apply this.
  2. SOLID → Single Responsibility Principle — the class-level companion to the above.

Then keep the full README open in a browser tab during review so you can link to a specific principle when leaving a comment. GitHub anchor links follow the pattern README.md#<principle-name-kebab-case>, e.g. README.md#use-searchable-names.

4.3 Integrating With Your Team

Tip

clean-code-javascript is most useful as a shared vocabulary. When you say "this function has a flag parameter" or "this needs SRP" during review, your teammate can follow the link, see the same Bad/Good pair, and respond concretely.

A simple adoption workflow for a 3-10 person team:

  • [ ] Share the link to the README in your team's onboarding document.
  • [ ] Pick one category per sprint and discuss it in a 30-minute reading group. Seven categories = roughly one quarter of weekly sessions.
  • [ ] Add a link in your CONTRIBUTING.md or CODE_REVIEW.md: "We use clean-code-javascript as our review vocabulary. Please skim the Functions section before your first PR."
  • [ ] When a reviewer wants to cite a principle, link to the anchor — don't paraphrase. The paraphrase loses the Bad/Good example.

4.4 Pairing With Automated Tools

Many principles in clean-code-javascript can be partially enforced by linters and formatters. Use both — automated enforcement for mechanical rules, human review for the rest.

Principle Enforceable by
"Use searchable names" (no magic numbers) ESLint no-magic-numbers
"Don't leave commented-out code" ESLint no-warning-comments with custom patterns; eslint-plugin-no-commented-out-code
"Don't have journal comments" Code review only
"Use consistent capitalization" Prettier / ESLint camelcase, new-cap
"Avoid negative conditionals" Code review only
"Don't ignore rejected promises" ESLint no-floating-promises (TypeScript ESLint)
"Async/Await over Promises" ESLint prefer-async-await (via plugins)
"Function arguments (2 or fewer)" ESLint max-params
"Avoid type-checking (part 2)" Migrate to TypeScript

The guide itself mentions two external tools by name: buddy.js and ESLint, both for catching unnamed magic numbers.

Warning

Do not configure a linter so strictly that it rejects all if statements in the name of "avoid conditionals". The guide's advice is aspirational; your reviewer judges when polymorphism is worth the indirection. Automated enforcement of aspirational rules produces noise, not cleaner code.

5. Common Tasks

5.1 Find a Specific Principle Quickly

Open the README on GitHub and use the browser's page search (Ctrl+F / Cmd+F). Every principle heading is unique, so searching for a few distinctive words — "mental mapping", "flags as function", "method chaining" — lands you on the right block.

Anchor links follow GitHub's kebab-case convention:

https://github.com/ryanmcdermott/clean-code-javascript#<principle-slug>

For example:

https://github.com/ryanmcdermott/clean-code-javascript#functions-should-do-one-thing
https://github.com/ryanmcdermott/clean-code-javascript#avoid-negative-conditionals
https://github.com/ryanmcdermott/clean-code-javascript#use-promises-not-callbacks

5.2 Copy a "Good" Example Into Your Codebase

Every code block in the guide is plain, fenced Markdown with the javascript language tag, so you can copy a Good example directly from GitHub's rendered view and paste it into your editor. Most examples are intentionally minimal — you will usually need to rename identifiers and wire the snippet into your real types and modules.

Note

Good examples sometimes use libraries that are not part of the standard library: moment (date formatting), request-promise and fs-extra (Node HTTP and filesystem), immutable-js (immutable collections). If you copy an example that uses one of these, install the library first or swap in a modern equivalent — Intl.DateTimeFormat for moment, native fetch for request, native fs.promises for fs-extra.

5.3 Cite a Principle in a Code Review

Paste the anchor link in a GitHub review comment:

This looks like a "Functions should do one thing" case — can we split
the DB lookup out of the rendering path?

Ref: https://github.com/ryanmcdermott/clean-code-javascript#functions-should-do-one-thing

GitHub renders the link with the repository's social-preview image and the principle's heading as the title, so the referenced principle is visible without a click.

5.4 Fork and Customise for Your Own Language or House Style

The guide's structure (category → principle name → prose → bad → good) has been forked to document clean code in Python, PHP, Go, Java, C#, TypeScript, and many other languages. If you want to build your own derivative:

  • [ ] Clone the repo (see Quick Start).
  • [ ] Keep the top-level category list as your skeleton.
  • [ ] Rewrite each Bad/Good pair in your target language; delete principles that don't apply (e.g. Object.assign has no analogue in Go).
  • [ ] Keep the Table of Contents and the "⬆ back to top" links — they are what make the document navigable at 40 printed pages.
  • [ ] Pick a licence and state it in LICENSE.

The original repository is published under the MIT License, so forking and relicensing your derivative is permitted as long as you retain Ryan McDermott's copyright notice in your LICENSE file.

6. Troubleshooting

Error: My teammate disagrees with a principle and cites another blog post

Cause. The guide is explicit that "not every principle herein has to be strictly followed, and even fewer will be universally agreed upon." It is not the only book on the subject, and some principles (e.g. Avoid conditionals) are stylistic.

Solution. Treat the disagreement as a design conversation, not a compliance audit. If the teammate's argument is sound, take the principle as a guideline in that case. Document the team's final decision in your own CODE_REVIEW.md so you don't re-litigate it on every PR.

Prevention. Agree early as a team on which principles are mandatory (usually: naming, single responsibility, error handling) and which are advisory (usually: "avoid conditionals", "method chaining", "composition over inheritance").

Error: A Good example uses a deprecated or missing library

Cause. The guide's commit history shows the last substantial content update was in early 2023, and the README references libraries like request (deprecated 2020) and request-promise (deprecated 2020).

Solution. When you adopt a concurrency or I/O pattern, swap the example's library for a currently-maintained equivalent:

Example uses Modern equivalent
request, request-promise native fetch (Node 18+), undici, axios, got
fs-extra native fs.promises (Node 10+)
moment Intl.DateTimeFormat, date-fns, luxon, Temporal proposal
Object.assign({}, defaults, config) spread syntax { ...defaults, ...config }

Prevention. The principles are about shape — Promises over callbacks, async/await over Promises, immutable updates — not about the specific package. When you read, abstract the shape away from the library.

Error: "Avoid conditionals" makes my simple function needlessly complex

Cause. Polymorphism is a good fit when you have three or more switch cases that each touch multiple fields. For a one-off two-branch if, introducing a class hierarchy adds more code than it removes.

Solution. Apply the rule only when the conditional is already growing. The Airplane/Boeing777/AirForceOne/Cessna example in the guide has three branches, and each branch has a different calculation — that is the threshold where polymorphism pays off.

Prevention. Use the Extract Method / Encapsulate Conditionals refactor first. Only promote to polymorphism once you have three or more conditionals that dispatch on the same discriminator.

Cause. The guide was last updated in February 2023. Some third-party links in prose (e.g. coverage tools, test-framework lists) have shifted since.

Solution. For coverage, use the Istanbul/nyc successor — c8 on Node 14+ (built on native V8 coverage). For test frameworks, search npm directly for currently popular frameworks.

Prevention. Treat external links in the guide as "things existed when this was written", not "things you must use today". The principles are timeless; the tool list is not.

Error: My linter flags the Good example for no-prototype-builtins, prefer-const, or similar

Cause. The Good examples are illustrative of the specific principle being taught. They are not meant to be lint-clean against every modern ESLint ruleset.

Solution. Apply the principle, then run your linter and fix secondary violations. The guide teaches shape; your linter teaches polish.

Prevention. Don't paste Good examples directly into production code. Copy, adapt, run the linter, commit.

7. Frequently Asked Questions

Q: Is this the same as Robert C. Martin's book Clean Code? No. The book is a 464-page treatment of the same principles with Java/C# examples and additional chapters on Boundaries, Emergent Design, and Concurrency. This guide is a ~40-page distillation with JavaScript examples. Read both if you can; read the guide first.

Q: Does this cover TypeScript? No. The guide is pure JavaScript (ES2015/ES6 and ES2017/ES8 features). The Avoid type-checking (part 2) principle recommends TypeScript, but there are no TypeScript code examples. For a TypeScript-focused equivalent, look for community forks named clean-code-typescript.

Q: Does it cover React, Vue, Angular, or Node.js idioms? No. The principles are at the language level: variables, functions, classes, errors. Framework-specific style is out of scope.

Q: Is there a version for async iterators / AbortController / ES2020+? Not explicitly. The latest async feature shown is ES2017/ES8's async/await. Newer features (optional chaining, nullish coalescing, private class fields, top-level await, async iterators, AbortController) are not discussed.

Q: Why does the guide use Object.assign instead of spread syntax? The Object.assign example is demonstrating a pattern (defaults + override) that pre-dates widespread object-spread support. Spread syntax is equivalent and often clearer today — use { ...defaults, ...config } in new code.

Q: Does the guide apply to library authors, application authors, or both? Both, but weight varies. Library authors should weight the Functions (arguments, single responsibility, no flags) and Objects and Data Structures (getters/setters, private members) chapters highly because their surface area is the API. Application authors should weight Variables, Error Handling, and Comments more, because most of their code is private to the application.

Q: How do I cite the guide academically?

McDermott, R. (2023). clean-code-javascript.
GitHub. https://github.com/ryanmcdermott/clean-code-javascript

Q: Can I contribute a new principle? Yes — open a pull request against master. The repo accepts principle additions and translations; see the Admin Guide for the contribution workflow.

Q: Can I contribute a translation? Yes. Fork the repo, translate README.md into your language, and open an issue on the original repo asking for your fork to be linked in the Translation section at the bottom. The guide is already linked to 20+ community translations this way.

Q: What about "Avoid comments entirely"? The guide's position is not "never comment". It is "comments are an apology; aim for self-documenting code and write comments only when business logic is genuinely complex". Code with no comments is not automatically cleaner than code with comments — code with obsolete or redundant comments is what the guide targets.

Q: Is this still maintained? The repository was last substantially updated in February 2023. It is in a relatively stable state — fundamentals do not change quickly. Open issues (see the project's issue tracker) tend to be minor corrections, translation requests, and discussion of new principles.

Q: What licence is the guide under? MIT. You can fork, adapt, and redistribute it for private or commercial use as long as you retain the copyright notice.

Q: I disagree with the guide's take on getters and setters. What now? That is allowed. The introduction explicitly says these are "guidelines and nothing more". Take what fits your team's style; skip what does not. Don't try to enforce every principle on every codebase — the guide itself would consider that over-application.

Q: Does applying every principle actually make my code better? Not necessarily. The introduction warns: "knowing these won't immediately make you a better software developer, and working with them for many years doesn't mean you won't make mistakes." The value is in having a shared vocabulary for review, not in ticking boxes.

Q: What is [⬆ back to top] all over the document? Each principle ends with a link back to the Table of Contents. It lets you jump category-to-category without scrolling through a 40-page document. No action required — it's a navigation aid, not a rule to follow.

Q: The Good example for "Avoid Side Effects (part 2)" uses [...cart, item]. Does that scale? For lists of hundreds of thousands of items, structural sharing via a persistent-data-structure library (the guide mentions immutable-js) is faster than re-copying the array on every update. For everyday shopping carts, the spread operator is fine.

Q: The guide says "Don't over-optimize". When IS optimization appropriate? When you have measured the hot path with a profiler and the bottleneck is in code you control. The guide's advice is against speculative optimization — rewriting loops because of a 2008 article, or manually caching list.length in modern engines. Profile first, then optimize the measured hot spots.

8. Translations

The original guide has been translated into 20+ languages by the community. All translations are forks maintained by independent authors; quality and freshness vary.

Note

Translations are community forks, not official releases. They may lag behind the English version by months or years. If you find a translation that is stale and you speak the language, open an issue on that fork offering to help update it.

9. Reference

9.1 Source Document

  • Canonical URL: https://github.com/ryanmcdermott/clean-code-javascript
  • Author: Ryan McDermott
  • Licence: MIT
  • Last commit at doc generation: 5311f64 (2023-02-11)

These are the full GitHub anchor links to every category heading. Bookmark the ones you cite most often in code review.

Category Anchor
Variables #variables
Functions #functions
Objects and Data Structures #objects-and-data-structures
Classes #classes
SOLID #solid
Testing #testing
Concurrency #concurrency
Error Handling #error-handling
Formatting #formatting
Comments #comments
Translation #translation

9.3 Further Reading

The guide explicitly references Ryan McDermott's companion work:

  • 3 R's of Software Architecture — https://github.com/ryanmcdermott/3rs-of-software-architecture Readable, reusable, refactorable — the same three properties the guide optimises for.

The guide also references Robert C. Martin's source book, Clean Code: A Handbook of Agile Software Craftsmanship (2008, ISBN 0-13-235088-2), as the origin of every principle included.

9.4 External Tools Mentioned in the Guide

Tool Purpose Principle it supports
buddy.js Magic-number detection Use searchable names
ESLint JavaScript linting Multiple (naming, max-params, etc.)
standard Auto-formatter Formatting (all rules)
Istanbul Test coverage Testing (100% coverage goal)
immutable-js Persistent data structures Avoid Side Effects (part 2)