JavaScript Code Review: What Really Annoys Seniors
\n\nEvery developer has at least once received a pull request with a comment like "it's better to rewrite this" or "this is an antipattern." If you are a Junior or Middle developer, such comments often seem like nitpicking. But behind every senior's word lies real experience: a production outage, a bug at three in the morning, or a performance drop.
\n\nWe've gathered the most frequent comments that seniors leave on JavaScript code reviews. This is not just a list of "this is bad, this is good." It's an analysis of why a particular approach is considered bad and what can replace it.
\n\nThe article will be useful for everyone who writes in JS: from beginners to experienced developers who want to improve code quality.
\n\n1. Mutating Input Data: The Cardinal Sin
\n\nOne of the most common mistakes in reviews is modifying objects or arrays passed into a function. Seniors hate this because mutation breaks immutability and leads to hard-to-find bugs.
\n\nBad Example
\nfunction addUser(users, newUser) {\n users.push(newUser); // mutates the original array!\n return users;\n}\n\nconst allUsers = [{ id: 1 }, { id: 2 }];\nconst updated = addUser(allUsers, { id: 3 });\nconsole.log(allUsers.length); // 3 — the original array is modified!\n\nHow It Should Be Done
\nfunction addUser(users, newUser) {\n return [...users, newUser]; // returns a new array\n}\n\nconst allUsers = [{ id: 1 }, { id: 2 }];\nconst updated = addUser(allUsers, { id: 3 });\nconsole.log(allUsers.length); // 2 — the original is untouched\n\nWhy this matters: when you mutate input data, any other code referencing the same object gets unexpected changes. This is a direct path to bugs in asynchronous operations, Redux stores, and React states.
\n\n2. Magic Numbers and Strings: Readability Killers
\n\nSeniors often write in comments: "What is the number 86400?" or "Where did this string come from?" Magic values make code unclear and complicate its maintenance.
\n\nBad Example
\nfunction calculateDiscount(price) {\n if (price > 1000) {\n return price * 0.9;\n }\n return price * 0.95;\n}\n\nHow It Should Be Done
\nconst DISCOUNT_THRESHOLD = 1000;\nconst HIGH_DISCOUNT_RATE = 0.9;\nconst STANDARD_DISCOUNT_RATE = 0.95;\n\nfunction calculateDiscount(price) {\n if (price > DISCOUNT_THRESHOLD) {\n return price * HIGH_DISCOUNT_RATE;\n }\n return price * STANDARD_DISCOUNT_RATE;\n}\n\nTip: extract all constants into named variables or a separate configuration file. This simplifies JavaScript code review and makes it self-documenting.
\n\n3. Errors with async/await and Promises
\n\nAsynchronous code is an area where even mid-level developers stumble. Seniors in reviews focus on two main points: incorrect error handling and mixing syntaxes.
\n\nBad Example (promise without catch)
\nasync function loadData() {\n const response = await fetch('/api/data');\n const data = await response.json();\n return data;\n}\n\nHow It Should Be Done
\nasync function loadData() {\n try {\n const response = await fetch('/api/data');\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const data = await response.json();\n return data;\n } catch (error) {\n console.error('Data loading error:', error);\n throw error; // re-throw if needed\n }\n}\n\nImportant: never ignore errors. An empty catch is evil. If an error occurs, you should at least log it, and ideally handle it correctly.
Another common mistake is a forgotten await
\nfunction processUsers(users) {\n users.forEach(async (user) => {\n await updateUser(user);