{"id":13403945,"url":"https://github.com/ryanmcdermott/code-review-tips","last_synced_at":"2025-10-25T13:02:53.862Z","repository":{"id":40685990,"uuid":"100813255","full_name":"ryanmcdermott/code-review-tips","owner":"ryanmcdermott","description":":microscope: Common problems to look for in a code review","archived":false,"fork":false,"pushed_at":"2023-02-27T07:26:42.000Z","size":45,"stargazers_count":1503,"open_issues_count":1,"forks_count":179,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-04-08T11:10:01.284Z","etag":null,"topics":["code-review","javascript","readability","review-process","review-tips","reviews"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ryanmcdermott.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-08-19T18:19:14.000Z","updated_at":"2025-04-06T05:35:38.000Z","dependencies_parsed_at":"2024-02-09T14:00:22.908Z","dependency_job_id":null,"html_url":"https://github.com/ryanmcdermott/code-review-tips","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanmcdermott%2Fcode-review-tips","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanmcdermott%2Fcode-review-tips/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanmcdermott%2Fcode-review-tips/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryanmcdermott%2Fcode-review-tips/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ryanmcdermott","download_url":"https://codeload.github.com/ryanmcdermott/code-review-tips/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254442854,"owners_count":22071878,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code-review","javascript","readability","review-process","review-tips","reviews"],"created_at":"2024-07-30T19:01:36.670Z","updated_at":"2025-10-25T13:02:48.822Z","avatar_url":"https://github.com/ryanmcdermott.png","language":"JavaScript","readme":"# code-review-tips\n\n## Table of Contents\n\n1. [Introduction](#introduction)\n2. [Why Review Code?](#why-review-code)\n3. [Basics](#basics)\n4. [Readability](#readability)\n5. [Side Effects](#side-effects)\n6. [Limits](#limits)\n7. [Security](#security)\n8. [Performance](#performance)\n9. [Testing](#testing)\n10. [Miscellaneous](#miscellaneous)\n\n## Introduction\n\nCode reviews can inspire dread in both reviewer and reviewee. Having your\ncode analyzed can feel invasive and uncomfortable. Even worse, reviewing other \npeople's code can feel like a painful and ambiguous exercise, searching \nfor problems and not even knowing where to begin.\n\nThis project aims to provide some solid tips for how to review the code that\nyou and your team write. All examples are written in JavaScript, but the advice\nshould be applicable to any project of any language. This is by no means an\nexhaustive list, but hopefully this will help you catch as many bugs as\npossible long before users ever see your feature.\n\n## Why Review Code?\n\nCode reviews are a necessary part of the software engineering process because\nyou alone can't catch every problem in a piece of code you\nwrite. That's ok though! Even the best basketball players in the world miss\nshots.\n\nHaving others review our work ensures that we deliver the best product to users\nand with the least amount of errors. Make sure your team implements a code\nreview process for new code that is introduced into your codebase. Find a\nprocess that works for you and your team. There's no one size fits all. The\nimportant point is to do code reviews as regularly as possible.\n\n## Basics\n\n### Code reviews should be as automated as possible\n\nAvoid discussing details that can be handled by a static analysis tool. Don't\nargue about nuances such as code formatting and whether to use `let` or `var`.\nHaving a formatter and linter can save your team a lot of time from reviews\nthat your computer can do for you.\n\n### Code reviews should avoid API discussion\n\nThese discussions should happen before the code is even written. Don't try to\nargue about the floor plan once you've poured the concrete\nfoundation.\n\n### Code reviews should be kind\n\nIt's scary to have your code reviewed and it can bring about feelings of\ninsecurity in even the most experienced developer. Be positive in your language\nand keep your teammates comfortable and secure in their work!\n\n## Readability\n\n### Typos should be corrected\n\nAvoid nitpicking as much as you can and save it for your linter, compiler, and\nformatter. When you can't, such as in the case of typos, leave a kind comment\nsuggesting a fix. It's the little things that make a big difference sometimes!\n\n### Variable and function names should be clear\n\nNaming is one of the hardest problems in computer science. We've all given names\nto variables, functions, and files that are confusing. Help your teammate out\nby suggesting a clearer name, if the one you're reading doesn't make sense.\n\n```javascript\n// This function could be better named as namesToUpperCase\nfunction u(names) {\n  // ...\n}\n```\n\n### Functions should be short\n\nFunctions should do one thing! Long functions usually mean that they are doing\ntoo much. Tell your teammate to split out the function into multiple different\nones.\n\n```javascript\n// This is both emailing clients and deciding which are active. Should be\n// 2 different functions.\nfunction emailClients(clients) {\n  clients.forEach(client =\u003e {\n    const clientRecord = database.lookup(client);\n    if (clientRecord.isActive()) {\n      email(client);\n    }\n  });\n}\n```\n\n### Files should be cohesive, and ideally short\n\nJust like functions, a file should be about one thing. A file represents a\nmodule and a module should do one thing for your codebase.\n\nFor example, if your module is called `fake-name-generator` it should just be\nresponsible for creating fake names like \"Keyser Söze\". If the\n`fake-name-generator` also includes a bunch of utility functions for querying a\ndatabase of names, that should be in a separate module.\n\nThere's no rule for how long a file should be, but if it's long like below **and**\nincludes functions that don't relate to one another, then it should probably\nbe split apart.\n\n```javascript\n// Line 1\nimport _ from 'lodash';\nfunction generateFakeNames() {\n  // ..\n}\n\n// Line 1128\nfunction queryRemoteDatabase() {\n  // ...\n}\n```\n\n### Exported functions should be documented\n\nIf your function is intended to be used by other libraries, it helps to add\ndocumentation so users of it know what it does.\n\n```javascript\n// This needs documentation. What is this function for? How is it used?\nexport function networkMonitor(graph, duration, failureCallback) {\n  // ...\n}\n```\n\n### Complex code should be commented\n\nIf you have named things well and the logic is still confusing, then it's time\nfor a comment.\n\n```javascript\nfunction leftPad(str, len, ch) {\n  str = str + '';\n  len = len - str.length;\n\n  while (true) {\n    // This needs a comment, why a bitwise and here?\n    if (len \u0026 1) pad += ch;\n    // This needs a comment, why a bit shift here?\n    len \u003e\u003e= 1;\n    if (len) ch += ch;\n    else break;\n  }\n\n  return pad + str;\n}\n```\n\n## Side Effects\n\n### Functions should be as pure as possible\n\n```javascript\n// Global variable is referenced by the following function.\n// If we had another function that used this name, now it'd be an array and it\n// could break it. Instead it's better to pass in a name parameter\nlet name = 'Ryan McDermott';\n\nfunction splitIntoFirstAndLastName() {\n  name = name.split(' ');\n}\n\nsplitIntoFirstAndLastName();\n```\n\n### I/O functions should have failure cases handled\n\nAny function that does I/O should handle when something goes wrong\n\n```javascript\nfunction getIngredientsFromFile() {\n  const onFulfilled = buffer =\u003e {\n    let lines = buffer.split('\\n');\n    return lines.map(line =\u003e \u003cIngredient ingredient={line} /\u003e);\n  };\n\n  // What about when this is rejected because of an error? What do we return?\n  return readFile('./ingredients.txt').then(onFulfilled);\n}\n```\n\n## Limits\n\n### Null cases should be handled\n\nIf you have a list component for example, all is well and good if you display a\nnice beautiful table that shows all its data. Your users love it and you get a\npromotion! But what happens when no data comes back? What do you show in the\nnull case? Your code should be resilient to every case that can occur. If\nthere's something bad that can happen in your code, eventually it will happen.\n\n```javascript\nclass InventoryList {\n  constructor(data) {\n    this.data = data;\n  }\n\n  render() {\n    return (\n      \u003ctable\u003e\n        \u003ccaption\u003eInventory\u003c/caption\u003e\n        \u003cthead\u003e\n           \u003ctr\u003e\n            \u003cth scope=\"col\"\u003eID\u003c/th\u003e\n            \u003cth scope=\"col\"\u003eProduct\u003c/th\u003e\n           \u003c/tr\u003e\n        \u003c/thead\u003e\n        \u003ctbody\u003e\n          // We should show something for the null case here if there's // nothing\n          in the data inventory\n          {Object.keys(this.data.inventory).map(itemId =\u003e (\n            \u003ctr key={i}\u003e\n              \u003ctd\u003e{itemId}\u003c/td\u003e\n\n              \u003ctd\u003e{this.state.inventory[itemId].product}\u003c/td\u003e\n            \u003c/tr\u003e\n          ))}\n        \u003c/tbody\u003e\n      \u003c/table\u003e\n    );\n  }\n}\n```\n\n### Large cases should be handled\n\nIn the list above, what would happen if 10,000 items came back from the\ninventory? In that case, you need some form of pagination or infinite scroll.\nBe sure to always assess the potential edge cases in terms of volume, especially\nwhen it comes to UI programming.\n\n### Singular cases should be handled\n\n```javascript\nclass MoneyDislay {\n  constructor(amount) {\n    this.amount = amount;\n  }\n\n  render() {\n    // What happens if the user has 1 dollar? You can't say plural \"dollars\"\n    return (\n      \u003cdiv className=\"fancy-class\"\u003e\n        You have {this.amount} dollars in your account\n      \u003c/div\u003e\n    );\n  }\n}\n```\n\n### User input should be limited\n\nUsers can potentially input an unlimited amount of data to send to you. It's\nimportant to set limits if a function takes any kind of user data in.\n\n```javascript\nrouter.route('/message').post((req, res) =\u003e {\n  const message = req.body.content;\n\n  // What happens if the message is many megabytes of data? Do we want to store\n  // that in the database? We should set limits on the size.\n  db.save(message);\n});\n```\n\n### Functions should handle unexpected user input\n\nUsers will always surprise you with the data they give you. Don't expect that\nyou will always get the right type of data or even any data in a request from a\nuser. [And don't rely on client-side validation alone](https://twitter.com/ryconoclast/status/885523459748487169)\n\n```javascript\nrouter.route('/transfer-money').post((req, res) =\u003e {\n  const amount = req.body.amount;\n  const from = user.id;\n  const to = req.body.to;\n\n  // What happens if we got a string instead of a number as our amount? This\n  // function would fail\n  transferMoney(from, to, amount);\n});\n```\n\n## Security\n\nData security is the most important aspect of your application. If users can't\ntrust you with their data, then you won't have a business. There are numerous\ndifferent types of security exploits that can plague an app, depending on the\nparticular language and runtime environment. Below is a very small and\nincomplete list of common security problems. Don't rely on this alone! Automate\nas much security review as you can on every commit, and perform routine security\naudits.\n\n### XSS should not be possible\n\nCross-site scripting (XSS) is one of the largest vectors for security attacks\non a web application. It occurs when you take user data and include it in your\npage without first properly sanitizing it. This can cause your site to execute\nsource code from remote pages.\n\n```javascript\nfunction getBadges() {\n  let badge = document.getElementsByClassName('badge');\n  let nameQueryParam = getQueryParams('name');\n\n  /**\n   * What if nameQueryParam was `\u003cscript\u003esendCookie(document.cookie)\u003c/script\u003e`?\n   * If that was the query param, a malicious user could lure a user to click a\n   * link with that as the `name` query param, and have the user unknowingly\n   * send their data to a bad actor.\n   */\n  badge.children[0].innerHTML = nameQueryParam;\n}\n```\n\n### Personally Identifiable Information (PII) should not leak\n\nYou bear an enormous weight of responsibility every time you take in user data.\nIf you leak data in URLs, in analytics tracking to third parties, or even expose\ndata to employees that shouldn't have access, you greatly hurt your users and\nyour business. Be careful with other people's lives!\n\n```javascript\nrouter.route('/bank-user-info').get((req, res) =\u003e {\n  const name = user.name;\n  const id = user.id;\n  const socialSecurityNumber = user.ssn;\n\n  // There's no reason to send a socialSecurityNumber back in a query parameter\n  // This would be exposed in the URL and potentially to any middleman on the\n  // network watching internet traffic\n  res.addToQueryParams({\n    name,\n    id,\n    socialSecurityNumber\n  });\n});\n```\n\n## Performance\n\n### Functions should use efficient algorithms and data structures\n\nThis is different for every particular case, but use your best judgment to see\nif there are any ways to improve the efficiency of a piece of code. Your users\nwill thank you for the faster speeds!\n\n```javascript\n// If mentions was a hash data structure, you wouldn't need to iterate through\n// all mentions to find a user. You could simply return the presence of the\n// user key in the mentions hash\nfunction isUserMentionedInComments(mentions, user) {\n  let mentioned = false;\n  mentions.forEach(mention =\u003e {\n    if (mention.user === user) {\n      mentioned = true;\n    }\n  });\n\n  return mentioned;\n}\n```\n\n### Important actions should be logged\n\nLogging helps give metrics about performance and insight into user behavior.\nNot every action needs to be logged, but decide with your team what makes sense\nto keep track of for data analytics. And be sure that no personally identifiable\ninformation is exposed!\n\n```javascript\nrouter.route('/request-ride').post((req, res) =\u003e {\n  const currentLocation = req.body.currentLocation;\n  const destination = req.body.destination;\n\n  requestRide(user, currentLocation, destination).then(result =\u003e {\n    // We should log before and after this block to get a metric for how long\n    // this task took, and potentially even what locations were involved in ride\n    // ...\n  });\n});\n```\n\n## Testing\n\n### New code should be tested\n\nAll new code should include a test, whether it fixes a bug, or is a new feature.\nIf it's a bug fix, it should have a test proving that the bug is fixed. And if\nit's a new feature, then every component should be unit tested and there should\nbe an integration test ensuring that the feature works with the rest of the\nsystem.\n\n### Tests should actually test all of what the function does\n\n```javascript\nfunction payEmployeeSalary(employeeId, amount, callback) {\n  db.get('EMPLOYEES', employeeId)\n    .then(user =\u003e {\n      return sendMoney(user, amount);\n    })\n    .then(res =\u003e {\n      if (callback) {\n        callback(res);\n      }\n\n      return res;\n    });\n}\n\nconst callback = res =\u003e console.log('called', res);\nconst employee = createFakeEmployee('john jacob jingleheimer schmidt');\nconst result = payEmployeeSalary(employee.id, 1000, callback);\nassert(result.status === enums.SUCCESS);\n// What about the callback? That should be tested\n```\n\n### Tests should stress edge cases and limits of a function\n\n```javascript\nfunction dateAddDays(dateTime, day) {\n  // ...\n}\n\nlet dateTime = '1/1/2017';\nlet date1 = dateAddDays(dateTime, 5);\n\nassert(date1 === '1/6/2017');\n\n// What happens if we add negative days?\n// What happens if we add fractional days: 1.2, 8.7, etc.\n// What happens if we add 1 billion days?\n```\n\n## Miscellaneous\n\n\u003e _\"Everything can be filed under miscellaneous\"_\n\n\u003e George Bernard Shaw\n\n### TODO comments should be tracked\n\nTODO comments are great for letting you and your fellow engineers know that something\nneeds to be fixed later. Sometimes you gotta ship code and wait to fix it\nlater. But eventually you'll have to clean it up! That's why you should track it\nand give a corresponding ID from your issue tracking system so you can schedule\nit and keep track of where the problem is in your codebase.\n\n### Commit messages should be clear and accurately describe new code\n\nWe've all written commit messages like \"Changed some crap\", \"damn it\",\n\"ugg one more to fix this stupid bug\". These are funny and satisfying, but not\nhelpful when you're up on a Saturday morning because you pushed code on a Friday\nnight and can't figure out what the bad code was doing when you `git blame` the\ncommit. Write commit messages that describe the code accurately, and include\na ticket number from your issue tracking system if you have one. That will make\nsearching through your commit log much easier.\n\n### The code should do what it's supposed to do\n\nThis seems obvious, but most reviewers don't have the time or take the time to\nmanually test every user-facing change. It's important to make sure the business\nlogic of every change is as per design. It's easy to forget that when you're\njust looking for problems in the code!\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryanmcdermott%2Fcode-review-tips","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fryanmcdermott%2Fcode-review-tips","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryanmcdermott%2Fcode-review-tips/lists"}