@vintcessun: A problem that has plagued countless teams finally has a solution. When writing JavaScript, the worst fear is that a day later you can't even understand your own code—variable names are arbitrary, functions are long and messy. This project adapts the engineering principles of Clean Code to JS, with each principle accompanied by a bad/good comparison, explaining why the change should be made. In short, it solves…
Summary
This project adapts the engineering principles of Clean Code to JavaScript, providing bad/good comparisons for each principle to help developers write readable, reusable, and refactorable code, solving the pain of code rot in team collaboration.
View Cached Full Text
Cached at: 07/03/26, 08:33 AM
A solution to a headache that has plagued countless teams has finally arrived. The scariest thing about writing JavaScript is that you can’t understand your own code just a day later—variable names are haphazard, functions are long and tangled. This project adapts the engineering principles from Clean Code to JavaScript, pairing each principle with bad/good comparisons and explaining why the change is made. In essence, it doesn’t solve syntax problems but addresses the pain of code decay in team collaboration. It works by breaking down abstract principles into concrete, executable rules, such as “functions should do one thing” and “variable names should be searchable.” Looking back at code I modified in the early hours, I truly feel that those pitfalls I encountered have been filled. The repository is here —
ryanmcdermott/clean-code-javascript Source: https://github.com/ryanmcdermott/clean-code-javascript
clean-code-javascript
Table of Contents
- Introduction
- Variables
- Functions
- Objects and Data Structures
- Classes
- SOLID
- Testing
- Concurrency
- Error Handling
- Formatting
- Comments
- Translation
Introduction
Humorous image of software quality estimation as a count of how many expletives you shout when reading code
Software engineering principles, from Robert C. Martin’s book Clean Code (https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), adapted for JavaScript. This is not a style guide. It’s a guide to producing readable, reusable, and refactorable (https://github.com/ryanmcdermott/3rs-of-software-architecture) software in JavaScript.
Not every principle herein has to be strictly followed, and even fewer will be universally agreed upon. These are guidelines and nothing more, but they are ones codified over many years of collective experience by the authors of Clean Code.
Our craft of software engineering is just a bit over 50 years old, and we are still learning a lot. When software architecture is as old as architecture itself, maybe then we will have harder rules to follow. For now, let these guidelines serve as a touchstone by which to assess the quality of the JavaScript code that you and your team produce.
One more thing: 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. Every piece of code starts as a first draft, like wet clay getting shaped into its final form. Finally, we chisel away the imperfections when we review it with our peers. Don’t beat yourself up for first drafts that need improvement. Beat up the code instead!
Variables
Use meaningful and pronounceable variable names
Bad:
javascript const yyyymmdstr = moment().format("YYYY/MM/DD");
Good:
javascript const currentDate = moment().format("YYYY/MM/DD");
Use the same vocabulary for the same type of variable
Bad:
javascript getUserInfo(); getClientData(); getCustomerRecord();
Good:
javascript getUser();
Use searchable names
We will read more code than we will ever write. It’s important that the code we do write is readable and searchable. By not naming variables that end up being meaningful for understanding our program, we hurt our readers. Make your names searchable.
Tools like buddy.js (https://github.com/danielstjules/buddy.js) and ESLint (https://github.com/eslint/eslint/blob/660e0918933e6e7fede26bc675a0763a6b357c94/docs/rules/no-magic-numbers.md) can help identify unnamed constants.
Bad:
javascript // What the heck is 86400000 for? setTimeout(blastOff, 86400000);
Good:
javascript // Declare them as capitalized named constants. const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000; //86400000; setTimeout(blastOff, MILLISECONDS_PER_DAY);
Use explanatory variables
Bad:
javascript const address = "One Infinite Loop, Cupertino 95014"; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; saveCityZipCode( address.match(cityZipCodeRegex)[1], address.match(cityZipCodeRegex)[2] );
Good:
javascript const address = "One Infinite Loop, Cupertino 95014"; const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/; const [_, city, zipCode] = address.match(cityZipCodeRegex) || []; saveCityZipCode(city, zipCode);
Avoid Mental Mapping
Explicit is better than implicit.
Bad:
javascript const locations = ["Austin", "New York", "San Francisco"]; locations.forEach(l => { doStuff(); doSomeOtherStuff(); // ... // ... // ... // Wait, what is `l` for again? dispatch(l); });
Good:
javascript const locations = ["Austin", "New York", "San Francisco"]; locations.forEach(location => { doStuff(); doSomeOtherStuff(); // ... // ... // ... dispatch(location); });
Don’t add unneeded context
If your class/object name tells you something, don’t repeat that in your variable name.
Bad:
``javascript
const Car = {
carMake: “Honda”,
carModel: “Accord”,
carColor: “Blue”
};
function paintCar(car, color) {
car.carColor = color;
}
``
Good:
``javascript
const Car = {
make: “Honda”,
model: “Accord”,
color: “Blue”
};
function paintCar(car, color) {
car.color = color;
}
``
Use default parameters instead of short circuiting or conditionals
Default parameters are often cleaner than short circuiting. Be aware that if you use them, your function will only provide default values for undefined arguments. Other “falsy” values such as '', "", false, null, 0, and NaN, will not be replaced by a default value.
Bad:
javascript function createMicrobrewery(name) { const breweryName = name || "Hipster Brew Co."; // ... }
Good:
javascript function createMicrobrewery(name = "Hipster Brew Co.") { // ... }
Functions
Function arguments (2 or fewer ideally)
Limiting the amount of function parameters is incredibly important because it makes testing your function easier. Having more than three leads to a combinatorial explosion where you have to test tons of different cases with each separate argument.
One or two arguments is the ideal case, and three should be avoided if possible. Anything more than that should be consolidated. Usually, if you have more than two arguments then your function is trying to do too much. In cases where it’s not, most of the time a higher-level object will suffice as an argument.
Since JavaScript allows you to make objects on the fly, without a lot of class boilerplate, you can use an object if you are finding yourself needing a lot of arguments.
To make it obvious what properties the function expects, you can use the ES2015/ES6 destructuring syntax. This has a few advantages:
- When someone looks at the function signature, it’s immediately clear what properties are being used.
- It can be used to simulate named parameters.
- Destructuring also clones the specified primitive values of the argument object passed into the function. This can help prevent side effects. Note: objects and arrays that are destructured from the argument object are NOT cloned.
- Linters can warn you about unused properties, which would be impossible without destructuring.
Bad:
``javascript
function createMenu(title, body, buttonText, cancellable) {
// …
}
createMenu(“Foo”, “Bar”, “Baz”, true);
``
Good:
``javascript
function createMenu({ title, body, buttonText, cancellable }) {
// …
}
createMenu({
title: “Foo”,
body: “Bar”,
buttonText: “Baz”,
cancellable: true
});
``
Functions should do one thing
This is by far the most important rule in software engineering. When functions do more than one thing, they are harder to compose, test, and reason about. When you can isolate a function to just one action, it can be refactored easily and your code will read much cleaner. If you take nothing else away from this guide other than this, you’ll be ahead of many developers.
Bad:
javascript function emailClients(clients) { clients.forEach(client => { const clientRecord = database.lookup(client); if (clientRecord.isActive()) { email(client); } }); }
Good:
``javascript
function emailActiveClients(clients) {
clients
.filter(isActiveClient)
.forEach(email);
}
function isActiveClient(client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
``
Function names should say what they do
Bad:
``javascript
function addToDate(date, month) {
// …
}
const date = new Date();
// It’s hard to tell from the function name what is added
addToDate(date, 1);
``
Good:
``javascript
function addMonthToDate(month, date) {
// …
}
const date = new Date();
addMonthToDate(1, date);
``
Functions should only be one level of abstraction
When you have more than one level of abstraction your function is usually doing too much. Splitting up functions leads to reusability and easier testing.
Bad:
``javascript
function parseBetterJSAlternative(code) {
const REGEXES = [
// …
];
const statements = code.split(“ “);
const tokens = [];
REGEXES.forEach(REGEX => {
statements.forEach(statement => {
// …
});
});
const ast = [];
tokens.forEach(token => {
// lex…
});
ast.forEach(node => {
// parse…
});
}
``
Good:
``javascript
function parseBetterJSAlternative(code) {
const tokens = tokenize(code);
const syntaxTree = parse(tokens);
syntaxTree.forEach(node => {
// parse…
});
}
function tokenize(code) {
const REGEXES = [
// …
];
const statements = code.split(“ “);
const tokens = [];
REGEXES.forEach(REGEX => {
statements.forEach(statement => {
tokens.push(/* … */);
});
});
return tokens;
}
function parse(tokens) {
const syntaxTree = [];
tokens.forEach(token => {
syntaxTree.push(/* … */);
});
return syntaxTree;
}
``
Remove duplicate code
Do your absolute best to avoid duplicate code. Duplicate code is bad because it means that there’s more than one place to alter something if you need to change some logic.
Imagine if you run a restaurant and you keep track of your inventory: all your tomatoes, onions, garlic, spices, etc. If you have multiple lists that you keep this on, then all have to be updated when you serve a dish with tomatoes in them. If you only have one list, there’s only one place to update!
Oftentimes you have duplicate code because you have two or more slightly different things, that share a lot in common, but their differences force you to have two or more separate functions that do much of the same things. Removing duplicate code means creating an abstraction that can handle this set of different things with just one function/module/class.
Getting the abstraction right is critical, that’s why you should follow the SOLID principles laid out in the Classes section. Bad abstractions can be worse than duplicate code, so be careful! Having said this, if you can make a good abstraction, do it! Don’t repeat yourself, otherwise you’ll find yourself updating multiple places anytime you want to change one thing.
Bad:
``javascript
function showDeveloperList(developers) {
developers.forEach(developer => {
const expectedSalary = developer.calculateExpectedSalary();
const experience = developer.getExperience();
const githubLink = developer.getGithubLink();
const data = {
expectedSalary,
experience,
githubLink
};
render(data);
});
}
function showManagerList(managers) {
managers.forEach(manager => {
const expectedSalary = manager.calculateExpectedSalary();
const experience = manager.getExperience();
const portfolio = manager.getMBAProjects();
const data = {
expectedSalary,
experience,
portfolio
};
render(data);
});
}
``
Good:
``javascript
function showEmployeeList(employees) {
employees.forEach(employee => {
const expectedSalary = employee.calculateExpectedSalary();
const experience = employee.getExperience();
const data = {
expectedSalary,
experience
};
switch (employee.type) {
case "manager":
data.portfolio = employee.getMBAProjects();
break;
case "developer":
data.githubLink = employee.getGithubLink();
break;
}
render(data);
});
}
``
Set default objects with Object.assign
Bad:
``javascript
const menuConfig = {
title: null,
body: “Bar”,
buttonText: null,
cancellable: true
};
function createMenu(config) {
config.title = config.title || “Foo”;
config.body = config.body || “Bar”;
config.buttonText = config.buttonText || “Baz”;
config.cancellable = config.cancellable !== undefined ? config.cancellable : true;
}
createMenu(menuConfig);
``
Good:
``javascript
const menuConfig = {
title: “Order”,
// User did not include ‘body’ key
buttonText: “Send”,
cancellable: true
};
function createMenu(config) {
let finalConfig = Object.assign(
{
title: “Foo”,
body: “Bar”,
buttonText: “Baz”,
cancellable: true
},
config
);
return finalConfig
// config now equals: {title: “Order”, body: “Bar”, buttonText: “Send”, cancellable: true}
// …
}
createMenu(menuConfig);
``
Don’t use flags as function parameters
Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean.
Bad:
javascript function createFile(name, temp) { if (temp) { fs.create(`./temp/${name}`); } else { fs.create(name); } }
Good:
``javascript
function createFile(name) {
fs.create(name);
}
function createTempFile(name) {
createFile(./temp/${name});
}
``
Avoid Side Effects (part 1)
A function produces a side effect if it does anything other than take a value in and return another value or values. A side effect could be writing to a file, modifying some global variable, or accidentally wiring all your money to a stranger.
Now, you do need to have side effects in a program on occasion. Like the previous example, you might need to write to a file. What you want to do is to centralize where you are doing this. Don’t have several functions and classes that write to a particular file. Have one service that does it. One and only one.
The main point is to avoid common pitfalls like sharing state between objects without any structure, using mutable data types that can be written to by anything, and not centralizing where your side effects occur. If you can do this, you will be happier than the vast majority of other programmers.
Bad:
``javascript
// Global variable referenced by following function.
// If we had another function that used this name, now it’d be an array and it could break it.
let name = “Ryan McDermott”;
function splitIntoFirstAndLastName() {
name = name.split(“ “);
}
splitIntoFirstAndLastName();
console.log(name); // [‘Ryan’, ‘McDermott’];
``
Good:
``javascript
function splitIntoFirstAndLastName(name) {
return name.split(“ “);
}
const name = “Ryan McDermott”;
const newName = splitIntoFirstAndLastName(name);
console.log(name); // ‘Ryan McDermott’;
console.log(newName); // [‘Ryan’, ‘McDermott’];
``
Avoid Side Effects (part 2)
In JavaScript, some values are unchangeable (immutable) and some are changeable (mutable). Objects and arrays are two kinds of mutable values so it’s important to handle them carefully when they’re passed as parameters to a function. A JavaScript function can change an object’s properties or alter the contents of an array which could easily cause bugs elsewhere.
Suppose there’s a function that accepts an array parameter representing a shopping cart. If the function makes a change in that shopping cart array - by adding an item to purchase, for example - then any other function that uses that same cart array will be affected by this addition. That may be great, however it could also be bad. Let’s imagine a bad situation:
The user clicks the “Purchase” button which calls a purchase function that spawns a network request and sends the cart array to the server. Because of a bad network connection, the purchase function has to keep retrying the request. Now, what if in the meantime
Similar Articles
ryanmcdermott/clean-code-javascript
A guide to writing clean, readable, and maintainable JavaScript code based on Robert C. Martin's Clean Code principles, covering variables, functions, classes, testing, and more.
@freeCodeCamp: Writing clean code helps you build scalable, maintainable software applications. And in this handbook, @shahancd explai…
A handbook explaining clean code principles and patterns for building scalable software, with JavaScript examples.
@VincentLogic: The hardest part of reading someone else’s code isn’t that you can’t understand a single line. It’s that you have no idea where you are. When you open a unfamiliar repo, files, calls, services, and dependencies are all mixed together, and AI explanations often just read through the files verbatim. The Understand-Anything approach is quite aggressive: it directly turns the codebase into a…
Introduces the Understand-Anything tool, which transforms a codebase into a clickable, searchable, call-tracking knowledge graph to help developers understand unfamiliar projects.
@QingQ77: A verifiable reference of programming patterns extracted from production code, each with interactive visualization, line-accurate source links, and multi-language implementations. https://github.com/Totoro-jam/battle-tested-patterns… Battle-Teste…
A reference library of 46 programming patterns extracted from real production code (e.g., React, Linux kernel, Go, Redis, etc.), each with interactive visualization, line-accurate source links, implementations in TypeScript/Python/Go/Rust, and runnable exercises. The site is bilingual (Chinese and English) and supports search and category browsing.
@IndieDevHailey: The nightmare era of developers groveling through source code is over! This project turns any codebase into an interactive knowledge graph you can click and query. Understand Anything, 21.8k stars, #1 on GitHub Trending. Click any function and instantly know: what this module does...
Understand Anything is an open-source tool that transforms any codebase into an interactive, queryable knowledge graph, helping developers quickly understand code structure, dependencies, and call relationships. It supports Claude Code, Cursor, and VS Code, and can be installed with a single command.