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-javascriptis 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:
- A short name (e.g. "Use searchable names").
- One or two paragraphs of prose explaining why.
- A Bad JavaScript example.
- A Good JavaScript example.
- 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, notyyyymmdstr. - Use the same vocabulary for the same type of variable — pick
getUserand stop switching betweengetUserInfo,getClientData, andgetCustomerRecord. - Use searchable names —
const MILLISECONDS_PER_DAY = 86_400_000, not a naked86400000buried in asetTimeoutcall. - Use explanatory variables — extract regex capture groups into named
destructured variables instead of re-indexing
address.match(...)twice. - Avoid mental mapping —
location, notl, in aforEachbody. - Don't add unneeded context — if the object is named
Car, the property iscolor, notcarColor. - Use default parameters instead of short-circuiting — prefer
function f(name = "Hipster Brew Co.")overname = 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)beatsaddToDate(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)intocreateFile(name)andcreateTempFile(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, notArray.prototype.diff = .... - Favor functional over imperative —
reduceoverfor (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
switchontypewould 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
#privateFieldsyntax (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
thisso callers can writenew Car(...).setColor("pink").save(). - Prefer composition over inheritance —
Employee.setTaxData(...)that instantiates anEmployeeTaxDatabeatsclass 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
interfacekeyword, 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 acatchis 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_SNAKEfor constants andPascalCasefor 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 logis 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:
- Functions → "Functions should do one thing" — every pull request is the right moment to apply this.
- 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-javascriptis 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.mdorCODE_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
ifstatements 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-promiseandfs-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.DateTimeFormatformoment, nativefetchforrequest, nativefs.promisesforfs-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.assignhas 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.
Error: Links to gotwarlost.github.io/istanbul/ or jstherightway.org return 404 or have moved
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.
| Language | Link |
|---|---|
| Armenian | hanumanum/clean-code-javascript |
| Bangla (বাংলা) | InsomniacSabbir/clean-code-javascript |
| Brazilian Portuguese | fesnt/clean-code-javascript |
| Simplified Chinese | alivebao/clean-code-js, beginor/clean-code-javascript |
| Traditional Chinese | AllJointTW/clean-code-javascript |
| French | eugene-augier/clean-code-javascript-fr |
| German | marcbruederlin/clean-code-javascript |
| Indonesian | andirkh/clean-code-javascript |
| Italian | frappacchio/clean-code-javascript |
| Japanese | mitsuruog/clean-code-javascript |
| Korean | qkraudghgh/clean-code-javascript-ko |
| Persian | hamettio/clean-code-javascript |
| Polish | greg-dev/clean-code-javascript-pl |
| Russian | BoryaMogila/clean-code-javascript-ru, maksugr/clean-code-javascript |
| Serbian | doskovicmilos/clean-code-javascript |
| Spanish | tureey/clean-code-javascript, andersontr15/clean-code-javascript-es |
| Turkish | bsonmez/clean-code-javascript (turkish-translation) |
| Ukrainian | mindfr1k/clean-code-javascript-ua |
| Vietnamese | hienvd/clean-code-javascript |
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)
9.2 Anchor Link Reference
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-architectureReadable, 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) |