{"id":20290667,"url":"https://github.com/gocardless/javascript-style-guide","last_synced_at":"2025-03-04T04:43:21.931Z","repository":{"id":27677679,"uuid":"31163879","full_name":"gocardless/javascript-style-guide","owner":"gocardless","description":"The GoCardless JavaScript styleguide","archived":false,"fork":false,"pushed_at":"2015-06-19T17:37:15.000Z","size":246,"stargazers_count":18,"open_issues_count":0,"forks_count":3,"subscribers_count":101,"default_branch":"master","last_synced_at":"2025-01-14T08:52:53.782Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gocardless.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-02-22T13:31:49.000Z","updated_at":"2018-12-21T11:28:07.000Z","dependencies_parsed_at":"2022-08-21T03:10:30.920Z","dependency_job_id":null,"html_url":"https://github.com/gocardless/javascript-style-guide","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/gocardless%2Fjavascript-style-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fjavascript-style-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fjavascript-style-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gocardless%2Fjavascript-style-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gocardless","download_url":"https://codeload.github.com/gocardless/javascript-style-guide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241787488,"owners_count":20020099,"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":[],"created_at":"2024-11-14T15:08:39.585Z","updated_at":"2025-03-04T04:43:21.909Z","avatar_url":"https://github.com/gocardless.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# The GoCardless JavaScript style guide\n\nThis style guide is for general JavaScript conventions and styles that we follow across all our JavaScript. For Angular specific conventions, refer to the [Angular Style Guide](https://github.com/gocardless/angularjs-style-guide).\n\n## Browser Support\n\nThe projects we work on support the following browsers:\n\n- Chrome 39+\n- Safari 7+\n- Firefox 33+\n- IE10+\n\nBe aware of this when reading the guide - some of the JS shown here may not work in older versions.\n\n## Strings\n\n#### Use single quotes for all strings.\n\n_Why_: in JavaScript there is no difference between single and double quotes. Rather than have a mix throughout a code base, pick one and stick to it.\n\n#### Keep line length at around 80 characters.\n\n_Why_: this encourages developers to write lines that do less, and extract variables and functions where a line is longer than required.\n\nWe don’t have an 80 char hard limit because sometimes it’s more readable to let a line be 85 characters rather than break it up, but in general you should aim for 80 characters or less.\n\n#### Use `String.prototype.includes` to check for substrings\n\n_Why_: it's intent is much clearer than `indexOf`, and it is included as part of the ES6 specification\n\nIn a JS project that is transpiled using Traceur, Babel or similar, use the new `String.prototype.includes` method:\n\n```js\n// bad\nif (str.indexOf('foo') \u003e -1) {\n}\n\n// good\nif (str.includes('foo')) {\n}\n```\n\n## Async\n\n#### Ensure that each promise chain has at least one `catch`.\n\n_Why_: ensures that errors are always handled.\n\n```js\n// bad\ndoSomething().then(...)\n\n// good\ndoSomething().then(...).catch(...)\n```\n\n#### Prefer multiple `then` callbacks over one `then` callback which performs multiple operations.\n\n_Why_: this keeps promise chains easier to read, and keeps callback functions smaller.\n\n```js\n// bad\ndoSomething().then(function processSomething(data) {\n  // do something\n  // do something else\n  // do something else else\n});\n\n// good\ndoSomething().then(function processSomething(data) {\n // do something\n return data;\n}).then(function somethingElse(data) {\n // do something else\n return data;\n}).then(function anotherThing(data) {\n  // and so on\n});\n```\n\n## Functions\n\n#### Name functions that are given as a callback.\n\n_Why_: the function name replaces “anonymous function” in stack traces, which makes debugging easier. Naming functions also helps code be self documenting.\n\n```js\n// bad\ncreateUser().then(function() {\n});\n\n// good\ncreateUser().then(function userCreatedSuccesfully() {\n});\n```\n\n#### Prefer function declarations to function expressions.\n\n_Why_: function declarations are easier to pick out in larger code files.\n\n#### Ensure that the function is defined above its first usage.\n\n_Why_: hoisting enables JavaScript files to have their most important content at the top. However, we’re used to reading top to bottom and left to right, so this can be confusing.\n\n```js\n// this will work but is unclear\nthing();\n\n// more code\n\nfunction thing() {\n}\n```\n\n#### Prefer guard clauses to nested function bodies.\n\n_Why_: indenting an entire function body makes it harder to read. Providing a base case early keeps the code cleaner.\n\n```js\n// bad\nfunction doSomethingOnOddNumbers(x) {\n  if (x % 2 === 1) {\n    // do stuff\n  }\n};\n\n// good\nfunction doSomethingOnOddNumbers(x) {\n  if (x % 2 === 0) return;\n  // do stuff\n}\n```\n\n#### Leave a space after the closing `)` and before the opening `{`, but never before the opening `(`.\n\n```js\n// bad\nfunction doSomething () {\n}\n\n// bad\nfunction doSomething(){\n}\n\n// good\nfunction doSomething() {\n}\n```\n\n#### When a function has arguments that make the line longer than ~80 characters, split it into one parameter per line.\n\n_Why_: keeps lines below 80 characters, after which readability declines.\n\n```js\n// good\nfunction SomeController(foo, bar, baz) {\n}\n\n// bad\nfunction SomeController(foo, bar, baz, abc, def, ghi, jkl, mno, pqr, stu, vwx, yz) {\n}\n\n// good\nfunction SomeController(\n  foo,\n  bar,\n  baz,\n  abc,\n  def,\n  ghi,\n  jkl,\n  mno,\n  pqr,\n  stu,\n  vwx,\n  yz\n) {\n}\n```\n\n## Objects\n\n#### When an object literal has more than one property, split it into one property per line, leaving a trailing comma.\n\n_Why_: keeps lines below 80 characters, after which readability declines. Trailing commas make it easier to reorder existing lines without editing them, and makes new lines clearer in the diff.\n\n```js\n// good\n{ foo: 2 }\n\n// bad\n{ foo: 2, bar: 3 }\n\n// bad\n{\n  foo: 2,\n  bar: 3\n}\n\n// good\n{\n  foo: 2,\n  bar: 3,\n}\n```\n\n## Arrays\n\n#### When using array methods such as `forEach`, `map`, etc, pass function names in if possible.\n\n_Why_: avoids unnecessary function wrapping.\n\n```js\nfunction isOdd(x) {\n  return x % 2 === 1;\n}\n\n// bad\n[1, 2, 3].filter(function(x) {\n  return isOdd(x);\n});\n\n// good\n[1, 2, 3].filter(isOdd);\n```\n\n#### Never mutate the array or objects within an array in a `forEach`, `map` or similar.\n\n_Why_: JavaScript's object mutation can be very implicit and easy to miss. It's much better to have slightly more verbose but much clearer code.\n\n```js\n// bad -  payments array has been mutated, not obvious\npayments.forEach(function(payment) {\n  payment.id = 'ABC123'\n});\n\n// bad - the original payments array is mutated\npayments.map(function(payment) {\n  payment.id = 'ABC123';\n});\n\n// good - don't mutate the array, but create and modify a new one:\npayments = _.cloneDeep(payments).map(function(payment) {\n  payment.id = 'ABC123';\n  return payment;\n});\n```\n\nIn practice we may not use `_.cloneDeep` - alter the above code as appropriate based on the libraries available.\n\n#### Leave a trailing comma in array literals that span multiple lines.\n\n_Why_: it’s easier to reorder items and the diff is cleaner when a new item is added.\n\n```js\n// bad\n[\n  'jack',\n  'max'\n]\n\n// good\n[\n  'jack',\n  'max',\n]\n```\n\n#### Prefer LoDash's `_.includes` method to test for Array inclusion\n\n_Why_: its intent is much clearer than using `indexOf`.\n\nIf LoDash is available and included in the project, `_.includes` is much easier to read and cleaner to use than `indexOf`.\n\n```js\n// bad\nif (names.indexOf('jack') \u003e -1) {\n}\n\n// good\nif (_.includes(names, 'jack')) {\n}\n```\n\n## Variables\n\n#### Don’t declare a variable within a block.\n\n_Why_: it’s unclear where the variable is defined.\n\n```js\n// bad\nif (x) {\n  var y = 2;\n} else {\n  var y = 3;\n}\n\n// good\nvar y;\nif (x) {\n  y = 2;\n} else {\n  y = 3;\n}\n```\n\n#### Use ternary operators for very succinct conditionals which change the initial value of a variable.\n\n_Why_: ternary operators are concise when used with small conditionals.\n\nUse an `if` statement if any branch of the condition is complex.\n\n```js\nvar y = x ? 2 : 3;\n```\n\n#### One variable per line.\n\n_Why_: adding a new variable does not mean other lines need to be edited, and existing lines can be reordered easily.\n\n```js\n// bad\nvar x, y, z;\n\n// good\nvar x;\nvar y;\nvar z;\n```\n\n## Conditionals\n\n#### Always prefer `===` and `!==` when comparing.\n\n_Why_: eliminates errors caused by JavaScript’s complex equality and coercion rules.\n\n#### Use `==` or `!=` only when checking a variable is `undefined` or `null`:\n\n_Why_: using `==` checks for both `undefined` and `null`, whereas `===` only checks for one.\n\n```js\n// bad\nif (x === undefined || x === null) {}\n\n// good\nif (x == null) {}\n```\n\n#### Prefer `hasOwnProperty` for checking if a key exists within an object.\n\n_Why_: keys that have a falsey value but are still present might result in false positives.\n\n```js\nvar thing = { count: 0 };\n\nif (!thing.count) // will evaluate to true, but we don’t want it to\n\nif (!thing.hasOwnProperty('count')) // will evaluate to false, which is what we want\n```\n\n#### Wrap a conditional in braces if it’s \u003e 1 line long.\n\n_Why_: conditionals without braces over multiple lines can easily be misconstrued.\n\n```js\n// bad\nif (something) { x = true }\n\n// good\nif (something) x = true;\n\n// bad\nif (something)\n  doSomethingElse();\n  doAnotherThing(); // \u003c- not part of the conditional!\n\n// good\nif (something) {\n  doSomethingElse();\n  doAnotherThing(); \n}\n```\n\n#### Leave a space between `if` and the opening `(`.\n\n_Why_: makes it easier to pick out `if`s in a large code file.\n\n```js\n// bad\nif(foo)\n\n// good\nif (foo)\n```\n\n# ES6 Specific Conventions\n\n## `=\u003e` functions\n\n#### Prefer “fat arrow” functions when the function is small enough to fit on one line, or when the function’s job is to return the result of an expression.\n\n_Why_: they are more concise and readable, and implicitly return when the body is an expression.\n\n```js\n// bad\n[1, 2, 3].map(function(x) { return x * 2 });\n\n// good\n[1, 2, 3].map((x) =\u003e x * 2);\n```\n\n#### Use function expressions when the function body is a block.\n\n_Why_: an arrow function that spans multiple lines requires a block and an explicit return statement. Therefore, using an arrow function offers a minimally shorter expression at the expense of losing the function name in a stack trace.\n\n```js\n// bad\n[1, 2, 3].map((x) =\u003e {\n  // do lots of things\n  // do more things\n  return x * 2;\n});\n\n// good\n[1, 2, 3].map(function(x) {\n  // do lots of things\n  // do more things\n  return x * 2;\n});\n```\n\n#### Always wrap arrow function arguments in brackets.\n\n_Why_: they aren’t needed when the function only takes one parameter, but it’s clearer to always include them.\n\n```js\n// bad\n[1, 2, 3].map(x =\u003e x * 2);\n\n// good\n[1, 2, 3].map((x) =\u003e x * 2);\n```\n\n#### In tests prefer arrow functions to function expressions.\n\nUnfortunately our e2e tests run in Node.js, so don't support arrow functions (yet). When they do, we'll prefer to use them there too.\n\n_Why_: keeps tests cleaner and easier to pick out the actual assertions.\n\n```js\n// bad\nit('does a thing', function() {\n});\n\n// good\nit('does a thing', () =\u003e {\n});\n```\n\n## Strings\n\n#### Prefer ES6 template strings to concatenation.\n\n_Why_: easier to read.\n\n```js\n// bad\nvar fullName = firstName + ' ' + lastName;\n\n// good\nvar fullName = `${firstName} ${lastName}`;\n```\n\n## Function Arguments\n\n#### Use the spread operator over converting the `arguments` object.\n\n```js\n// bad\nfunction foo() {\n  var args = [].slice.call(arguments);\n}\n\n// good\nfunction foo(...args) {\n}\n```\n\n## ES6 Modules\n\n#### Use relative paths when the target is a descendant of the path declaration, else prefer absolute paths.\n\n_Why_: easier to see a file’s position, and prevents many parent directory operators.\n\n```js\n// bad\nimport {foo} from '../../service/foo';\n\n// good\nimport {foo} from 'client/app/service/foo';\n\n// good\nimport {foo} from './foo';\n```\n\n## Constants\n\n## `let` and `const`\n\nUse them! Prefer `const` by default.\n\n```js\nconst apiUrl = 'http://example.com';\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgocardless%2Fjavascript-style-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgocardless%2Fjavascript-style-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgocardless%2Fjavascript-style-guide/lists"}