├── .editorconfig
├── .gitignore
├── .markdownlint.json
├── C#.md
├── Go
├── Git.md
├── Go.md
├── Markdown.md
├── Shell.md
├── Text.md
└── YAML.md
├── Java.md
├── JavaScript
├── .eslintrc.js
└── Javascript.md
├── PHP.md
└── README.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 4
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 | end_of_line = lf
10 | # editorconfig-tools is unable to ignore longs strings or urls
11 | max_line_length = off
12 |
13 | [Makefile]
14 | indent_style = tab
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 |
--------------------------------------------------------------------------------
/.markdownlint.json:
--------------------------------------------------------------------------------
1 | {
2 | "ul-indent": {
3 | "indent": 4
4 | },
5 | "ul-style": {
6 | "style": "dash"
7 | },
8 | "emphasis-style": {
9 | "style": "asterisk"
10 | },
11 | "no-duplicate-heading": {
12 | "siblings_only": true
13 | },
14 | "no-inline-html": {
15 | "allowed_elements": [
16 | "a",
17 | "del",
18 | "br",
19 | "details",
20 | "iframe",
21 | "summary"
22 | ]
23 | },
24 | "no-trailing-spaces": {
25 | "br_spaces": 0
26 | },
27 | "line-length": false,
28 | "no-bare-urls": false,
29 | "no-emphasis-as-heading": false,
30 | "link-fragments": false
31 | }
32 |
--------------------------------------------------------------------------------
/C#.md:
--------------------------------------------------------------------------------
1 | # AdGuard C# Guidelines
2 | *Version: 0.1*
3 |
4 | - [Introduction](#introduction)
5 | - [Access Modifiers](#access-modifiers)
6 | - [Naming Conventions](#naming-conventions)
7 | - [Comments and Documentation](#comments-and-documentation)
8 | - [General Documentation Quality](#general-documentation-quality)
9 | - [Documenting a Class](#documenting-a-class)
10 | - [Documenting a Method](#documenting-a-method)
11 | - [Inline Comments](#inline-comments)
12 | - [General Code Quality](#general-code-quality)
13 | - [Best Practices](#best-practices)
14 | - [Minimize Visibility](#minimize-visibility)
15 | - [Exceptions](#exceptions)
16 | - [Premature Optimization](#premature-optimization)
17 | - [Always Use Properties instead of Public Variables](#always-use-properties-instead-of-public-variables)
18 | - [Use IDisposable Interface](#use-idisposable-interface)
19 | - [Prefer “is” and “as” Operators While Casting](#prefer-“is”-and-“as”-operators-while-casting)
20 |
21 |
22 | ## Introduction
23 |
24 | This is a coding standard and best practices guide for C# we should use in AdGuard projects.
25 |
26 | Based on: [AdGuard Java Guidelines](https://github.com/AdguardTeam/CodeGuidelines/blob/master/Java.md)
27 |
28 | ## Access Modifiers
29 |
30 | Access level modifiers should be explicitly defined when it's possible.
31 | Examples:
32 | ```c#
33 | // Bad.
34 | class mountainBike // implicit internal
35 | {
36 | object Frame { get; set; } // implicit private
37 | object[] Wheels { get; set; } // implicit private
38 | }
39 |
40 | // Good.
41 | private class MountainBike
42 | {
43 | private object Frame { get; set; }
44 | private object[] Wheels { get; set; }
45 | }
46 | ```
47 |
48 |
49 | ## Naming Conventions
50 |
51 | **General Guidelines**
52 |
53 | * All names should be meaningful.
54 | * Use whole words and avoid acronyms and abbreviations.
55 |
56 | **Classes**
57 |
58 | * Class names should be nouns in **PascalCase**.
59 | * Multiple classes in the single file are allowed, but when it really makes sense.
60 |
61 | Examples:
62 | ```c#
63 | // Bad.
64 | private class mountainBike
65 | {
66 |
67 | }
68 |
69 | // Good.
70 | private class MountainBike
71 | {
72 |
73 | }
74 | ```
75 |
76 |
77 | **Interfaces**
78 |
79 | * Interface names should be nouns in **PascalCase**.
80 | * Interfaces should be prefaced with the letter **I**.
81 | Examples:
82 | ```c#
83 | // Bad.
84 | public interface Bike
85 | {
86 |
87 | }
88 |
89 | // Good.
90 | public interface IBike
91 | {
92 |
93 | }
94 | ```
95 |
96 | **Namespaces, Enums, Structs, Delegates, Events, Methods and Properties**
97 |
98 | * **PascalCase** should be used
99 |
100 | **Variables and Constants general guidelines**
101 |
102 | * Variable names should be short yet meaningful.
103 | * Should not start with underscore(_) or dollar sign $ characters.
104 | * Should be mnemonic i.e, designed to indicate to the casual observer the intent of its use.
105 | * One-character variable names should be avoided except for temporary variables.
106 | * Magic-numbers (hard-coded numbers) shouldn't be used
107 | * Include units in variable names
108 |
109 | Examples:
110 | ```c#
111 | private void DoSomething()
112 | {
113 | // Bad.
114 | long pollInterval;
115 | int fileSize;
116 | }
117 |
118 | private void DoSomething()
119 | {
120 | // Good.
121 | long pollIntervalMs;
122 | int fileSizeGb.
123 | }
124 | ```
125 |
126 | **Constants**
127 |
128 | * Should be all uppercase with words separated by underscores (_).
129 |
130 | Examples:
131 | ```c#
132 | // Bad.
133 | public const int TimeInSeconds = 5;
134 |
135 | //Good.
136 | public const int TIME_IN_SECONDS = 5;
137 | ```
138 |
139 | **Private variables**
140 |
141 | * Should be written in **PascalCase**.
142 | * Should be prefixed with the **m_**
143 |
144 | Examples:
145 | ```c#
146 | // Bad.
147 | int fileSize;
148 |
149 | // Good.
150 | int m_FileSizeGb.
151 | ```
152 |
153 | **Local variables**
154 |
155 | * Should be written in **lowerCamelCase**.
156 | * Using the implicit typing for the local variables declaration is encouraged.
157 |
158 | Examples:
159 | ```c#
160 | private void DoSomething()
161 | {
162 | // Bad.
163 | long PollInterval = Convert.ToInt32(Console.ReadLine());;
164 | }
165 |
166 | private void DoSomething()
167 | {
168 | // Good.
169 | long pollIntervalMs = Convert.ToInt32(Console.ReadLine());
170 | }
171 | ```
172 |
173 | **Generics**
174 |
175 | 1. **Generic Version**: If you have a generic only version of a class or interface, name the file with the format `Something.cs`.
176 |
177 | 2. **Both generic and non generic versions**: If you have both generic and non generic versions of a class or interface use `Something.cs` for non generic version file and `Something(T).cs` for generic version file.
178 |
179 | ## Comments and Documentation
180 |
181 | Have you ever heard that "good code is supposed to be self-explanatory"? I'd love to find the author of this statement and tell him everything I think about it. This guy is responsible for thousands of unmaintainable projects because devs are lazy by nature and use it as excuse whenever it's possible.
182 |
183 | The problem is rather obvious: self-explanatory code only tell how it is working. It rarely tells how it should work. That's why we have some strict rules regarding code documentation and comments.
184 |
185 | The more visible a piece of code is (and by extension - the farther away consumers might be), the more documentation is needed.
186 |
187 | ### General Documentation Quality
188 |
189 | * Use [XMLDoc](http://msdn.microsoft.com/en-us/library/b2s063f7%28v=VS.100%29.aspx)-style comments.
190 | * Every public class or method **must** have a comment.
191 | * Don't document overriding methods unless you want to tell what's different in its behavior.
192 | * Documentation text should be written using complete sentences, full stops are not necessary.
193 |
194 | ### Documenting a Class
195 | Documentation for a class may range from a single sentence to paragraphs with code examples. Documentation should serve to disambiguate any conceptual blanks in the API, and make it easier to quickly and **correctly** use your API. A thorough class doc usually has a one sentence summary and, if necessary, a more detailed explanation.
196 |
197 | ```C#
198 | ///
199 | /// Implements a variable-size List that uses an array of objects to store the
200 | /// elements. A List has a capacity, which is the allocated length
201 | /// of the internal array. As elements are added to a List, the capacity
202 | /// of the List is automatically increased as required by reallocating the
203 | /// internal array.
204 | ///
205 | public class List : IList, System.Collections.IList, IReadOnlyList
206 | {
207 | ...
208 | }
209 | ```
210 |
211 | ### Documenting a Method
212 |
213 | A method doc should tell what the method does and what exceptions can be thrown by the method. Depending on the argument types, it may also be important to document input format.
214 |
215 | ```C#
216 | ///
217 | /// Adds two doubles and returns the result.
218 | ///
219 | ///
220 | /// The sum of two doubles.
221 | ///
222 | /// Thrown when one parameter is max
223 | /// and the other is greater than zero.
224 | /// See to add integers.
225 | /// A double precision number.
226 | /// A double precision number.
227 | public static double Add(double a, double b)
228 | {
229 | ...
230 | }
231 | ```
232 |
233 |
234 | ### Documenting a Constructor
235 |
236 | A constructor doc should tell the specific information about usage and parameters, if there is some not obvious behaviour.
237 |
238 | ```C#
239 | ///
240 | /// Initializes a new instance of with the concatentaion of passed values: *explaination if required*
241 | ///
242 | /// The unique value 1.
243 | /// The unique value 2
244 | /// Thrown, if values are equal.
245 | public SomeTransientObject(
246 | Value1 value1,
247 | Value2 value2)
248 | {
249 | // thrown exceptions must be documented.
250 | if (value1.Equals(value2){
251 | throw ArguementException(nameof(value1));
252 | })
253 |
254 | // not obvious behaviour must be documented.
255 | Value = value1.Append(value2);
256 | }
257 | ```
258 |
259 | However, even if the constructor contains only transparent initialization code, we should leave a minimal boilerplate comment, e.g. :
260 |
261 | ```C#
262 | ///
263 | /// Initializes a new instance of
264 | ///
265 | /// The description of value1
266 | /// The description of value2
267 | public SomeTransientObject(
268 | Value1 value1,
269 | Value2 value2)
270 | {
271 | Value1 = value1;
272 | Value2 = value2;
273 | }
274 | ```
275 |
276 | There is an exclusion for singleton object constructors, that are not supposed to have any explicit usages and require only injected types as parameters (e.g. created only using IoC, or reflection). For such objects we don't describe parameters, because it clutters up the code and causes additional merge conflicts, while it does not contain any useful information:
277 | ```C#
278 | // bad
279 |
280 | ///
281 | /// Initializes an instance of
282 | ///
283 | /// The nice service // this information is useless.
284 | /// The good service
285 | /// The smart service
286 | /// The N service
287 | public SomeService(
288 | INiceService niceService,
289 | IGoodService goodService,
290 | ISmartService smartService,
291 | INService nService)
292 | {
293 | ...
294 | }
295 |
296 | // good
297 |
298 | ///
299 | /// Initializes an instance of
300 | ///
301 | public SomeService(
302 | INiceService niceService,
303 | IGoodService goodService,
304 | ISmartService smartService,
305 | INService nService)
306 | {
307 | ...
308 | }
309 |
310 | ```
311 |
312 |
313 | ### Inline Comments
314 |
315 | Inline comments should always be added when the intent or purpose of any code isn't completely explicit, but the code itself ought to be clear enough to follow logically.
316 |
317 | ```C#
318 | private void EnsureCapacity(int min)
319 | {
320 | if (m_Items.Length < min)
321 | {
322 | long newCapacity = m_Items.Length == 0 ? m_DefaultCapacity : m_Items.Length * 2;
323 | // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
324 | // Note that this check works even when m_Items.Length overflowed thanks to the (uint) cast
325 | if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
326 | if (newCapacity < min) newCapacity = min;
327 | Capacity = newCapacity;
328 | }
329 | }
330 | ```
331 |
332 | ## General Code Quality
333 |
334 | **Single responsibility principle**
335 |
336 | Every module, class or method should have responsibility over a single part of the functionality provided by the software. For the more detailed description of the Single Responsibility Principle from Robert C. Martin follow the [link](https://8thlight.com/blog/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html).
337 |
338 | **Line length**
339 |
340 | Try to limit line length. This limit can be arbitrary (e.g. 80 or 100 characters) and not rigidly enforced, but the goal is to reduce the amount of horizontal scrolling for the developer.
341 |
342 | **Method and block length**
343 |
344 | Try to limit the length of method and code blocks by 50 lines so that they are not trying to do too much. Shorter methods are easier to test, and smaller sections of code are more quickly comprehended by developers.
345 |
346 | **Never use Extension Methods**
347 |
348 | All the additional logic that is necessary is implemented through utility classes
349 |
350 | **Brackets and blocks**
351 |
352 | Open braces should always be at the beginning of the line after the statement that begins the block.
353 |
354 |
355 | ```C#
356 | // bad
357 | private void DoSomething() {
358 | ...
359 | }
360 |
361 | // good
362 | private void DoSomething()
363 | {
364 | ...
365 | }
366 | ```
367 |
368 | An empty line should be used after block of code in the methods.
369 | ```C#
370 | // bad
371 | private void DoSomething()
372 | {
373 | if (foobar)
374 | {
375 | ...
376 | }
377 | DoAnotherAction();
378 | }
379 |
380 | // good
381 | private void DoSomething()
382 | {
383 | if (foobar)
384 | {
385 | ...
386 | }
387 |
388 | DoAnotherAction();
389 | }
390 | ```
391 |
392 | Always use brackets when creating code blocks of any kind. Every block, even if it is only one line, needs to have its own curly braces in order to avoid confusion and prevent the possibility of hard to track bugs.
393 |
394 | ```C#
395 | // bad
396 | if (foobar) DoSomething();
397 |
398 | // good
399 | if (foobar)
400 | {
401 | DoSomething();
402 | }
403 | ```
404 |
405 | **Explicit type**
406 |
407 | Please use explicit type instead of `var` in all cases (may be except creating a new object with `new` keyword). It makes code cleaner and more understandable (especially while reading pull requests)
408 |
409 |
410 | ```C#
411 | // bad
412 | var foo = GetSomething();
413 |
414 | // good
415 | FooDto foo = GetSomething();
416 | ```
417 |
418 | **Ternary operators**
419 |
420 | Ternary operators are fine for clear-cut conditionals, but unacceptable for confusing choices.
421 |
422 | ```C#
423 | // bad
424 | int value = a && b ? 11 : a ? 10 : b ? 1 : 0;
425 |
426 | // good
427 | int value = isSimple ? 11 : 1;
428 | ```
429 |
430 | Ternary expressions should never be nested because they just add to the confusion.
431 |
432 | **Expression-bodied members**
433 |
434 | Don't use [Expression-bodied members](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members) in all over the cases where it can be used. Despite of the fact `c#` supports such "sugar" and it looks more compactable, it makes code more complicated to read
435 |
436 | ```C#
437 | // bad
438 | public bool Foo() => false;
439 |
440 | // good
441 | public bool Foo()
442 | {
443 | return false;
444 | }
445 | ```
446 |
447 | **Centralize duplicate logic in utility functions**
448 |
449 | **Using the `dynamic` keyword allowed only in tests**
450 |
451 | **Use a StringBuilder instead of string if multiple concatenations are required**
452 |
453 | **Override ToString method for the types which you want to provide with custom information.**
454 |
455 | **Avoid straightaway copy/pasting of code from other sources. It is always recommended to hand write the code.**
456 |
457 | **Usage of 'out' and 'ref' keywords be avoided as recommended by Microsoft [(in the Code analysis Rules and guidelines)](https://msdn.microsoft.com/en-us/library/dd264925.aspx)**
458 |
459 | ## Best Practices
460 |
461 | ### Minimize Visibility
462 |
463 | In a class API, you should support access to any methods and fields that you make accessible. Therefore, only expose what you intend the caller to use. This can be imperative when writing thread-safe code.
464 |
465 | ```C#
466 | public class Parser
467 | {
468 | // Bad.
469 | // - Callers can directly access and mutate, possibly breaking internal assumptions.
470 | public Dictionary RawFields;
471 |
472 | // Bad.
473 | // - This is probably intended to be an internal utility function.
474 | public String ReadConfigLine()
475 | {
476 | ..
477 | }
478 | }
479 |
480 | // Good.
481 | // - rawFields and the utility function are hidden
482 | // - The class is package-private, indicating that it should only be accessed indirectly.
483 | class Parser
484 | {
485 | private Dictionary m_RawFields;
486 |
487 | private string ReadConfigLine()
488 | {
489 | ..
490 | }
491 | }
492 | ```
493 |
494 | ### Exceptions
495 |
496 | **Catch narrow exceptions**
497 |
498 | Sometimes when using try/catch blocks, it may be tempting to just `catch Exception`, so you don't have to worry about what type was thrown. This is usually a bad idea, as you can end up catching more than you really wanted to deal with. For example, `catch Exception` would capture `NullReferenceException`, and would capture `OutOfMemoryException`.
499 |
500 | ```C#
501 | // Bad.
502 | // - If a OutOfMemoryException happens, the program continues rather than aborting.
503 | try
504 | {
505 | storage.InsertUser(user);
506 | }
507 | catch (Exception ex)
508 | {
509 | LOG.Error("Failed to insert user.");
510 | }
511 |
512 | try
513 | {
514 | storage.InsertUser(user);
515 | }
516 | catch (StorageException ex)
517 | {
518 | LOG.Error("Failed to insert user.");
519 | }
520 | ```
521 |
522 | **Don't swallow exceptions**
523 |
524 | An empty catch block is usually a bad idea, as you have no signal of a problem. Coupled with narrow exception violations, it's a recipe for disaster.
525 |
526 | **Throw appropriate exception types**
527 |
528 | Let your API users obey the "catch narrow exceptions" rule, don't throw `Exception`. Even if you are calling another naughty API that throws `Exception`, at least hide that so it doesn't bubble up even further. You should also make an effort to hide implementation details from your callers when it comes to exceptions. Also, don't forget to add the information about exceptions to the method's documentation, as it was described in [Documenting a Method](#documenting-a-method).
529 |
530 | ```C#
531 | // Bad.
532 | // - Caller is forced to catch Exception, trapping many unnecessary types of issues.
533 | public class DataStore
534 | {
535 | public string FetchValue(string key)
536 | {
537 | ...
538 | throw new Exception("error message");
539 | }
540 | }
541 |
542 | // Better.
543 | // - The interface leaks details about one specific implementation.
544 | public DataStore
545 | {
546 | public string FetchValue(string key)
547 | {
548 | ...
549 | throw new SQLException("error message");
550 | }
551 | }
552 |
553 | // Good.
554 | // - A custom exception type insulates the user from the implementation.
555 | // - Different implementations aren't forced to abuse irrelevant exception types.
556 | public DataStore
557 | {
558 | public string FetchValue(string key)
559 | {
560 | ...
561 | throw new StorageException("error message");
562 | }
563 |
564 | public class StorageException : Exception
565 | {
566 | ...
567 | }
568 | }
569 | ```
570 |
571 | ## Premature Optimization
572 |
573 | **Premature optimization is the root of all evil.**
574 |
575 | Donald Knuth is a smart guy, and he had a few things to [say](http://c2.com/cgi/wiki?PrematureOptimization) on the topic.
576 |
577 | Unless you have strong evidence that an optimization is necessary, it's usually best to implement the un-optimized version first (possibly leaving notes about where optimizations could be made).
578 |
579 | So before you spend a week writing your memory-mapped compressed huffman-encoded hashmap, use the stock stuff first and measure.
580 |
581 | ## Always Use Properties instead of Public Variables
582 |
583 | Reason behind this is, it makes your code properly encapsulated in OOPs environment. By using getters & setters, you can restrict the user directly accessing the member variables. You can restrict setting the values explicitly thus making your data protected from accidental changes. Also, properties give you easier validation for your data.
584 |
585 | ## Use IDisposable Interface
586 |
587 | Use IDisposable interface to free all the resources from the memory. Once you implement IDisposable interface in your class, you will get a Dispose() method there. Write code there to free the resources. If you implement IDisposable, you can initialize your class like this:
588 |
589 | ```C#
590 | using (PersonDataSource personDataSource = DataSource.GetPerson())
591 | {
592 | // write your code here
593 | }
594 | ```
595 |
596 | After the using() {} block, it will call the Dispose() method automatically to free up the class resources. You will not have to call the Dispose() explicitly for the class.
597 |
598 | ## Prefer “is” and “as” Operators While Casting
599 |
600 | It is better to use “is” and “as” operator while casting. Instead of Explicit casting, use the Implicit casting.
601 |
602 | ## Structured approach to logging
603 |
604 | ### INFO
605 |
606 | As the name suggests, these are purely informational messages. To use this log level effectively, try to think about what general information would be useful for diagnosing an application error or oddities.
607 |
608 | ### WARN
609 |
610 | Should be used to indicate that the application faced a potential problem; however, the user experience has not been affected in any way. In this case, it is acceptable to swallow an exception in the catch block.
611 |
612 | ### ERROR
613 | Should be used to indicate that the application faced a significant problem and that, as a result, the user experience was affected in some way. In this case, it is necessary to, optionally, modify exception and throw it again.
614 |
615 | ### VERBOSE
616 | Information that is diagnostically helpful to the developer.
617 |
618 | ### TRACE
619 | Very detailed information used only in specific cases to find out the reasons of much-complicated issues
620 |
--------------------------------------------------------------------------------
/Go/Git.md:
--------------------------------------------------------------------------------
1 | # AdGuard Go Team Git guidelines
2 |
3 | * §
4 | Call your branches either `NNNN-fix-foo` (where `NNNN` is the ID of the
5 | GitHub / GitLab / Jira issue you worked on in this branch) or just `fix-foo`
6 | if there was no issue. Make sure to always spell the Jira IDs (but [not the
7 | suffix][low]) in uppercase letters to make sure it gets linked.
8 |
9 | * §
10 | Do not put your text editors' temporary files into the project's
11 | `.gitignore` files. The more are added, the harder they become to maintain.
12 | Put them into your [global `.gitignore`][ignore] file instead.
13 |
14 | Only build, run, and test outputs should be placed into `.gitignore`,
15 | sorted, with negations at the bottom to make sure they take effect.
16 |
17 | * §
18 | Follow the commit message header format:
19 |
20 | ```none
21 | pkg: fix the network error logging issue
22 | ```
23 |
24 | Where `pkg` is usually the directory or Go package (without the
25 | `internal/…` part) where most changes took place. If there are several such
26 | packages, or the change is top-level only, write `all`.
27 |
28 | * §
29 | Keep your commit messages, including headers, to eighty (**80**) columns.
30 |
31 | * §
32 | Only use lowercase letters in your commit message headers. The rest of the
33 | message should follow the [plain text conventions][text].
34 |
35 | The exceptions are direct mentions of identifiers from the source code and
36 | filenames like `HACKING.md` as well as [issue IDs][jira].
37 |
38 | [ignore]: https://stackoverflow.com/a/7335487/1892060.
39 | [jira]: #li-b085c724
40 | [low]: #li-b5f7773a
41 | [text]: Text.md
42 |
--------------------------------------------------------------------------------
/Go/Go.md:
--------------------------------------------------------------------------------
1 | # AdGuard Go Team Development Guidelines
2 |
3 | Following this document is obligatory for all new Go code. Some of the rules
4 | aren't enforced as thoroughly or remain broken in old code, but this is still
5 | the place to find out about what we **want** our code to look like and how to
6 | improve it.
7 |
8 | The rules are mostly sorted in the alphabetical order.
9 |
10 |
11 |
12 | ## Contents
13 |
14 | * [Code](#code)
15 | * [Commenting](#commenting)
16 | * [Error Handling](#errors)
17 | * [Formatting](#formatting)
18 | * [Naming](#naming)
19 | * [Testing](#testing)
20 | * [Recommended Reading](#recommended-reading)
21 |
22 |
23 |
24 | > Not Golang, not GO, not GOLANG, not GoLang. It is Go in natural language,
25 | > golang for others.
26 |
27 | — [@rakyll](https://twitter.com/rakyll/status/1229850223184269312)
28 |
29 |
30 |
31 | ## Code
32 |
33 | * §
34 | Avoid `break` and `continue` with labels. Most of the time the code can be
35 | rewritten without them, and most of the time the resulting new code is also
36 | clearer.
37 |
38 | * §
39 | Always `recover` from panics in new goroutines. Preferably in the very
40 | first statement. If all you want there is a log message, use `log.OnPanic`.
41 |
42 | * §
43 | Avoid `fallthrough`. It makes it harder to rearrange `case`s, to reason
44 | about the code, and also to switch the code to a handler approach, if that
45 | becomes necessary later.
46 |
47 | * §
48 | Avoid `goto`.
49 |
50 | * §
51 | Avoid `init` and use explicit initialization functions instead.
52 |
53 | * §
54 | Avoid lazy initialization. Constructors should validate their arguments and
55 | return meaningful errors.
56 |
57 | * §
58 | Avoid `new`, especially with structs, unless a temporary value is needed,
59 | for example when checking the type of an error using `errors.As`.
60 |
61 | * §
62 | Avoid packages `reflect` and `unsafe` unless absolutely necessary. Always
63 | provide a comment explaining why you are using it.
64 |
65 | * §
66 |
67 | Be aware of and strive to shorten resource scopes.
68 |
69 | For example, if you have a long function that makes an HTTP request and
70 | defers the close of the body at the beginning and then does more stuff, it's
71 | better to factor out the HTTP request and the response parsing into
72 | a separate method. Same goes for mutexes.
73 |
74 | That is, do **not** do this:
75 |
76 | ```go
77 | // Bad! Resource r is only closed at the end of the very long function.
78 | func Foo() (err error) {
79 | r, err := open()
80 | check(err)
81 | defer r.close()
82 |
83 | v, err := decode(r)
84 | check(err)
85 |
86 | // Lots of slow stuff with v. r is only closed once Foo exits.
87 | }
88 | ```
89 |
90 | Do this instead:
91 |
92 | ```go
93 | // Good, r is closed after loadV returns.
94 | func Foo() (err error) {
95 | v, err := loadV()
96 | check(err)
97 |
98 | // Lots of slow stuff with v.
99 | }
100 |
101 | func loadV() (v *V, err error) {
102 | r, err := open()
103 | check(err)
104 | defer r.close()
105 |
106 | return decode(r)
107 | }
108 | ```
109 |
110 | * §
111 | Be aware of structure alignment as well as the number of [pointer
112 | bytes][ptr]. Exceptions could be made if a suboptimal alignment produces
113 | significantly better readability.
114 |
115 | ```go
116 | // Bad! Lots of padding between the uint64s and bools. Pointers are at the
117 | // end of the structure.
118 | type bad struct {
119 | a uint64
120 | b bool
121 | c uint64
122 | d bool
123 | e uint64
124 | f bool
125 | g uint64
126 | h bool
127 |
128 | y *otherType
129 | z *otherType
130 | }
131 |
132 | // Good. The padding is minimized, and the pointers are closer to the top.
133 | type good struct {
134 | y *otherType
135 | z *otherType
136 |
137 | g uint64
138 | e uint64
139 | c uint64
140 | a uint64
141 |
142 | h bool
143 | f bool
144 | d bool
145 | b bool
146 | }
147 | ```
148 |
149 | Use the [`fieldalignment`][fa] analyzer when unsure.
150 |
151 | * §
152 | Check against empty strings like this:
153 |
154 | ```go
155 | if s == "" {
156 | // …
157 | }
158 | ```
159 |
160 | Except when the check is done to then use the first character:
161 |
162 | ```go
163 | if len(s) > 0 {
164 | c := s[0]
165 | }
166 | ```
167 |
168 | * §
169 | Context values should be considered immutable. Do not use modifications of
170 | context values as a means of communicating something up the stack. That is,
171 | do **not** do this:
172 |
173 | ```go
174 | // Bad! Function outer expects inner to mutate logEntry.
175 | func outer(ctx context.Context, /* … */) {
176 | logEntry := &LogEntry{}
177 | ctx = contextWithLogEntry(ctx, logEntry)
178 |
179 | inner(ctx, /* … */)
180 |
181 | if logEntry.Success {
182 | // …
183 | }
184 | }
185 |
186 | // Bad! Function inner mutates logEntry just so that outer could receive
187 | // that change.
188 | func inner(ctx context.Context, /* … */) {
189 | logEntry := logEntryFromContext(ctx)
190 | logEntry.Success = true
191 |
192 | // …
193 | }
194 | ```
195 |
196 | * §
197 | Don't rely only on file names for build tags to work. Always add build tags
198 | as well.
199 |
200 | * §
201 | Don't use `fmt.Sprintf` where a more structured approach to string
202 | conversion could be used. For example, `netutil.JoinHostPort`,
203 | `net.JoinHostPort` or `url.(*URL).String`.
204 |
205 | * §
206 | Don't use naked `return`s.
207 |
208 | * §
209 | Eschew external dependencies, including transitive, unless absolutely
210 | necessary.
211 |
212 | * §
213 | In templates, put spaces between the delimiters (`{{` and `}}` by default)
214 | and the content, because when the minus sign is used to remove whitespace,
215 | it's an error to omit these spaces.
216 |
217 | * §
218 | No name shadowing, including of predeclared identifiers, since it can often
219 | lead to subtle bugs, especially with errors. This rule does not apply to
220 | struct fields, since they are always used together with the name of the
221 | struct value, so there isn't any confusion.
222 |
223 | * §
224 | Prefer build constraints to `runtime.GOOS`.
225 |
226 | * §
227 | Prefer constants to variables where possible. Avoid global variables unless
228 | necessary. Use [constant errors] instead of `errors.New`.
229 |
230 | * §
231 | Prefer defining `Foo.String` and `ParseFoo` in terms of `Foo.MarshalText`
232 | and `Foo.UnmarshalText` correspondingly and not the other way around.
233 |
234 | * §
235 | Prefer to use pointers to structs in function arguments, including method
236 | receivers, unless there is a reason to do the opposite. Among valid reasons
237 | are:
238 |
239 | * the struct is small, which typically means less than a few machine
240 | words;
241 | * the struct is read-only, like `netip.Addr` or `time.Time`;
242 | * the struct is immediately used by an external API that requires a
243 | non-pointer struct;
244 | * the method implements a `FooMarshaler` kind of interface, and so
245 | a non-pointer receiver is required (see [`staticcheck` issue
246 | 911][staticcheck-911]).
247 |
248 | * §
249 | Prefer to [return structs and accept interfaces][struct].
250 |
251 | * §
252 | Prefer to use named functions for goroutines.
253 |
254 | * §
255 | Prefer to use package `golang.org/x/sys` and not package `syscall`, as the
256 | latter is frozen.
257 |
258 | * §
259 | Program code lines should not be longer than one hundred (**100**) columns.
260 | For comments, see the [text guidelines][text].
261 |
262 | Don't forget to also [set the tab width][tab] in your editor's settings.
263 |
264 | * §
265 | Use linters. `make go-lint`, if the project has one. A minimum of `go vet`,
266 | `errcheck`, and staticcheck if the project does not.
267 |
268 | * §
269 | When returning an error from a function that also returns a non-nilable
270 | type, for example a `time.Time`, a `time.Duration`, or a `netip.Addr`,
271 | spell the empty value explicitly to show that the value is invalid.
272 |
273 | So, do this:
274 |
275 | ```go
276 | func fooTime() (t time.Time, err error) {
277 | err = bar()
278 | if err != nil {
279 | return time.Time{}, err
280 | }
281 |
282 | // …
283 | }
284 | ```
285 |
286 | and **not** this:
287 |
288 | ```go
289 | func fooTime() (t time.Time, err error) {
290 | err = bar()
291 | if err != nil {
292 | // Bad! This could be read as t being valid, which it is not.
293 | return t, err
294 | }
295 |
296 | // …
297 | }
298 | ```
299 |
300 | * §
301 | Write logs and error messages in lowercase only to make it easier to `grep`
302 | logs and error messages without using the `-i` flag.
303 |
304 | * §
305 | Write static interface type checks like this, including the standard
306 | comment:
307 |
308 | ```go
309 | // type check
310 | var _ ifaceType = (*implType)(nil)
311 | ```
312 |
313 | Put them right before the declaration of the first method implementing the
314 | interface in question.
315 |
316 | [constant errors]: https://dave.cheney.net/2016/04/07/constant-errors
317 | [fa]: https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignment/cmd/fieldalignment
318 | [ptr]: https://github.com/golang/go/issues/44877#issuecomment-794565908
319 | [staticcheck-911]: https://github.com/dominikh/go-tools/issues/911
320 | [struct]: https://medium.com/@cep21/what-accept-interfaces-return-structs-means-in-go-2fe879e25ee8
321 | [tab]: Text.md#li-84467c92
322 | [text]: Text.md
323 |
324 |
325 |
326 | ##
327 |
328 | See also the [text guidelines][text].
329 |
330 | * §
331 | Document everything, including unexported top-level identifiers, to build
332 | a habit of writing documentation.
333 |
334 | * §
335 | Don't put identifiers into any kind of quotes.
336 |
337 | * §
338 | Put comments above the documented entity, **not** to the side, to improve
339 | readability.
340 |
341 | * §
342 | When a method implements an interface, start the doc comment with the
343 | standard template:
344 |
345 | ```go
346 | // Foo implements the Fooer interface for *foo.
347 | func (f *foo) Foo() {
348 | // …
349 | }
350 | ```
351 |
352 | When the implemented interface is unexported:
353 |
354 | ```go
355 | // Unwrap implements the hidden wrapper interface for *fooError.
356 | func (err *fooError) Unwrap() (unwrapped error) {
357 | // …
358 | }
359 | ```
360 |
361 | * §
362 | Write names of RFCs without a hyphen and don't put a newline between the
363 | letters and the numbers. Godoc and pkg.go.dev both will add a link to the
364 | RFC, but only if the RFC is spelled that way.
365 |
366 | So, do this:
367 |
368 | ```go
369 | // toTime parses an RFC 3339 timestamp.
370 | ```
371 |
372 | and **not** this:
373 |
374 | ```go
375 | // Bad! A hyphen is used.
376 |
377 | // toTime parses an RFC-3339 timestamp.
378 | ```
379 |
380 | ```go
381 | // Bad! A newline before the number.
382 |
383 | // toTime parses and also uses an optimized validation technique on an RFC
384 | // 3339 timestamp.
385 | ```
386 |
387 | * §
388 | Document important contracts (assertions, pre- and postconditions) of
389 | fields, functions and methods. That is, nilness of arguments, state of
390 | mutexes, relationship between struct fields, and so on. As an exception,
391 | a method receiver can be generally considered to be required to be non-nil,
392 | unless a different behavior is explicitly documented.
393 |
394 | For example:
395 |
396 | ```go
397 | // needsNonNil is an example of a method that requires a non-nil argument.
398 | // m must not be nil. r.mu is expected to be locked.
399 | func (r *Receiver) needsNonNil(m *Message) (err error) {
400 | // …
401 | }
402 | ```
403 |
404 |
405 |
406 | ## Error Handling
407 |
408 | * §
409 | Add context to errors but avoid duplication. For example, `os.Open` always
410 | adds the path to its error, so this is redundant:
411 |
412 | ```go
413 | // Bad! Will duplicate the file name.
414 | f, err := os.Open(fileName)
415 | if err != nil {
416 | return fmt.Errorf("opening %q: %w", fileName, err)
417 | }
418 | ```
419 |
420 | If a function returns enough context, or a deferred helper is used, document
421 | that. Prefer to use a standard comment across a project. For example:
422 |
423 | ```go
424 | err = f()
425 | if err != nil {
426 | // Don't wrap the error, because it's informative enough as is.
427 | return err
428 | }
429 | ```
430 |
431 | * §
432 | Avoid having multiple errors in a function. In situations when it's not
433 | feasible, use meaningful names. For example, `closeErr` for errors from
434 | `Close()` or `testErr` for subtest errors.
435 |
436 | * §
437 | Avoid using the word `error` inside error messages.
438 |
439 | ```go
440 | // BAD!
441 | err = foo()
442 | if err != nil {
443 | return fmt.Errorf("error while calling foo: %w", err)
444 | }
445 | ```
446 |
447 | Just provide the action instead:
448 |
449 | ```go
450 | // Good.
451 | err = foo()
452 | if err != nil {
453 | return fmt.Errorf("performing foo: %w", err)
454 | }
455 | ```
456 |
457 | * §
458 | Parsing functions should include the invalid input into the error message,
459 | unless the input is too big.
460 |
461 | * §
462 | Use only lowercase unless you have to reference an identifier in code or the
463 | project has its own conventions regarding uppercase letters.
464 |
465 | * §
466 | Use `panic` **only** to indicate critical assertion failures. **Do not**
467 | use panics for normal error handling.
468 |
469 | * §
470 | Use utilities from the [`github.com/AdguardTeam/golibs/testutil`][testutil]
471 | package when necessary.
472 |
473 | [testutil]: https://pkg.go.dev/github.com/AdguardTeam/golibs/testutil
474 |
475 |
476 |
477 | ## Formatting
478 |
479 | * §
480 | Consider putting empty lines between documented struct fields and interface
481 | methods to improve readability:
482 |
483 | ```go
484 | // FooConfig is the configuration for a single foo.
485 | type FooConfig struct {
486 | // ID is the unique ID of foo.
487 | ID FooID
488 |
489 | // Name is the human-readable name of this foo.
490 | Name string
491 |
492 | // Timeout is the timeout used for all frobulation operations
493 | // performed by this foo.
494 | Timeout time.Duration
495 | }
496 | ```
497 |
498 | * §
499 | Decorate `break`, `continue`, `return`, and other terminating statements
500 | with empty lines unless it's the only statement in that block.
501 |
502 | * §
503 | Don't group type declarations together. Unlike with blocks of `const`s,
504 | where a `iota` may be used or where all constants belong to a certain type,
505 | there is no reason to group `type`s.
506 |
507 | * §
508 | Don't mix horizontal and vertical placement of groups of arguments and
509 | [return values][ret] in calls and definitions of functions and methods.
510 | That is, either this:
511 |
512 | ```go
513 | func f(a, b, c T) {
514 | // …
515 | }
516 | ```
517 |
518 | ```go
519 | err := f(a, b, c)
520 | ```
521 |
522 | Or, when the arguments are too long, this:
523 |
524 | ```go
525 | func functionWithALongName(
526 | firstArgumentWithALongName typeWithALongName,
527 | secondArgumentWithALongName otherTypeWithALongName,
528 | thirdArgumentWithALongName thirdTypeWithALongName,
529 | ) {
530 | // …
531 | }
532 | ```
533 |
534 | ```go
535 | err := functionWithALongName(
536 | firstArgumentWithALongName,
537 | secondArgumentWithALongName,
538 | thirdArgumentWithALongName,
539 | )
540 | ```
541 |
542 | Or, with a call with a struct literal:
543 |
544 | ```go
545 | err := functionWithALongName(arg, structType{
546 | field1: val1,
547 | field2: val2,
548 | })
549 | ```
550 |
551 | But **never** this:
552 |
553 | ```go
554 | // Bad!
555 | func functionWithALongName(firstArgumentWithALongName typeWithALongName,
556 | secondArgumentWithALongName otherTypeWithALongName,
557 | thirdArgumentWithALongName thirdTypeWithALongName) {
558 | // …
559 | }
560 | ```
561 |
562 | ```go
563 | // Bad!
564 | err := functionWithALongName(firstArgumentWithALongName,
565 | secondArgumentWithALongName,
566 | thirdArgumentWithALongName,
567 | )
568 | ```
569 |
570 | * §
571 | Don't write non-test code with more than four (**4**) levels of indentation.
572 | Just like [Linus said][3tabs], plus an additional level for an occasional
573 | error check or struct initialization.
574 |
575 | The exception proving the rule is the table-driven test code, where an
576 | additional level of indentation is allowed.
577 |
578 | * §
579 | Group `require.*` blocks together with the preceding related statements, but
580 | separate from the following `assert.*` and unrelated requirements.
581 |
582 | ```go
583 | val, ok := valMap[key]
584 | require.True(t, ok)
585 | require.NotNil(t, val)
586 |
587 | assert.Equal(t, expected, val)
588 | ```
589 |
590 | * §
591 | Put deferred calls of destructors, for example `f.Close()`, into the same
592 | paragraph as constructors. This is an exception to the [paragraph
593 | rule][par].
594 |
595 | ```go
596 | f, err := os.Open(fileName)
597 | if err != nil {
598 | return err
599 | }
600 | defer func() { processError(f.Close()) }()
601 | ```
602 |
603 | * §
604 | Split numbers with more than four (**4**) digits into triplets using
605 | underscores:
606 |
607 | ```go
608 | const (
609 | max8 = 255
610 | max16 = 65_535
611 | max32 = 4_294_967_295
612 | )
613 | ```
614 |
615 | * §
616 | Start a new paragraph after the final closing curly brace of a block. So
617 | this:
618 |
619 | ```go
620 | if a == b {
621 | // …
622 | }
623 |
624 | for i := 0; i < N; i++ {
625 | // …
626 | }
627 |
628 | switch x {
629 | // …
630 | }
631 | ```
632 |
633 | and **not** this:
634 |
635 | ```go
636 | // Bad!
637 | if a == b {
638 | // …
639 | }
640 | for i := 0; i < N; i++ {
641 | // …
642 | }
643 | switch x {
644 | // …
645 | }
646 | ```
647 |
648 | The exceptions are the final block inside a bigger block and the [destructor
649 | `defer`s][dtr].
650 |
651 | * §
652 | Use `gofumpt --extra -s`.
653 |
654 | * §
655 | When a function's definition becomes [too long][long], first make the
656 | function's arguments vertically placed and only then do the same with the
657 | return values. That is, do this:
658 |
659 | ```go
660 | func functionWithALongName(
661 | argumentWithALongName typeWithALongName,
662 | ) (returnValueWithALongName typeWithALongName, err error) {
663 | // …
664 | }
665 | ```
666 |
667 | and **not** this:
668 |
669 | ```go
670 | // Bad!
671 | func functionWithALongName(argumentWithALongName typeWithALongName) (
672 | returnValueWithALongName typeWithALongName,
673 | err error,
674 | ) {
675 | // …
676 | }
677 | ```
678 |
679 | * §
680 | Write `switch`es with large lists of values in one `case` like this:
681 |
682 | ```go
683 | switch n {
684 | case
685 | longValueName1,
686 | longValueName2,
687 | longValueName3,
688 | longValueName4,
689 | longValueName5:
690 | return true
691 | default:
692 | return false
693 | }
694 | ```
695 |
696 | * §
697 | Write slices of struct like this:
698 |
699 | ```go
700 | ts := []T{{
701 | Field: Value0,
702 | // …
703 | }, {
704 | Field: Value1,
705 | // …
706 | }, {
707 | Field: Value2,
708 | // …
709 | }}
710 | ```
711 |
712 | [3tabs]: https://www.kernel.org/doc/html/v4.17/process/coding-style.html#indentation
713 | [dtr]: #li-46a924cd
714 | [long]: #li-baa640a3
715 | [par]: #li-f2156af9
716 | [ret]: #li-d9b8f5a8
717 |
718 |
719 |
720 | ## Naming
721 |
722 | * §
723 | Example files should be names `example_test.go` if there is one such file
724 | for the whole package and `foo_example_test.go` if there are several.
725 |
726 | * §
727 | Don't use underscores in file and package names, unless they're build tags
728 | or for tests. This is to prevent accidental build errors with weird tags.
729 |
730 | * §
731 | For brands or words with more than 1 capital letter, lowercase all letters:
732 | `githubToken`, **not** `gitHubToken`.
733 |
734 | * §
735 | Methods that convert types for external data, such as configuration
736 | structures and response structures, into internal types should be called
737 | `toInternal`:
738 |
739 | ```go
740 | // toInternal converts a user object from the configuration file into
741 | // a *User.
742 | func (u *confUser) toInternal() (user *User) {
743 | // …
744 | }
745 | ```
746 |
747 | * §
748 | Name benchmarks and tests using the same convention as examples. For
749 | example:
750 |
751 | ```go
752 | func TestFunction(t *testing.T) { /* … */ }
753 | func TestFunction_suffix(t *testing.T) { /* … */ }
754 | func TestType_Method(t *testing.T) { /* … */ }
755 | func TestType_Method_suffix(t *testing.T) { /* … */ }
756 | ```
757 |
758 | * §
759 | Name `context.Context` helper functions that return values from the context
760 | `FooFromContext` and the ones that return a new contest with new values,
761 | `ContextWithFoo` or `WithFoo`. Just like in the standard library, the
762 | parent context should be called `parent`.
763 |
764 | ```go
765 | // ContextWithFoo returns a copy of the parent context with the value of `f`
766 | // attached to it.
767 | func ContextWithFoo(parent context.Context, f Foo) (ctx context.Context) {
768 | // …
769 | }
770 |
771 | // FooFromContext returns the Foo attached to the context.
772 | func FooFromContext(parent context.Context) (f Foo, ok bool) {
773 | // …
774 | }
775 | ```
776 |
777 | * §
778 | Name parameters in interface definitions:
779 |
780 | ```go
781 | type Frobulator interface {
782 | Frobulate(f Foo, b Bar) (r Result, err error)
783 | }
784 | ```
785 |
786 | * §
787 | Unused arguments in anonymous functions must be called `_`:
788 |
789 | ```go
790 | v.onSuccess = func(_ int, msg string) {
791 | // …
792 | }
793 | ```
794 |
795 | * §
796 | Use named returns to improve readability of function signatures:
797 |
798 | ```go
799 | func split(data []*datum) (less, greater []*datum) {
800 | // …
801 | }
802 | ```
803 |
804 | * §
805 | Use names like `ErrFoo` for package-level error values and `FooError` for
806 | error types.
807 |
808 | * §
809 | When naming a file which defines an entity, use singular nouns, unless the
810 | entity is some form of a container for other entities:
811 |
812 | ```go
813 | // File: client.go
814 |
815 | package foo
816 |
817 | type Client struct {
818 | // …
819 | }
820 | ```
821 |
822 | ```go
823 | // File: clients.go
824 |
825 | package foo
826 |
827 | type Clients []*Client
828 |
829 | // …
830 |
831 | type ClientsWithCache struct {
832 | // …
833 | }
834 | ```
835 |
836 |
837 |
838 | ## Testing
839 |
840 | * §
841 | If you write a fake implementation of an interface for a test, prefer to
842 | write it using callbacks:
843 |
844 | ```go
845 | // FakeReader …
846 | type FakeReader struct {
847 | OnRead func(b []byte) (n int, err error)
848 | }
849 |
850 | // Read implements the io.Reader interface for *FakeReader.
851 | func (r *FakeReader) Read(b []byte) (n int, err error) {
852 | return r.OnRead(b)
853 | }
854 | ```
855 |
856 | If the method must not be called in this particular test, place a single
857 | `panic("not implemented")` as its body:
858 |
859 | ```go
860 | r := &FakeReader{
861 | OnRead: func(b []byte) (n int, err error) { panic("not implemented") },
862 | }
863 | ```
864 |
865 | * §
866 | Prefer to put all tests into a separate [test package][tpkg]. Tests in the
867 | same package that check unexported APIs should be put into a separate file
868 | named `foo_internal_test.go`.
869 |
870 | * §
871 | Strive to make the test suite finish quickly. If you have long-running
872 | integration test or fuzzes, document them, put them into a separate
873 | Makefile target, and include them into the CI pipeline, but not the main
874 | `make test` target.
875 |
876 | * §
877 | Use `assert.NoError` and `require.NoError` instead of `assert.Nil` and
878 | `require.Nil` on errors.
879 |
880 | * §
881 | Use formatted helpers, like `assert.Nilf` or `require.Nilf`, instead of
882 | simple helpers when a formatted message is required.
883 |
884 | * §
885 | Use functions like `require.Foo` instead of `assert.Foo` when the test
886 | cannot continue if the condition is false.
887 |
888 | [tpkg]: https://pkg.go.dev/cmd/go@master#hdr-Test_packages
889 |
890 |
891 |
892 | ## Recommended Reading
893 |
894 | Here are some links that describe the common ways Go code is written or have
895 | inspire some of the rules we use here:
896 |
897 | * [The Go Wiki: Go Code Review Comments][rev]
898 | * [The Go Wiki: Go Test Comments][test]
899 | * [Go Proverbs][prov]
900 | * [Dmitri Shuralyov: Idiomatic Go][shur]
901 |
902 | [prov]: https://go-proverbs.github.io/
903 | [rev]: https://github.com/golang/go/wiki/CodeReviewComments
904 | [shur]: https://dmitri.shuralyov.com/idiomatic-go
905 | [test]: https://github.com/golang/go/wiki/TestComments
906 |
--------------------------------------------------------------------------------
/Go/Markdown.md:
--------------------------------------------------------------------------------
1 | # AdGuard Go Team Markdown guidelines
2 |
3 | > [!NOTE]
4 | >
5 | > Some rules are deprecated. New code should instead use `markdownlint` and follow the configuration and guidelines favored by the Content Team.
6 | >
7 | > See and .
8 |
9 | * §
10 |
11 | > [!WARNING]
12 | >
13 | > This rule is deprecated. See message at the beginning of the document.
14 |
15 |
16 |
17 | Align things to multiples of four columns. Leave two spaces between the
18 | marker and the content. Below are some examples:
19 |
20 | * §
21 | Lists:
22 |
23 | ```md
24 | * Item one.
25 | * Item two.
26 | ```
27 |
28 | ```md
29 | 1. Item one.
30 | 1. Item two.
31 | ```
32 |
33 | * §
34 | Headers:
35 |
36 | ```md
37 | # Article
38 | ## Section
39 | ### Subsection
40 | #### Subsubsection
41 | ##### Subsubsubsection
42 | ###### Subsubsubsubsection
43 | ```
44 |
45 | * §
46 | Quotes:
47 |
48 | ```md
49 | > This is a quote from some other source. This is a rather long quote
50 | > to show exactly how long quotes must be formatted.
51 | ```
52 |
53 | * §
54 | Embedded HTML:
55 |
56 | ```md
57 |
58 |
59 |
60 | ```
61 |
62 |
63 |
64 | * §
65 | Do not ever change elements' IDs. That breaks people's links. Strive to
66 | give links to moved content.
67 |
68 | * §
69 | Generate IDs for list items. To generate a new random ID, use something
70 | like the following Unix command:
71 |
72 | ```sh
73 | od -A n -N 4 -t x4 < /dev/urandom
74 | ```
75 |
76 | * §
77 | In numbered lists, use `1.` as the list item marker.
78 |
79 | * §
80 | Only use tight lists—that is, lists without empty lines between the
81 | items—for lists of short numeric information or tables of contents. Use
82 | loose lists—lists with empty lines between the items—for everything else.
83 |
84 | ```md
85 | An example of a loose list:
86 |
87 | * This text is a paragraph, and so will be separated from the other items
88 | by a longer vertical space.
89 |
90 | * That is good because it improves readability both in the rendered form
91 | as well as in the text form.
92 |
93 | * And we care about the readability of our texts and encourage people to
94 | write them better.
95 | ```
96 |
97 | ```md
98 | The tight list of valid values:
99 |
100 | * 6;
101 | * 24;
102 | * 72.
103 | ```
104 |
105 | * §
106 | Prefer triple-backtick fenced code blocks with language tags as info strings
107 | to indented code blocks and fenced code blocks without info strings. If the
108 | code block must contain a triple-backtick itself, use triple-tilde fenced
109 | code blocks instead.
110 |
111 | * §
112 | Provide a table of contents for large documents.
113 |
114 | * §
115 | Put punctuation inside the text in bold and italic:
116 |
117 | ```md
118 | This is **very important!** You need to use *these;* those won't work.
119 | ```
120 |
121 | * §
122 | Use asterisks and not underscores for bold and italic.
123 |
124 | * §
125 | Use both `id` and the deprecated `name` attributes to make it work in
126 | Markdown renderers that strip `id`s.
127 |
128 | * §
129 | Use either link references or link destinations only, unless making a table
130 | of contents. Put all link reference definitions at the end of the
131 | second-level section or, if the second-level section is large, at the end
132 | of a third-level one.
133 |
134 | * §
135 | Use `none` as the info string for plain text or custom formats, such as
136 | adblock rules.
137 |
138 | * §
139 |
140 | > [!WARNING]
141 | >
142 | > This rule is obsoleted by the [rule about `names`][names].
143 |
144 | Use the IDs that GitHub would generate in order for this to work both
145 | on GitHub and most other Markdown renderers.
146 |
147 | * §
148 |
149 | > [!WARNING]
150 | >
151 | > This rule is deprecated. See message at the beginning of the document.
152 |
153 |
154 |
155 | When a code block interrupts a flow of a sentence, the line following the
156 | code block should start with a small letter.
157 |
158 | ~~~md
159 | For example, the following line of code:
160 |
161 | ```c
162 | printf("hello from process #%d\n", procnum);
163 | ```
164 |
165 | will print `hello from process #42`.
166 | ~~~
167 |
168 |
169 |
170 | [names]: #li-a825a6f4
171 |
--------------------------------------------------------------------------------
/Go/Shell.md:
--------------------------------------------------------------------------------
1 | # AdGuard Go Team Shell scripts guidelines
2 |
3 | > [!NOTE]
4 | >
5 | > Some formatting rules are deprecated. New code should instead use `shfmt` and call `make sh-lint`.
6 |
7 | Following this document is obligatory for all new code.
8 |
9 | The rules are mostly sorted in the alphabetical order.
10 |
11 | ## Contents
12 |
13 | - [General](#shell-scripting)
14 | - [Conditionals](#shell-conditionals)
15 |
16 | ## General
17 |
18 | - § Avoid bashisms and GNUisms, prefer POSIX features only.
19 |
20 | - §
21 |
22 | > [!WARNING]
23 | >
24 | > This rule is deprecated. See message at the beginning of the document.
25 |
26 | Avoid spaces between patterns of the same `case` condition.
27 |
28 | - § Don't use the option `-q` of the command `ls`. Some systems that use the Busybox version of `ash` don't support it.
29 |
30 | - § `export` and `readonly` should be used separately from variable assignment, because otherwise failures in command substitutions won't stop the script. That is, do this:
31 |
32 | ```sh
33 | X="$(echo 42)"
34 | export X
35 | ```
36 |
37 | and **not** this:
38 |
39 | ```sh
40 | # Bad!
41 | export X="$(echo 42)"
42 | ```
43 |
44 | - § If a boolean value is needed, use `0` for `false`, and `1` for `true`.
45 |
46 | - § Mark every variable that shouldn't change later as `readonly`.
47 |
48 | - § Prefer `'raw strings'` to `"double quoted strings"` whenever possible.
49 |
50 | - §
51 |
52 | > [!WARNING]
53 | >
54 | > This rule is deprecated. See message at the beginning of the document.
55 |
56 | Put spaces within `$( cmd )`, `$(( expr ))`, and `{ cmd; }`. Avoid spaces within `${var}`.
57 |
58 | - § Put utility flags in the ASCII order and **don't** group them together. For example, `ls -1 -A -l`.
59 |
60 | - § Script code lines should not be longer than one hundred (**100**) columns. For comments, see the [text guidelines][text].
61 |
62 | - § `snake_case`, not `camelCase` for variables. `kebab-case` for filenames.
63 |
64 | - § Start scripts with the following sections in the following order:
65 |
66 | 1. Shebang.
67 | 1. Some initial documentation (optional).
68 | 1. Verbosity level parsing (optional).
69 | 1. `set` options.
70 |
71 | - § UPPERCASE names for external exported variables, lowercase for local, unexported ones.
72 |
73 | - § Use `set -e -f -u` and also `set -x` in verbose mode.
74 |
75 | - § Use the `"$var"` form instead of the `$var` form, unless word splitting is required.
76 |
77 | - § When concatenating, always use the form with curly braces to prevent accidental bad variable names. That is, `"${var}_tmp.text"` and **not** `"$var_tmp.text"`. The latter will try to lookup variable `var_tmp`.
78 |
79 | - § When concatenating, surround the whole string with quotes. That is, use this:
80 |
81 | ```sh
82 | dir="${TOP_DIR}/sub"
83 | ```
84 |
85 | and **not** this:
86 |
87 | ```sh
88 | # Bad!
89 | dir="${TOP_DIR}"/sub
90 | ```
91 |
92 | ## Conditionals
93 |
94 | Guidelines and agreements for using command `test`, also known as `[`:
95 |
96 | - § For conditionals that check for equality against multiple values, prefer `case` instead of `test`.
97 |
98 | - § Prefer the `!= ''` form instead of using `-n` to check if string is empty.
99 |
100 | - § Spell compound conditions with `&&`, `||`, and `!` **outside** of `test` instead of `-a`, `-o`, and `!` **inside** of `test` correspondingly. The latter ones are pretty much deprecated in POSIX.
101 |
102 | See also: “[Problems With the `test` Builtin: What Does `-a` Mean?][test]”.
103 |
104 | - § Use `=` for strings and `-eq` for numbers to catch typing errors.
105 |
106 | [test]: https://www.oilshell.org/blog/2017/08/31.html
107 | [text]: ./Text.md
108 |
--------------------------------------------------------------------------------
/Go/Text.md:
--------------------------------------------------------------------------------
1 | # AdGuard Go Team text and commenting guidelines
2 |
3 | Following this document is obligatory for comments and text in new code. Some of the rules aren't enforced as thoroughly or remain broken in old texts, but this is still the place to find out about what we **want** our texts to look like and how to improve them.
4 |
5 | The rules are mostly sorted in the alphabetical order.
6 |
7 | - § Avoid overly technical and markup-ey comments. Write comments for people, with full sentences and proper punctuation.
8 |
9 | - § Avoid duplication of words, even if it's grammatically correct. For example, avoid:
10 |
11 | ```none
12 | # Bad! The duplication in “that that” impedes reading.
13 | It isn't obvious that that will not cause issues.
14 | ```
15 |
16 | instead, write something like:
17 |
18 | ```none
19 | It isn't obvious that the code will not cause issues.
20 | ```
21 |
22 | - § Capitalize only the first letter in headers and titles, unless a proper name or an acronym is used. So, a header should be something like this:
23 |
24 | ```none
25 | Using the new API
26 | ```
27 |
28 | and **not** like this:
29 |
30 | ```none
31 | Using The New API
32 | ```
33 |
34 | - § Changelogs should follow the [Keep a Changelog format][keep]. The “Security” section may be put first.
35 |
36 | - § End sentences with appropriate punctuation. Do not add full stops to headers.
37 |
38 | - § Mark temporary todos—that is, todos that must be resolved or removed before publishing a change—with two exclamation signs:
39 |
40 | ```go
41 | // TODO(usr1): !! Remove this debug before pushing!
42 | ```
43 |
44 | This makes it easier to find them both during development and during code review.
45 |
46 | - § RFC links should lead to the HTMLized version on `datatracker.ietf.org`. For example:
47 |
48 | ```none
49 | https://datatracker.ietf.org/doc/html/rfc9000
50 | ```
51 |
52 | - § Set your editor to always end all your files with a newline to make sure that Unix tools [work correctly][nl].
53 |
54 | - § Set your editor to consider one tab to be four (**4**) columns wide. This is the default in most editors, and it is also a common preference among developers.
55 |
56 | - § Start sentences with a capital letter, unless the first word is a reference to a variable name that starts with a lowercase letter.
57 |
58 | - § Strive to not leave any space on the right side and properly justify the text. For example, **do not** do this:
59 |
60 | ```none
61 | This text is way too
62 | narrow and should be
63 | expanded.
64 | ```
65 |
66 | - § Text should wrap at eighty (**80**) columns to be more readable, to use a common standard, and to allow editing or diffing side-by-side without wrapping.
67 |
68 | The only exception are long hyperlinks.
69 |
70 | Don't forget to also [set the tab width][tab] in your editor's settings.
71 |
72 | - § Use U.S. English, as it is the most widely used variety of English in the code right now as well as generally.
73 |
74 | - § Double spacing between sentences may be used in code comments to make sentence borders more clear. In human-facing documentation (Markdown, etc.), prefer single spacing.
75 |
76 | - § Use the serial comma (a.k.a. Oxford comma) to improve comprehension, decrease ambiguity, and use a common standard.
77 |
78 | - § Write todos like this:
79 |
80 | ```go
81 | // TODO(usr1): Fix the frobulation issue.
82 | ```
83 |
84 | Or, if several people need to look at the code:
85 |
86 | ```go
87 | // TODO(usr1, usr2): Fix the frobulation issue.
88 | ```
89 |
90 | [keep]: https://keepachangelog.com/en/1.0.0/
91 | [nl]: https://stackoverflow.com/q/729692/1892060
92 | [tab]: #li-84467c92
93 |
--------------------------------------------------------------------------------
/Go/YAML.md:
--------------------------------------------------------------------------------
1 | # AdGuard Go Team YAML guidelines
2 |
3 | * **TODO(a.garipov):** Define naming conventions for schema names in our
4 | OpenAPI YAML file. And just generally OpenAPI conventions.
5 |
6 | * **TODO(a.garipov):** Find a YAML formatter or write our own.
7 |
8 | * §
9 | All strings, including keys, must be quoted. Reason: the “[NO-rway Law]”.
10 |
11 | * §
12 | Indent with four (**4**) spaces.
13 |
14 | * §
15 | Multiline arrays should be formatted with two spaces, one hyphen, and one
16 | space:
17 |
18 | ```yaml
19 | 'values':
20 | - 'value-1'
21 | - 'value-2'
22 | - 'value-3'
23 | ```
24 |
25 | although four spaces before a hyphen is acceptable, when a program formats a
26 | document that way:
27 |
28 | ```yaml
29 | 'values':
30 | - 'value-1'
31 | - 'value-2'
32 | - 'value-3'
33 | ```
34 |
35 | * §
36 | Prefer single quotes for strings to prevent accidental escaping, unless
37 | escaping is required or there are single quotes inside the string (e.g. for
38 | GitHub Actions).
39 |
40 | * §
41 | Use `>` for multiline strings, unless you need to keep the line breaks. Use
42 | `|` for multiline strings when you do.
43 |
44 | [NO-rway Law]: https://news.ycombinator.com/item?id=17359376
45 |
--------------------------------------------------------------------------------
/Java.md:
--------------------------------------------------------------------------------
1 |
2 | # AdGuard Java Guidelines
3 | *Version: 0.1*
4 |
5 | - [Introduction](#introduction)
6 | - [Naming Conventions](#naming-conventions)
7 | - [Comments and Documentation](#comments-and-documentation)
8 | - [General Documentation Quality](#general-documentation-quality)
9 | - [Documenting a Class](#documenting-a-class)
10 | - [Documenting a Method](#documenting-a-method)
11 | - [Inline Comments](#inline-comments)
12 | - [General Code Quality](#general-code-quality)
13 | - [Best Practices](#best-practices)
14 | - [Preconditions](#preconditions)
15 | - [Use Interfaces](#use-interfaces)
16 | - [Minimize Visibility](#minimize-visibility)
17 | - [Exceptions](#exceptions)
18 | - [Premature Optimization](#premature-optimization)
19 |
20 |
21 | ## Introduction
22 |
23 | This is a coding standard and best practices guide for Java we should use in AdGuard projects.
24 |
25 | Heavily inspired by and partly mirrors [Twitter's guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.md).
26 |
27 | ## Naming Conventions
28 |
29 | **Classes and Interfaces**
30 |
31 | * Class names should be nouns in **CamelCase**.
32 | * Use whole words and avoid acronyms and abbreviations.
33 |
34 | Examples:
35 | ```java
36 | interface Bicycle
37 | Class MountainBike implements Bicycle
38 | ```
39 | **Methods**
40 | * Methods should be verbs in **lowerCamelCase**.
41 |
42 | Examples:
43 | ```java
44 | void changeGear(int newValue);
45 | void speedUp(int increment);
46 | void applyBrakes(int decrement);
47 | ```
48 |
49 | **Variables**
50 |
51 | Variable names should be short yet meaningful. Always use **lowerCamelCase** for variable names.
52 |
53 | * Should not start with underscore(`_`) or dollar sign `$` characters.
54 | * Should be mnemonic i.e, designed to indicate to the casual observer the intent of its use.
55 | * One-character variable names should be avoided except for temporary variables.
56 | * Include units in variable names
57 |
58 | Examples:
59 | ```
60 | // Bad.
61 | long pollInterval;
62 | int fileSize;
63 |
64 | // Good.
65 | long pollIntervalMs;
66 | int fileSizeGb.
67 |
68 | // Better.
69 | // - Unit is built in to the type.
70 | // - The field is easily adaptable between units, readability is high.
71 | Amount pollInterval;
72 | Amount fileSize;
73 | ```
74 |
75 | **Constant variables**
76 |
77 | * Should be all uppercase with words separated by underscores (`_`).
78 |
79 | Examples:
80 | ```java
81 | // Some Constant variables used in predefined Float class
82 | public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
83 | public static final float NEGATIVE_INFINITY = -1.0f / 0.0f;
84 | public static final float NaN = 0.0f / 0.0f;
85 | ```
86 |
87 | ## Comments and Documentation
88 |
89 | Have you ever heard that "good code is supposed to be self-explanatory"? I'd love to find the author of this statement and tell him everything I think about it. This guy is responsible for thousands of unmaintainable projects because devs are lazy by nature and use it as excuse whenever it's possible.
90 |
91 | The problem is rather obvious: self-explanatory code only tell how it is working. It rarely tells how it should work. That's why we have some strict rules regarding code documentation and comments.
92 |
93 | The more visible a piece of code is (and by extension - the farther away consumers might be), the more documentation is needed.
94 |
95 | ### General Documentation Quality
96 |
97 | * Use [Javadoc](http://www.oracle.com/technetwork/java/javase/tech/index-137868.html)-style comments.
98 | * No author tags. The author can be always found in the commits history.
99 | * Every public class or method **must** have a comment.
100 | * Don't document overriding methods unless you want to tell what's different in its behavior.
101 |
102 | ### Documenting a Class
103 |
104 | Documentation for a class may range from a single sentence to paragraphs with code examples. Documentation should serve to disambiguate any conceptual blanks in the API, and make it easier to quickly and **correctly** use your API. A thorough class doc usually has a one sentence summary and, if necessary, a more detailed explanation.
105 |
106 | ```java
107 | /**
108 | * An RPC equivalent of a unix pipe tee. Any RPC sent to the tee input is guaranteed to have
109 | * been sent to both tee outputs before the call returns.
110 | *
111 | * @param The type of the tee'd service.
112 | */
113 | public class RpcTee {
114 | ...
115 | }
116 | ```
117 |
118 | ### Documenting a Method
119 |
120 | A method doc should tell what the method does. Depending on the argument types, it may also be important to document input format.
121 |
122 | ```java
123 | // Bad.
124 | // - The doc tells nothing that the method declaration didn't.
125 | // - This is the 'filler doc'. It would pass style checks, but doesn't help anybody.
126 | /**
127 | * Splits a string.
128 | *
129 | * @param s A string.
130 | * @return A list of strings.
131 | */
132 | List split(String s);
133 |
134 | // Better.
135 | // - We know what the method splits on.
136 | // - Still some undefined behavior.
137 | /**
138 | * Splits a string on whitespace.
139 | *
140 | * @param s The string to split. An {@code null} string is treated as an empty string.
141 | * @return A list of the whitespace-delimited parts of the input.
142 | */
143 | List split(String s);
144 |
145 | // Great.
146 | // - Covers yet another edge case.
147 | /**
148 | * Splits a string on whitespace. Repeated whitespace characters are collapsed.
149 | *
150 | * @param s The string to split. An {@code null} string is treated as an empty string.
151 | * @return A list of the whitespace-delimited parts of the input.
152 | */
153 | List split(String s);
154 | ```
155 |
156 | ### Inline Comments
157 |
158 | Inline comments should always be added when the intent or purpose of any code isn't completely explicit, but the code itself ought to be clear enough to follow logically.
159 |
160 | ```java
161 | // In a majority of cases, the controller ID will be the same as the name.
162 | // However, when a controller is manually given an ID, it will be keyed
163 | // in the collection that way. So if we don't find it, we attempt to loop
164 | // over the existing controllers and find it by classname
165 | if (controller == null) {
166 | all = controllers.items;
167 | for (i = 0; i < all.length; ++i) {
168 | cls = all[i];
169 | className = cls.getModuleClassName();
170 | if (className && className == name) {
171 | controller = cls;
172 | break;
173 | }
174 | }
175 | }
176 | ```
177 |
178 | ## General Code Quality
179 |
180 | **File organization**
181 |
182 | 1. Class (static) variables: First the public class variables, then the protected, and then the private.
183 | 2. Class (static) methods.
184 | 3. Instance variables: First public, then protected, and then private.
185 | 4. Constructors
186 | 5. Methods: list all the public methods first, and then all the private ones - that means it's easy to separate the API from the implementation, even when there's no interface involved.
187 |
188 | **Line length**
189 |
190 | Try to limit line length. This limit can be arbitrary (e.g. 80 or 100 characters) and not rigidly enforced, but the goal is to reduce the amount of horizontal scrolling for the developer.
191 |
192 | **Method and block length**
193 |
194 | Try to limit the length of method and code blocks by 50 lines so that they are not trying to do too much. Shorter methods are easier to test, and smaller sections of code are more quickly comprehended by developers.
195 |
196 | **Brackets and blocks**
197 |
198 | Always use brackets when creating code blocks of any kind. Every block, even if it is only one line, needs to have its own curly braces in order to avoid confusion and prevent the possibility of hard to track bugs.
199 |
200 | ```java
201 | // bad
202 | if (foobar) doSomething();
203 |
204 | // good
205 | if (foobar) {
206 | doSomething();
207 | }
208 | ```
209 |
210 | **Ternary operators**
211 |
212 | Ternary operators are fine for clear-cut conditionals, but unacceptable for confusing choices.
213 |
214 | ```java
215 | // bad
216 | int value = a && b ? 11 : a ? 10 : b ? 1 : 0;
217 |
218 | // good
219 | int value = isSimple ? 11 : 1;
220 | ```
221 |
222 | Ternary expressions should never be nested because they just add to the confusion.
223 |
224 | **Use final fields**
225 |
226 | Final fields are useful because they declare that a field may not be reassigned. When it comes to checking for thread-safety, a final field is one less thing that needs to be checked.
227 |
228 | **Extract constants whenever it makes sense**
229 |
230 | **Centralize duplicate logic in utility functions**
231 |
232 | ## Best Practices
233 |
234 | ### Preconditions
235 |
236 | Preconditions checks are a good practice, since they serve as a well-defined barrier against bad input from callers. As a convention, object parameters to public constructors and methods should always be checked against null, unless null is explicitly allowed.
237 |
238 | ```java
239 | // Bad.
240 | // - If the file or callback are null, the problem isn't noticed until much later.
241 | class AsyncFileReader {
242 | void readLater(File file, Closure callback) {
243 | scheduledExecutor.schedule(new Runnable() {
244 | @Override public void run() {
245 | callback.execute(readSync(file));
246 | }
247 | }, 1L, TimeUnit.HOURS);
248 | }
249 | }
250 |
251 | // Good.
252 | class AsyncFileReader {
253 | void readLater(File file, Closure callback) {
254 | checkNotNull(file);
255 | checkArgument(file.exists() && file.canRead(), "File must exist and be readable.");
256 | checkNotNull(callback);
257 |
258 | scheduledExecutor.schedule(new Runnable() {
259 | @Override public void run() {
260 | callback.execute(readSync(file));
261 | }
262 | }, 1L, TimeUnit.HOURS);
263 | }
264 | }
265 | ```
266 |
267 | ### Use Interfaces
268 |
269 | Interfaces decouple functionality from implementation, allowing you to use multiple implementations without changing consumers. Interfaces are a great way to isolate packages - provide a set of interfaces, and keep your implementations package private.
270 |
271 | Many small interfaces can seem heavyweight, since you end up with a large number of source files. Consider the pattern below as an alternative.
272 |
273 | ```java
274 | interface FileFetcher {
275 | File getFile(String name);
276 |
277 | // All the benefits of an interface, with little source management overhead.
278 | // This is particularly useful when you only expect one implementation of an interface.
279 | static class HdfsFileFetcher implements FileFetcher {
280 | @Override File getFile(String name) {
281 | ...
282 | }
283 | }
284 | }
285 | ```
286 |
287 | ### Minimize Visibility
288 |
289 | In a class API, you should support access to any methods and fields that you make accessible. Therefore, only expose what you intend the caller to use. This can be imperative when writing thread-safe code.
290 |
291 | ```java
292 | public class Parser {
293 | // Bad.
294 | // - Callers can directly access and mutate, possibly breaking internal assumptions.
295 | public Map rawFields;
296 |
297 | // Bad.
298 | // - This is probably intended to be an internal utility function.
299 | public String readConfigLine() {
300 | ..
301 | }
302 | }
303 |
304 | // Good.
305 | // - rawFields and the utility function are hidden
306 | // - The class is package-private, indicating that it should only be accessed indirectly.
307 | class Parser {
308 | private final Map rawFields;
309 |
310 | private String readConfigLine() {
311 | ..
312 | }
313 | }
314 | ```
315 |
316 | ### Exceptions
317 |
318 | **Catch narrow exceptions**
319 |
320 | Sometimes when using try/catch blocks, it may be tempting to just `catch Exception`, `Error`, or `Throwable` so you don't have to worry about what type was thrown. This is usually a bad idea, as you can end up catching more than you really wanted to deal with. For example, `catch Exception` would capture `NullPointerException`, and `catch Throwable` would capture `OutOfMemoryError`.
321 |
322 | ```java
323 | // Bad.
324 | // - If a RuntimeException happens, the program continues rather than aborting.
325 | try {
326 | storage.insertUser(user);
327 | } catch (Exception e) {
328 | LOG.error("Failed to insert user.");
329 | }
330 |
331 | try {
332 | storage.insertUser(user);
333 | } catch (StorageException e) {
334 | LOG.error("Failed to insert user.");
335 | }
336 | ```
337 |
338 | **Don't swallow exceptions**
339 |
340 | An empty catch block is usually a bad idea, as you have no signal of a problem. Coupled with narrow exception violations, it's a recipe for disaster.
341 |
342 | **When interrupted, reset thread interrupted state**
343 |
344 | Many blocking operations throw InterruptedException so that you may be awaken for events like a JVM shutdown. When catching InterruptedException, it is good practice to ensure that the thread interrupted state is preserved.
345 |
346 | IBM has a good [article](http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html) on this topic.
347 |
348 | ```java
349 | // Bad.
350 | // - Surrounding code (or higher-level code) has no idea that the thread was interrupted.
351 | try {
352 | lock.tryLock(1L, TimeUnit.SECONDS)
353 | } catch (InterruptedException e) {
354 | LOG.info("Interrupted while doing x");
355 | }
356 |
357 | // Good.
358 | // - Interrupted state is preserved.
359 | try {
360 | lock.tryLock(1L, TimeUnit.SECONDS)
361 | } catch (InterruptedException e) {
362 | LOG.info("Interrupted while doing x");
363 | Thread.currentThread().interrupt();
364 | }
365 | ```
366 |
367 | **Throw appropriate exception types**
368 |
369 | Let your API users obey the "catch narrow exceptions" rule, don't throw `Exception`. Even if you are calling another naughty API that throws `Exception`, at least hide that so it doesn't bubble up even further. You should also make an effort to hide implementation details from your callers when it comes to exceptions.
370 |
371 | ```java
372 | // Bad.
373 | // - Caller is forced to catch Exception, trapping many unnecessary types of issues.
374 | interface DataStore {
375 | String fetchValue(String key) throws Exception;
376 | }
377 |
378 | // Better.
379 | // - The interface leaks details about one specific implementation.
380 | interface DataStore {
381 | String fetchValue(String key) throws SQLException, UnknownHostException;
382 | }
383 |
384 | // Good.
385 | // - A custom exception type insulates the user from the implementation.
386 | // - Different implementations aren't forced to abuse irrelevant exception types.
387 | interface DataStore {
388 | String fetchValue(String key) throws StorageException;
389 |
390 | static class StorageException extends Exception {
391 | ...
392 | }
393 | }
394 | ```
395 |
396 | ### Premature Optimization
397 |
398 | **Premature optimization is the root of all evil.**
399 |
400 | Donald Knuth is a smart guy, and he had a few things to [say](http://c2.com/cgi/wiki?PrematureOptimization) on the topic.
401 |
402 | Unless you have strong evidence that an optimization is necessary, it's usually best to implement the un-optimized version first (possibly leaving notes about where optimizations could be made).
403 |
404 | So before you spend a week writing your memory-mapped compressed huffman-encoded hashmap, use the stock stuff first and measure.
405 |
--------------------------------------------------------------------------------
/JavaScript/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | },
5 | extends: [
6 | 'airbnb',
7 | 'plugin:jsdoc/recommended',
8 | ],
9 | rules: {
10 | 'max-len': ['error', {
11 | 'code': 120,
12 | 'comments': 120,
13 | 'tabWidth': 4,
14 | 'ignoreUrls': false,
15 | 'ignoreTrailingComments': false,
16 | 'ignoreComments': false
17 | }],
18 | 'curly': ['error', 'all'],
19 | 'brace-style': ['error', '1tbs', { 'allowSingleLine': false }],
20 | indent: ['error', 4, {
21 | SwitchCase: 1,
22 | }],
23 | },
24 | };
25 |
--------------------------------------------------------------------------------
/JavaScript/Javascript.md:
--------------------------------------------------------------------------------
1 | # AdGuard Javascript Guidelines
2 | *Version: 1.0*
3 | ## Introduction
4 |
5 | This is a coding standard and best practices guide for Javascript we should use in AdGuard projects.
6 |
7 | Heavily inspired by and partly composed of [Airbnb JavaScript Style Guide
8 | ](https://github.com/airbnb/javascript)
9 |
10 | ## Table of Contents
11 |
12 | 1. [Types](#types)
13 | 1. [References](#references)
14 | 1. [Objects](#objects)
15 | 1. [Arrays](#arrays)
16 | 1. [Destructuring](#destructuring)
17 | 1. [Strings](#strings)
18 | 1. [Functions](#functions)
19 | 1. [Arrow Functions](#arrow-functions)
20 | 1. [Classes & Constructors](#classes--constructors)
21 | 1. [Modules](#modules)
22 | 1. [Properties](#properties)
23 | 1. [Variables](#variables)
24 | 1. [Hoisting](#hoisting)
25 | 1. [Comparison Operators & Equality](#comparison-operators--equality)
26 | 1. [Blocks](#blocks)
27 | 1. [Control Statements](#control-statements)
28 | 1. [Comments](#comments)
29 | 1. [Whitespace](#whitespace)
30 | 1. [Commas](#commas)
31 | 1. [Semicolons](#semicolons)
32 | 1. [Type Casting & Coercion](#type-casting--coercion)
33 | 1. [Naming Conventions](#naming-conventions)
34 | 1. [Accessors](#accessors)
35 | 1. [Standard Library](#standard-library)
36 | 1. [Sample eslint config](#sample-eslint-config)
37 | 1. [TypeScript](#typescript)
38 | 1. [Changelog](#changelog)
39 |
40 | ## Types
41 |
42 |
43 | - [1.1](#types--primitives) **Primitives**: When you access a primitive type you work directly on its value.
44 |
45 | - `string`
46 | - `number`
47 | - `boolean`
48 | - `null`
49 | - `undefined`
50 | - `symbol`
51 | - `bigint`
52 |
53 | ```javascript
54 | const foo = 1;
55 | let bar = foo;
56 |
57 | bar = 9;
58 |
59 | console.log(foo, bar); // => 1, 9
60 | ```
61 |
62 | - Symbols and BigInts cannot be faithfully polyfilled, so they should not be used when targeting browsers/environments that don’t support them natively.
63 |
64 |
65 | - [1.2](#types--complex) **Complex**: When you access a complex type you work on a reference to its value.
66 |
67 | - `object`
68 | - `array`
69 | - `function`
70 |
71 | ```javascript
72 | const foo = [1, 2];
73 | const bar = foo;
74 |
75 | bar[0] = 9;
76 |
77 | console.log(foo[0], bar[0]); // => 9, 9
78 | ```
79 |
80 | **[⬆ back to top](#table-of-contents)**
81 |
82 | ## References
83 |
84 |
85 | - [2.1](#references--prefer-const) Use `const` for all of your references; avoid using `var`. eslint: [`prefer-const`](https://eslint.org/docs/rules/prefer-const), [`no-const-assign`](https://eslint.org/docs/rules/no-const-assign)
86 |
87 | > Why? This ensures that you can’t reassign your references, which can lead to bugs and difficult to comprehend code.
88 |
89 | ```javascript
90 | // bad
91 | var a = 1;
92 | var b = 2;
93 |
94 | // good
95 | const a = 1;
96 | const b = 2;
97 | ```
98 |
99 |
100 | - [2.2](#references--disallow-var) If you must reassign references, use `let` instead of `var`. eslint: [`no-var`](https://eslint.org/docs/rules/no-var)
101 |
102 | > Why? `let` is block-scoped rather than function-scoped like `var`.
103 |
104 | ```javascript
105 | // bad
106 | var count = 1;
107 | if (true) {
108 | count += 1;
109 | }
110 |
111 | // good, use the let.
112 | let count = 1;
113 | if (true) {
114 | count += 1;
115 | }
116 | ```
117 |
118 |
119 | - [2.3](#references--block-scope) Note that both `let` and `const` are block-scoped, whereas `var` is function-scoped.
120 |
121 | ```javascript
122 | // const and let only exist in the blocks they are defined in.
123 | {
124 | let a = 1;
125 | const b = 1;
126 | var c = 1;
127 | }
128 | console.log(a); // ReferenceError
129 | console.log(b); // ReferenceError
130 | console.log(c); // Prints 1
131 | ```
132 |
133 | In the above code, you can see that referencing `a` and `b` will produce a ReferenceError, while `c` contains the number. This is because `a` and `b` are block scoped, while `c` is scoped to the containing function.
134 |
135 | **[⬆ back to top](#table-of-contents)**
136 |
137 | ## Objects
138 |
139 |
140 | - [3.1](#objects--quoted-props) Only quote properties that are invalid identifiers. eslint: [`quote-props`](https://eslint.org/docs/rules/quote-props)
141 |
142 | > Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines.
143 |
144 | ```javascript
145 | // bad
146 | const bad = {
147 | 'foo': 3,
148 | 'bar': 4,
149 | 'data-blah': 5,
150 | };
151 |
152 | // good
153 | const good = {
154 | foo: 3,
155 | bar: 4,
156 | 'data-blah': 5,
157 | };
158 | ```
159 |
160 |
161 | - [3.2](#objects--prototype-builtins) Do not call `Object.prototype` methods directly, such as `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf`. eslint: [`no-prototype-builtins`](https://eslint.org/docs/rules/no-prototype-builtins)
162 |
163 | > Why? These methods may be shadowed by properties on the object in question - consider `{ hasOwnProperty: false }` - or, the object may be a null object (`Object.create(null)`).
164 |
165 | ```javascript
166 | // bad
167 | console.log(object.hasOwnProperty(key));
168 |
169 | // good
170 | console.log(Object.prototype.hasOwnProperty.call(object, key));
171 |
172 | // best
173 | const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
174 | console.log(has.call(object, key));
175 | /* or */
176 | import has from 'has'; // https://www.npmjs.com/package/has
177 | console.log(has(object, key));
178 | ```
179 |
180 |
181 | - [3.3](#objects--rest-spread) Prefer the object spread syntax over [`Object.assign`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to shallow-copy objects. Use the object rest parameter syntax to get a new object with certain properties omitted. eslint: [`prefer-object-spread`](https://eslint.org/docs/rules/prefer-object-spread)
182 |
183 | ```javascript
184 | // very bad
185 | const original = { a: 1, b: 2 };
186 | const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
187 | delete copy.a; // so does this
188 |
189 | // bad
190 | const original = { a: 1, b: 2 };
191 | const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
192 |
193 | // good
194 | const original = { a: 1, b: 2 };
195 | const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
196 |
197 | const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
198 | ```
199 |
200 | **[⬆ back to top](#table-of-contents)**
201 |
202 | ## Arrays
203 |
204 |
205 | - [4.1](#es6-array-spreads) Use array spreads `...` to copy arrays.
206 |
207 | ```javascript
208 | // bad
209 | const len = items.length;
210 | const itemsCopy = [];
211 | let i;
212 |
213 | for (i = 0; i < len; i += 1) {
214 | itemsCopy[i] = items[i];
215 | }
216 |
217 | // good
218 | const itemsCopy = [...items];
219 | ```
220 |
221 |
222 | - [4.2](#arrays--from-iterable) To convert an iterable object to an array, use spreads `...` instead of [`Array.from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from)
223 |
224 | ```javascript
225 | const foo = document.querySelectorAll('.foo');
226 |
227 | // good
228 | const nodes = Array.from(foo);
229 |
230 | // best
231 | const nodes = [...foo];
232 | ```
233 |
234 |
235 | - [4.3](#arrays--from-array-like) Use [`Array.from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) for converting an array-like object to an array.
236 |
237 | ```javascript
238 | const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
239 |
240 | // bad
241 | const arr = Array.prototype.slice.call(arrLike);
242 |
243 | // good
244 | const arr = Array.from(arrLike);
245 | ```
246 |
247 |
248 | - [4.4](#arrays--mapping) Use [`Array.from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) instead of spread `...` for mapping over iterables, because it avoids creating an intermediate array.
249 |
250 | ```javascript
251 | // bad
252 | const baz = [...foo].map(bar);
253 |
254 | // good
255 | const baz = Array.from(foo, bar);
256 | ```
257 |
258 |
259 | - [4.5](#arrays--callback-return) Use return statements in array method callbacks. It’s ok to omit the return if the function body consists of a single statement returning an expression without side effects. eslint: [`array-callback-return`](https://eslint.org/docs/rules/array-callback-return)
260 |
261 | ```javascript
262 | // good
263 | [1, 2, 3].map((x) => {
264 | const y = x + 1;
265 | return x * y;
266 | });
267 |
268 | // good
269 | [1, 2, 3].map((x) => x + 1);
270 |
271 | // bad - no returned value means `acc` becomes undefined after the first iteration
272 | [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
273 | const flatten = acc.concat(item);
274 | });
275 |
276 | // good
277 | [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
278 | const flatten = acc.concat(item);
279 | return flatten;
280 | });
281 | ```
282 |
283 | **[⬆ back to top](#table-of-contents)**
284 |
285 | ## Destructuring
286 |
287 |
288 | - [5.1](#destructuring--object) Use object destructuring when accessing and using multiple properties of an object. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring)
289 |
290 | > Why? Destructuring saves you from creating temporary references for those properties, and from repetitive access of the object. Repeating object access creates more repetitive code, requires more reading, and creates more opportunities for mistakes. Destructuring objects also provides a single site of definition of the object structure that is used in the block, rather than requiring reading the entire block to determine what is used.
291 |
292 | ```javascript
293 | // bad
294 | function getFullName(user) {
295 | const firstName = user.firstName;
296 | const lastName = user.lastName;
297 |
298 | return `${firstName} ${lastName}`;
299 | }
300 |
301 | // good
302 | function getFullName(user) {
303 | const { firstName, lastName } = user;
304 | return `${firstName} ${lastName}`;
305 | }
306 |
307 | // best
308 | function getFullName({ firstName, lastName }) {
309 | return `${firstName} ${lastName}`;
310 | }
311 | ```
312 |
313 |
314 | - [5.2](#destructuring--array) Use array destructuring. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring)
315 |
316 | ```javascript
317 | const arr = [1, 2, 3, 4];
318 |
319 | // bad
320 | const first = arr[0];
321 | const second = arr[1];
322 |
323 | // good
324 | const [first, second] = arr;
325 | ```
326 |
327 |
328 | - [5.3](#destructuring--object-over-array) Use object destructuring for multiple return values, not array destructuring.
329 |
330 | > Why? You can add new properties over time or change the order of things without breaking call sites.
331 |
332 | ```javascript
333 | // bad
334 | function processInput(input) {
335 | // then a miracle occurs
336 | return [left, right, top, bottom];
337 | }
338 |
339 | // the caller needs to think about the order of return data
340 | const [left, __, top] = processInput(input);
341 |
342 | // good
343 | function processInput(input) {
344 | // then a miracle occurs
345 | return { left, right, top, bottom };
346 | }
347 |
348 | // the caller selects only the data they need
349 | const { left, top } = processInput(input);
350 | ```
351 |
352 | **[⬆ back to top](#table-of-contents)**
353 |
354 | ## Strings
355 |
356 |
357 | - [6.1](#strings--quotes) Use single quotes `''` for strings. eslint: [`quotes`](https://eslint.org/docs/rules/quotes)
358 |
359 | ```javascript
360 | // bad
361 | const name = "Capt. Janeway";
362 |
363 | // bad - template literals should contain interpolation or newlines
364 | const name = `Capt. Janeway`;
365 |
366 | // good
367 | const name = 'Capt. Janeway';
368 | ```
369 |
370 |
371 | - [6.2](#strings--line-length) Strings that cause the line to go over 120 characters should not be written across multiple lines using string concatenation.
372 |
373 | > Why? Broken strings are painful to work with and make code less searchable.
374 |
375 | ```javascript
376 | // bad
377 | const errorMessage = 'This is a super long error that was thrown because \
378 | of Batman. When you stop to think about how Batman had anything to do \
379 | with this, you would get nowhere \
380 | fast.';
381 |
382 | // bad
383 | const errorMessage = 'This is a super long error that was thrown because ' +
384 | 'of Batman. When you stop to think about how Batman had anything to do ' +
385 | 'with this, you would get nowhere fast.';
386 |
387 | // good
388 | const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
389 | ```
390 |
391 |
392 | - [6.3](#es6-template-literals) When programmatically building up strings, use template strings instead of concatenation. eslint: [`prefer-template`](https://eslint.org/docs/rules/prefer-template) [`template-curly-spacing`](https://eslint.org/docs/rules/template-curly-spacing)
393 |
394 | > Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.
395 |
396 | ```javascript
397 | // bad
398 | function sayHi(name) {
399 | return 'How are you, ' + name + '?';
400 | }
401 |
402 | // bad
403 | function sayHi(name) {
404 | return ['How are you, ', name, '?'].join();
405 | }
406 |
407 | // bad
408 | function sayHi(name) {
409 | return `How are you, ${ name }?`;
410 | }
411 |
412 | // good
413 | function sayHi(name) {
414 | return `How are you, ${name}?`;
415 | }
416 | ```
417 |
418 |
419 | - [6.4](#strings--eval) Never use `eval()` on a string, it opens too many vulnerabilities. eslint: [`no-eval`](https://eslint.org/docs/rules/no-eval)
420 |
421 |
422 | - [6.5](#strings--escaping) Do not unnecessarily escape characters in strings. eslint: [`no-useless-escape`](https://eslint.org/docs/rules/no-useless-escape)
423 |
424 | > Why? Backslashes harm readability, thus they should only be present when necessary.
425 |
426 | ```javascript
427 | // bad
428 | const foo = '\'this\' \i\s \"quoted\"';
429 |
430 | // good
431 | const foo = '\'this\' is "quoted"';
432 | const foo = `my name is '${name}'`;
433 | ```
434 |
435 | **[⬆ back to top](#table-of-contents)**
436 |
437 | ## Functions
438 |
439 |
440 | - [7.1](#functions--iife) Wrap immediately invoked function expressions in parentheses. eslint: [`wrap-iife`](https://eslint.org/docs/rules/wrap-iife)
441 |
442 | > Why? An immediately invoked function expression is a single unit - wrapping both it, and its invocation parens, in parens, cleanly expresses this. Note that in a world with modules everywhere, you almost never need an IIFE.
443 |
444 | ```javascript
445 | // immediately-invoked function expression (IIFE)
446 | (function () {
447 | console.log('Welcome to the Internet. Please follow me.');
448 | }());
449 | ```
450 |
451 |
452 | - [7.2](#functions--in-blocks) Never declare a function in a non-function block (`if`, `while`, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. eslint: [`no-loop-func`](https://eslint.org/docs/rules/no-loop-func)
453 |
454 |
455 | - [7.3](#functions--arguments-shadow) Never name a parameter `arguments`. This will take precedence over the `arguments` object that is given to every function scope.
456 |
457 | ```javascript
458 | // bad
459 | function foo(name, options, arguments) {
460 | // ...
461 | }
462 |
463 | // good
464 | function foo(name, options, args) {
465 | // ...
466 | }
467 | ```
468 |
469 |
470 | - [7.4](#es6-rest) Never use `arguments`, opt to use rest syntax `...` instead. eslint: [`prefer-rest-params`](https://eslint.org/docs/rules/prefer-rest-params)
471 |
472 | > Why? `...` is explicit about which arguments you want pulled. Plus, rest arguments are a real Array, and not merely Array-like like `arguments`.
473 |
474 | ```javascript
475 | // bad
476 | function concatenateAll() {
477 | const args = Array.prototype.slice.call(arguments);
478 | return args.join('');
479 | }
480 |
481 | // good
482 | function concatenateAll(...args) {
483 | return args.join('');
484 | }
485 | ```
486 |
487 |
488 | - [7.5](#es6-default-parameters) Use default parameter syntax rather than mutating function arguments.
489 |
490 | ```javascript
491 | // really bad
492 | function handleThings(opts) {
493 | // No! We shouldn’t mutate function arguments.
494 | // Double bad: if opts is falsy it'll be set to an object which may
495 | // be what you want but it can introduce subtle bugs.
496 | opts = opts || {};
497 | // ...
498 | }
499 |
500 | // still bad
501 | function handleThings(opts) {
502 | if (opts === void 0) {
503 | opts = {};
504 | }
505 | // ...
506 | }
507 |
508 | // good
509 | function handleThings(opts = {}) {
510 | // ...
511 | }
512 | ```
513 |
514 |
515 | - [7.6](#functions--default-side-effects) Avoid side effects with default parameters.
516 |
517 | > Why? They are confusing to reason about.
518 |
519 | ```javascript
520 | var b = 1;
521 | // bad
522 | function count(a = b++) {
523 | console.log(a);
524 | }
525 | count(); // 1
526 | count(); // 2
527 | count(3); // 3
528 | count(); // 3
529 | ```
530 |
531 |
532 | - [7.7](#functions--defaults-last) Always put default parameters last. eslint: [`default-param-last`](https://eslint.org/docs/rules/default-param-last)
533 |
534 | ```javascript
535 | // bad
536 | function handleThings(opts = {}, name) {
537 | // ...
538 | }
539 |
540 | // good
541 | function handleThings(name, opts = {}) {
542 | // ...
543 | }
544 | ```
545 |
546 |
547 | - [7.8](#functions--constructor) Never use the Function constructor to create a new function. eslint: [`no-new-func`](https://eslint.org/docs/rules/no-new-func)
548 |
549 | > Why? Creating a function in this way evaluates a string similarly to `eval()`, which opens vulnerabilities.
550 |
551 | ```javascript
552 | // bad
553 | var add = new Function('a', 'b', 'return a + b');
554 |
555 | // still bad
556 | var subtract = Function('a', 'b', 'return a - b');
557 | ```
558 |
559 |
560 | - [7.9](#functions--signature-spacing) Spacing in a function signature. eslint: [`space-before-function-paren`](https://eslint.org/docs/rules/space-before-function-paren) [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks)
561 |
562 | > Why? Consistency is good, and you shouldn’t have to add or remove a space when adding or removing a name.
563 |
564 | ```javascript
565 | // bad
566 | const f = function(){};
567 | const g = function (){};
568 | const h = function() {};
569 |
570 | // good
571 | const x = function () {};
572 | const y = function a() {};
573 | ```
574 |
575 |
576 | - [7.10](#functions--mutate-params) Never mutate parameters. eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign)
577 |
578 | > Why? Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller.
579 |
580 | ```javascript
581 | // bad
582 | function f1(obj) {
583 | obj.key = 1;
584 | }
585 |
586 | // good
587 | function f2(obj) {
588 | const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
589 | }
590 | ```
591 |
592 |
593 | - [7.11](#functions--reassign-params) Never reassign parameters. eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign)
594 |
595 | > Why? Reassigning parameters can lead to unexpected behavior, especially when accessing the `arguments` object. It can also cause optimization issues, especially in V8.
596 |
597 | ```javascript
598 | // bad
599 | function f1(a) {
600 | a = 1;
601 | // ...
602 | }
603 |
604 | function f2(a) {
605 | if (!a) { a = 1; }
606 | // ...
607 | }
608 |
609 | // good
610 | function f3(a) {
611 | const b = a || 1;
612 | // ...
613 | }
614 |
615 | function f4(a = 1) {
616 | // ...
617 | }
618 | ```
619 |
620 |
621 | - [7.12](#functions--spread-vs-apply) Prefer the use of the spread syntax `...` to call variadic functions. eslint: [`prefer-spread`](https://eslint.org/docs/rules/prefer-spread)
622 |
623 | > Why? It’s cleaner, you don’t need to supply a context, and you can not easily compose `new` with `apply`.
624 |
625 | ```javascript
626 | // bad
627 | const x = [1, 2, 3, 4, 5];
628 | console.log.apply(console, x);
629 |
630 | // good
631 | const x = [1, 2, 3, 4, 5];
632 | console.log(...x);
633 |
634 | // bad
635 | new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
636 |
637 | // good
638 | new Date(...[2016, 8, 5]);
639 | ```
640 |
641 |
642 | - [7.13](#functions--signature-invocation-indentation) Functions with multiline signatures, or invocations, should be indented just like every other multiline list in this guide: with each item on a line by itself, with a trailing comma on the last item. eslint: [`function-paren-newline`](https://eslint.org/docs/rules/function-paren-newline)
643 |
644 | ```javascript
645 | // bad
646 | function foo(bar,
647 | baz,
648 | quux) {
649 | // ...
650 | }
651 |
652 | // good
653 | function foo(
654 | bar,
655 | baz,
656 | quux,
657 | ) {
658 | // ...
659 | }
660 |
661 | // bad
662 | console.log(foo,
663 | bar,
664 | baz);
665 |
666 | // good
667 | console.log(
668 | foo,
669 | bar,
670 | baz,
671 | );
672 | ```
673 |
674 | **[⬆ back to top](#table-of-contents)**
675 |
676 | ## Arrow Functions
677 |
678 |
679 | - [8.1](#arrows--use-them) When you must use an anonymous function (as when passing an inline callback), use arrow function notation. eslint: [`prefer-arrow-callback`](https://eslint.org/docs/rules/prefer-arrow-callback), [`arrow-spacing`](https://eslint.org/docs/rules/arrow-spacing)
680 |
681 | > Why? It creates a version of the function that executes in the context of `this`, which is usually what you want, and is a more concise syntax.
682 |
683 | > Why not? If you have a fairly complicated function, you might move that logic out into its own named function expression.
684 |
685 | ```javascript
686 | // bad
687 | [1, 2, 3].map(function (x) {
688 | const y = x + 1;
689 | return x * y;
690 | });
691 |
692 | // good
693 | [1, 2, 3].map((x) => {
694 | const y = x + 1;
695 | return x * y;
696 | });
697 | ```
698 |
699 |
700 | - [8.2](#arrows--one-arg-parens) Always include parentheses around arguments for clarity and consistency. eslint: [`arrow-parens`](https://eslint.org/docs/rules/arrow-parens)
701 |
702 | > Why? Minimizes diff churn when adding or removing arguments.
703 |
704 | ```javascript
705 | // bad
706 | [1, 2, 3].map(x => x * x);
707 |
708 | // good
709 | [1, 2, 3].map((x) => x * x);
710 |
711 | // bad
712 | [1, 2, 3].map(number => (
713 | `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
714 | ));
715 |
716 | // good
717 | [1, 2, 3].map((number) => (
718 | `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
719 | ));
720 |
721 | // bad
722 | [1, 2, 3].map(x => {
723 | const y = x + 1;
724 | return x * y;
725 | });
726 |
727 | // good
728 | [1, 2, 3].map((x) => {
729 | const y = x + 1;
730 | return x * y;
731 | });
732 | ```
733 |
734 |
735 | - [8.3](#whitespace--implicit-arrow-linebreak) Enforce the location of arrow function bodies with implicit returns. eslint: [`implicit-arrow-linebreak`](https://eslint.org/docs/rules/implicit-arrow-linebreak)
736 |
737 | ```javascript
738 | // bad
739 | (foo) =>
740 | bar;
741 |
742 | (foo) =>
743 | (bar);
744 |
745 | // good
746 | (foo) => bar;
747 | (foo) => (bar);
748 | (foo) => (
749 | bar
750 | )
751 | ```
752 |
753 | **[⬆ back to top](#table-of-contents)**
754 |
755 | ## Classes & Constructors
756 |
757 |
758 | - [9.1](#constructors--use-class) Always use `class`. Avoid manipulating `prototype` directly.
759 |
760 | > Why? `class` syntax is more concise and easier to reason about.
761 |
762 | ```javascript
763 | // bad
764 | function Queue(contents = []) {
765 | this.queue = [...contents];
766 | }
767 | Queue.prototype.pop = function () {
768 | const value = this.queue[0];
769 | this.queue.splice(0, 1);
770 | return value;
771 | };
772 |
773 | // good
774 | class Queue {
775 | constructor(contents = []) {
776 | this.queue = [...contents];
777 | }
778 | pop() {
779 | const value = this.queue[0];
780 | this.queue.splice(0, 1);
781 | return value;
782 | }
783 | }
784 | ```
785 |
786 |
787 | - [9.2](#constructors--extends) Use `extends` for inheritance.
788 |
789 | > Why? It is a built-in way to inherit prototype functionality without breaking `instanceof`.
790 |
791 | ```javascript
792 | // bad
793 | const inherits = require('inherits');
794 | function PeekableQueue(contents) {
795 | Queue.apply(this, contents);
796 | }
797 | inherits(PeekableQueue, Queue);
798 | PeekableQueue.prototype.peek = function () {
799 | return this.queue[0];
800 | };
801 |
802 | // good
803 | class PeekableQueue extends Queue {
804 | peek() {
805 | return this.queue[0];
806 | }
807 | }
808 | ```
809 |
810 |
811 | - [9.3](#constructors--chaining) Methods can return `this` to help with method chaining.
812 |
813 | ```javascript
814 | // bad
815 | Jedi.prototype.jump = function () {
816 | this.jumping = true;
817 | return true;
818 | };
819 |
820 | Jedi.prototype.setHeight = function (height) {
821 | this.height = height;
822 | };
823 |
824 | const luke = new Jedi();
825 | luke.jump(); // => true
826 | luke.setHeight(20); // => undefined
827 |
828 | // good
829 | class Jedi {
830 | jump() {
831 | this.jumping = true;
832 | return this;
833 | }
834 |
835 | setHeight(height) {
836 | this.height = height;
837 | return this;
838 | }
839 | }
840 |
841 | const luke = new Jedi();
842 |
843 | luke.jump()
844 | .setHeight(20);
845 | ```
846 |
847 |
848 | - [9.4](#constructors--no-useless) Classes have a default constructor if one is not specified. An empty constructor function or one that just delegates to a parent class is unnecessary. eslint: [`no-useless-constructor`](https://eslint.org/docs/rules/no-useless-constructor)
849 |
850 | ```javascript
851 | // bad
852 | class Jedi {
853 | constructor() {}
854 |
855 | getName() {
856 | return this.name;
857 | }
858 | }
859 |
860 | // bad
861 | class Rey extends Jedi {
862 | constructor(...args) {
863 | super(...args);
864 | }
865 | }
866 |
867 | // good
868 | class Rey extends Jedi {
869 | constructor(...args) {
870 | super(...args);
871 | this.name = 'Rey';
872 | }
873 | }
874 | ```
875 |
876 |
877 | - [9.5](#classes--methods-use-this) Class methods should use `this` or be made into a static method unless an external library or framework requires using specific non-static methods. Being an instance method should indicate that it behaves differently based on properties of the receiver. eslint: [`class-methods-use-this`](https://eslint.org/docs/rules/class-methods-use-this)
878 |
879 | ```javascript
880 | // bad
881 | class Foo {
882 | bar() {
883 | console.log('bar');
884 | }
885 | }
886 |
887 | // good - this is used
888 | class Foo {
889 | bar() {
890 | console.log(this.bar);
891 | }
892 | }
893 |
894 | // good - constructor is exempt
895 | class Foo {
896 | constructor() {
897 | // ...
898 | }
899 | }
900 |
901 | // good - static methods aren't expected to use this
902 | class Foo {
903 | static bar() {
904 | console.log('bar');
905 | }
906 | }
907 | ```
908 |
909 | **[⬆ back to top](#table-of-contents)**
910 |
911 | ## Modules
912 |
913 |
914 | - [10.1](#modules--use-them) Always use modules (`import`/`export`) over a non-standard module system. You can always transpile to your preferred module system.
915 |
916 | > Why? Modules are the future, let’s start using the future now.
917 |
918 | ```javascript
919 | // bad
920 | const AirbnbStyleGuide = require('./AirbnbStyleGuide');
921 | module.exports = AirbnbStyleGuide.es6;
922 |
923 | // ok
924 | import AirbnbStyleGuide from './AirbnbStyleGuide';
925 | export default AirbnbStyleGuide.es6;
926 |
927 | // best
928 | import { es6 } from './AirbnbStyleGuide';
929 | export default es6;
930 | ```
931 |
932 |
933 | - [10.2](#modules--no-wildcard) Do not use wildcard imports.
934 |
935 | > Why? This makes sure you have a single default export.
936 |
937 | ```javascript
938 | // bad
939 | import * as AirbnbStyleGuide from './AirbnbStyleGuide';
940 |
941 | // good
942 | import AirbnbStyleGuide from './AirbnbStyleGuide';
943 | ```
944 |
945 |
946 | - [10.3](#modules--no-export-from-import) And do not export directly from an import.
947 |
948 | > Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent.
949 |
950 | ```javascript
951 | // bad
952 | // filename es6.js
953 | export { es6 as default } from './AirbnbStyleGuide';
954 |
955 | // good
956 | // filename es6.js
957 | import { es6 } from './AirbnbStyleGuide';
958 | export default es6;
959 | ```
960 |
961 |
962 | - [10.4](#modules--no-duplicate-imports) Only import from a path in one place.
963 | eslint: [`no-duplicate-imports`](https://eslint.org/docs/rules/no-duplicate-imports)
964 | > Why? Having multiple lines that import from the same path can make code harder to maintain.
965 |
966 | ```javascript
967 | // bad
968 | import foo from 'foo';
969 | // … some other imports … //
970 | import { named1, named2 } from 'foo';
971 |
972 | // good
973 | import foo, { named1, named2 } from 'foo';
974 |
975 | // good
976 | import foo, {
977 | named1,
978 | named2,
979 | } from 'foo';
980 | ```
981 |
982 |
983 | - [10.5](#modules--no-mutable-exports) Do not export mutable bindings.
984 | eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md)
985 | > Why? Mutation should be avoided in general, but in particular when exporting mutable bindings. While this technique may be needed for some special cases, in general, only constant references should be exported.
986 |
987 | ```javascript
988 | // bad
989 | let foo = 3;
990 | export { foo };
991 |
992 | // good
993 | const foo = 3;
994 | export { foo };
995 | ```
996 |
997 |
998 | - [10.6](#modules--prefer-named-export) Prefer named export over default export.
999 |
1000 | ```javascript
1001 | // bad
1002 | export default function foo() {}
1003 |
1004 | // good
1005 | export function foo() {}
1006 | ```
1007 |
1008 |
1009 | - [10.7](#modules--imports-first) Put all `import`s above non-import statements.
1010 | eslint: [`import/first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md)
1011 | > Why? Since `import`s are hoisted, keeping them all at the top prevents surprising behavior.
1012 |
1013 | ```javascript
1014 | // bad
1015 | import foo from 'foo';
1016 | foo.init();
1017 |
1018 | import bar from 'bar';
1019 |
1020 | // good
1021 | import foo from 'foo';
1022 | import bar from 'bar';
1023 |
1024 | foo.init();
1025 | ```
1026 |
1027 |
1028 | - [10.8](#modules--multiline-imports-over-newlines) Multiline imports should be indented just like multiline array and object literals.
1029 | eslint: [`object-curly-newline`](https://eslint.org/docs/rules/object-curly-newline)
1030 |
1031 | > Why? The curly braces follow the same indentation rules as every other curly brace block in the style guide, as do the trailing commas.
1032 |
1033 | ```javascript
1034 | // bad
1035 | import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
1036 |
1037 | // good
1038 | import {
1039 | longNameA,
1040 | longNameB,
1041 | longNameC,
1042 | longNameD,
1043 | longNameE,
1044 | } from 'path';
1045 | ```
1046 |
1047 |
1048 | - [10.9](#modules--import-extensions) Do not include JavaScript filename extensions
1049 | eslint: [`import/extensions`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md)
1050 | > Why? Including extensions inhibits refactoring, and inappropriately hardcodes implementation details of the module you're importing in every consumer.
1051 |
1052 | ```javascript
1053 | // bad
1054 | import foo from './foo.js';
1055 | import bar from './bar.jsx';
1056 | import baz from './baz/index.jsx';
1057 |
1058 | // good
1059 | import foo from './foo';
1060 | import bar from './bar';
1061 | import baz from './baz';
1062 | ```
1063 |
1064 | **[⬆ back to top](#table-of-contents)**
1065 |
1066 | ## Properties
1067 |
1068 |
1069 | - [11.1](#properties--dot) Use dot notation when accessing properties. eslint: [`dot-notation`](https://eslint.org/docs/rules/dot-notation)
1070 |
1071 | ```javascript
1072 | const luke = {
1073 | jedi: true,
1074 | age: 28,
1075 | };
1076 |
1077 | // bad
1078 | const isJedi = luke['jedi'];
1079 |
1080 | // good
1081 | const isJedi = luke.jedi;
1082 | ```
1083 |
1084 |
1085 | - [11.2](#properties--bracket) Use bracket notation `[]` when accessing properties with a variable.
1086 |
1087 | ```javascript
1088 | const luke = {
1089 | jedi: true,
1090 | age: 28,
1091 | };
1092 |
1093 | function getProp(prop) {
1094 | return luke[prop];
1095 | }
1096 |
1097 | const isJedi = getProp('jedi');
1098 | ```
1099 |
1100 |
1101 | - [11.3](#es2016-properties--exponentiation-operator) Use exponentiation operator `**` when calculating exponentiations. eslint: [`no-restricted-properties`](https://eslint.org/docs/rules/no-restricted-properties).
1102 |
1103 | ```javascript
1104 | // bad
1105 | const binary = Math.pow(2, 10);
1106 |
1107 | // good
1108 | const binary = 2 ** 10;
1109 | ```
1110 |
1111 | **[⬆ back to top](#table-of-contents)**
1112 |
1113 | ## Variables
1114 |
1115 |
1116 | - [12.1](#variables--const) Always use `const` or `let` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. eslint: [`no-undef`](https://eslint.org/docs/rules/no-undef) [`prefer-const`](https://eslint.org/docs/rules/prefer-const)
1117 |
1118 | ```javascript
1119 | // bad
1120 | superPower = new SuperPower();
1121 |
1122 | // good
1123 | const superPower = new SuperPower();
1124 | ```
1125 |
1126 |
1127 | - [12.2](#variables--one-const) Use one `const` or `let` declaration per variable or assignment. eslint: [`one-var`](https://eslint.org/docs/rules/one-var)
1128 |
1129 | > Why? It’s easier to add new variable declarations this way, and you never have to worry about swapping out a `;` for a `,` or introducing punctuation-only diffs. You can also step through each declaration with the debugger, instead of jumping through all of them at once.
1130 |
1131 | ```javascript
1132 | // bad
1133 | const items = getItems(),
1134 | goSportsTeam = true,
1135 | dragonball = 'z';
1136 |
1137 | // bad
1138 | // (compare to above, and try to spot the mistake)
1139 | const items = getItems(),
1140 | goSportsTeam = true;
1141 | dragonball = 'z';
1142 |
1143 | // good
1144 | const items = getItems();
1145 | const goSportsTeam = true;
1146 | const dragonball = 'z';
1147 | ```
1148 |
1149 |
1150 | - [12.3](#variables--define-where-used) Assign variables where you need them, but place them in a reasonable place.
1151 |
1152 | > Why? `let` and `const` are block scoped and not function scoped.
1153 |
1154 | ```javascript
1155 | // bad - unnecessary function call
1156 | function checkName(hasName) {
1157 | const name = getName();
1158 |
1159 | if (hasName === 'test') {
1160 | return false;
1161 | }
1162 |
1163 | if (name === 'test') {
1164 | this.setName('');
1165 | return false;
1166 | }
1167 |
1168 | return name;
1169 | }
1170 |
1171 | // good
1172 | function checkName(hasName) {
1173 | if (hasName === 'test') {
1174 | return false;
1175 | }
1176 |
1177 | const name = getName();
1178 |
1179 | if (name === 'test') {
1180 | this.setName('');
1181 | return false;
1182 | }
1183 |
1184 | return name;
1185 | }
1186 | ```
1187 |
1188 |
1189 | - [12.4](#variables--no-chain-assignment) Don’t chain variable assignments. eslint: [`no-multi-assign`](https://eslint.org/docs/rules/no-multi-assign)
1190 |
1191 | > Why? Chaining variable assignments creates implicit global variables.
1192 |
1193 | ```javascript
1194 | // bad
1195 | (function example() {
1196 | // JavaScript interprets this as
1197 | // let a = ( b = ( c = 1 ) );
1198 | // The let keyword only applies to variable a; variables b and c become
1199 | // global variables.
1200 | let a = b = c = 1;
1201 | }());
1202 |
1203 | console.log(a); // throws ReferenceError
1204 | console.log(b); // 1
1205 | console.log(c); // 1
1206 |
1207 | // good
1208 | (function example() {
1209 | let a = 1;
1210 | let b = a;
1211 | let c = a;
1212 | }());
1213 |
1214 | console.log(a); // throws ReferenceError
1215 | console.log(b); // throws ReferenceError
1216 | console.log(c); // throws ReferenceError
1217 |
1218 | // the same applies for `const`
1219 | ```
1220 |
1221 |
1222 | - [12.5](#variables--unary-increment-decrement) Avoid using unary increments and decrements (`++`, `--`). eslint [`no-plusplus`](https://eslint.org/docs/rules/no-plusplus)
1223 |
1224 | > Why? Per the eslint documentation, unary increment and decrement statements are subject to automatic semicolon insertion and can cause silent errors with incrementing or decrementing values within an application. It is also more expressive to mutate your values with statements like `num += 1` instead of `num++` or `num ++`. Disallowing unary increment and decrement statements also prevents you from pre-incrementing/pre-decrementing values unintentionally which can also cause unexpected behavior in your programs.
1225 |
1226 | ```javascript
1227 | // bad
1228 |
1229 | const array = [1, 2, 3];
1230 | let num = 1;
1231 | num++;
1232 | --num;
1233 |
1234 | let sum = 0;
1235 | let truthyCount = 0;
1236 | for (let i = 0; i < array.length; i++) {
1237 | let value = array[i];
1238 | sum += value;
1239 | if (value) {
1240 | truthyCount++;
1241 | }
1242 | }
1243 |
1244 | // good
1245 |
1246 | const array = [1, 2, 3];
1247 | let num = 1;
1248 | num += 1;
1249 | num -= 1;
1250 |
1251 | const sum = array.reduce((a, b) => a + b, 0);
1252 | const truthyCount = array.filter(Boolean).length;
1253 | ```
1254 |
1255 |
1256 | - [12.6](#variables--linebreak) Avoid linebreaks before or after `=` in an assignment. If your assignment violates [`max-len`](https://eslint.org/docs/rules/max-len), surround the value in parens. eslint [`operator-linebreak`](https://eslint.org/docs/rules/operator-linebreak).
1257 |
1258 | > Why? Linebreaks surrounding `=` can obfuscate the value of an assignment.
1259 |
1260 | ```javascript
1261 | // bad
1262 | const foo =
1263 | superLongLongLongLongLongLongLongLongFunctionName();
1264 |
1265 | // bad
1266 | const foo
1267 | = 'superLongLongLongLongLongLongLongLongString';
1268 |
1269 | // good
1270 | const foo = (
1271 | superLongLongLongLongLongLongLongLongFunctionName()
1272 | );
1273 |
1274 | // good
1275 | const foo = 'superLongLongLongLongLongLongLongLongString';
1276 | ```
1277 |
1278 |
1279 | - [12.7](#variables--no-unused-vars) Disallow unused variables. eslint: [`no-unused-vars`](https://eslint.org/docs/rules/no-unused-vars)
1280 |
1281 | > Why? Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers.
1282 |
1283 | ```javascript
1284 | // bad
1285 |
1286 | var some_unused_var = 42;
1287 |
1288 | // Write-only variables are not considered as used.
1289 | var y = 10;
1290 | y = 5;
1291 |
1292 | // A read for a modification of itself is not considered as used.
1293 | var z = 0;
1294 | z = z + 1;
1295 |
1296 | // Unused function arguments.
1297 | function getX(x, y) {
1298 | return x;
1299 | }
1300 |
1301 | // good
1302 |
1303 | function getXPlusY(x, y) {
1304 | return x + y;
1305 | }
1306 |
1307 | var x = 1;
1308 | var y = a + 2;
1309 |
1310 | alert(getXPlusY(x, y));
1311 |
1312 | // 'type' is ignored even if unused because it has a rest property sibling.
1313 | // This is a form of extracting an object that omits the specified keys.
1314 | var { type, ...coords } = data;
1315 | // 'coords' is now the 'data' object without its 'type' property.
1316 | ```
1317 |
1318 | **[⬆ back to top](#table-of-contents)**
1319 |
1320 | ## Hoisting
1321 |
1322 |
1323 | - [13.1](#hoisting--about) `var` declarations get hoisted to the top of their closest enclosing function scope, their assignment does not. `const` and `let` declarations are blessed with a new concept called [Temporal Dead Zones (TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#temporal_dead_zone_tdz). It’s important to know why [typeof is no longer safe](https://web.archive.org/web/20200121061528/http://es-discourse.com/t/why-typeof-is-no-longer-safe/15).
1324 |
1325 | ```javascript
1326 | // we know this wouldn’t work (assuming there
1327 | // is no notDefined global variable)
1328 | function example() {
1329 | console.log(notDefined); // => throws a ReferenceError
1330 | }
1331 |
1332 | // creating a variable declaration after you
1333 | // reference the variable will work due to
1334 | // variable hoisting. Note: the assignment
1335 | // value of `true` is not hoisted.
1336 | function example() {
1337 | console.log(declaredButNotAssigned); // => undefined
1338 | var declaredButNotAssigned = true;
1339 | }
1340 |
1341 | // the interpreter is hoisting the variable
1342 | // declaration to the top of the scope,
1343 | // which means our example could be rewritten as:
1344 | function example() {
1345 | let declaredButNotAssigned;
1346 | console.log(declaredButNotAssigned); // => undefined
1347 | declaredButNotAssigned = true;
1348 | }
1349 |
1350 | // using const and let
1351 | function example() {
1352 | console.log(declaredButNotAssigned); // => throws a ReferenceError
1353 | console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
1354 | const declaredButNotAssigned = true;
1355 | }
1356 | ```
1357 |
1358 |
1359 | - [13.2](#hoisting--anon-expressions) Anonymous function expressions hoist their variable name, but not the function assignment.
1360 |
1361 | ```javascript
1362 | function example() {
1363 | console.log(anonymous); // => undefined
1364 |
1365 | anonymous(); // => TypeError anonymous is not a function
1366 |
1367 | var anonymous = function () {
1368 | console.log('anonymous function expression');
1369 | };
1370 | }
1371 | ```
1372 |
1373 |
1374 | - [13.3](#hoisting--named-expresions) Named function expressions hoist the variable name, not the function name or the function body.
1375 |
1376 | ```javascript
1377 | function example() {
1378 | console.log(named); // => undefined
1379 |
1380 | named(); // => TypeError named is not a function
1381 |
1382 | superPower(); // => ReferenceError superPower is not defined
1383 |
1384 | var named = function superPower() {
1385 | console.log('Flying');
1386 | };
1387 | }
1388 |
1389 | // the same is true when the function name
1390 | // is the same as the variable name.
1391 | function example() {
1392 | console.log(named); // => undefined
1393 |
1394 | named(); // => TypeError named is not a function
1395 |
1396 | var named = function named() {
1397 | console.log('named');
1398 | };
1399 | }
1400 | ```
1401 |
1402 |
1403 | - [13.4](#hoisting--declarations) Function declarations hoist their name and the function body.
1404 |
1405 | ```javascript
1406 | function example() {
1407 | superPower(); // => Flying
1408 |
1409 | function superPower() {
1410 | console.log('Flying');
1411 | }
1412 | }
1413 | ```
1414 |
1415 | - For more information refer to [JavaScript Scoping & Hoisting](https://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/) by [Ben Cherry](https://www.adequatelygood.com/).
1416 |
1417 | **[⬆ back to top](#table-of-contents)**
1418 |
1419 | ## Comparison Operators & Equality
1420 |
1421 |
1422 | - [14.1](#comparison--eqeqeq) Use `===` and `!==` over `==` and `!=`. eslint: [`eqeqeq`](https://eslint.org/docs/rules/eqeqeq)
1423 |
1424 |
1425 | - [14.2](#comparison--if) Conditional statements such as the `if` statement evaluate their expression using coercion with the `ToBoolean` abstract method and always follow these simple rules:
1426 |
1427 | - **Objects** evaluate to **true**
1428 | - **Undefined** evaluates to **false**
1429 | - **Null** evaluates to **false**
1430 | - **Booleans** evaluate to **the value of the boolean**
1431 | - **Numbers** evaluate to **false** if **+0, -0, or NaN**, otherwise **true**
1432 | - **Strings** evaluate to **false** if an empty string `''`, otherwise **true**
1433 |
1434 | ```javascript
1435 | if ([0] && []) {
1436 | // true
1437 | // an array (even an empty one) is an object, objects will evaluate to true
1438 | }
1439 | ```
1440 |
1441 |
1442 | - [14.3](#comparison--shortcuts) Use shortcuts for booleans, but explicit comparisons for strings and numbers.
1443 |
1444 | ```javascript
1445 | // bad
1446 | if (isValid === true) {
1447 | // ...
1448 | }
1449 |
1450 | // good
1451 | if (isValid) {
1452 | // ...
1453 | }
1454 |
1455 | // bad
1456 | if (name) {
1457 | // ...
1458 | }
1459 |
1460 | // good
1461 | if (name !== '') {
1462 | // ...
1463 | }
1464 |
1465 | // bad
1466 | if (collection.length) {
1467 | // ...
1468 | }
1469 |
1470 | // good
1471 | if (collection.length > 0) {
1472 | // ...
1473 | }
1474 | ```
1475 |
1476 |
1477 | - [14.4](#comparison--moreinfo) For more information see [Truth Equality and JavaScript](https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll.
1478 |
1479 |
1480 | - [14.5](#comparison--switch-blocks) Use braces to create blocks in `case` and `default` clauses that contain lexical declarations (e.g. `let`, `const`, `function`, and `class`). eslint: [`no-case-declarations`](https://eslint.org/docs/rules/no-case-declarations)
1481 |
1482 | > Why? Lexical declarations are visible in the entire `switch` block but only get initialized when assigned, which only happens when its `case` is reached. This causes problems when multiple `case` clauses attempt to define the same thing.
1483 |
1484 | ```javascript
1485 | // bad
1486 | switch (foo) {
1487 | case 1:
1488 | let x = 1;
1489 | break;
1490 | case 2:
1491 | const y = 2;
1492 | break;
1493 | case 3:
1494 | function f() {
1495 | // ...
1496 | }
1497 | break;
1498 | default:
1499 | class C {}
1500 | }
1501 |
1502 | // good
1503 | switch (foo) {
1504 | case 1: {
1505 | let x = 1;
1506 | break;
1507 | }
1508 | case 2: {
1509 | const y = 2;
1510 | break;
1511 | }
1512 | case 3: {
1513 | function f() {
1514 | // ...
1515 | }
1516 | break;
1517 | }
1518 | case 4:
1519 | bar();
1520 | break;
1521 | default: {
1522 | class C {}
1523 | }
1524 | }
1525 | ```
1526 |
1527 |
1528 | - [14.6](#comparison--nested-ternaries) Ternaries should not be nested and generally be single line expressions. eslint: [`no-nested-ternary`](https://eslint.org/docs/rules/no-nested-ternary)
1529 |
1530 | ```javascript
1531 | // bad
1532 | const foo = maybe1 > maybe2
1533 | ? "bar"
1534 | : value1 > value2 ? "baz" : null;
1535 |
1536 | // split into 2 separated ternary expressions
1537 | const maybeNull = value1 > value2 ? 'baz' : null;
1538 |
1539 | // better
1540 | const foo = maybe1 > maybe2
1541 | ? 'bar'
1542 | : maybeNull;
1543 |
1544 | // best
1545 | const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
1546 | ```
1547 |
1548 |
1549 | - [14.7](#comparison--unneeded-ternary) Avoid unneeded ternary statements. eslint: [`no-unneeded-ternary`](https://eslint.org/docs/rules/no-unneeded-ternary)
1550 |
1551 | ```javascript
1552 | // bad
1553 | const foo = a ? a : b;
1554 | const bar = c ? true : false;
1555 | const baz = c ? false : true;
1556 |
1557 | // good
1558 | const foo = a || b;
1559 | const bar = !!c;
1560 | const baz = !c;
1561 | ```
1562 |
1563 |
1564 | - [14.8](#comparison--no-mixed-operators) When mixing operators, enclose them in parentheses. The only exception is the standard arithmetic operators: `+`, `-`, and `**` since their precedence is broadly understood. We recommend enclosing `/` and `*` in parentheses because their precedence can be ambiguous when they are mixed.
1565 | eslint: [`no-mixed-operators`](https://eslint.org/docs/rules/no-mixed-operators)
1566 |
1567 | > Why? This improves readability and clarifies the developer’s intention.
1568 |
1569 | ```javascript
1570 | // bad
1571 | const foo = a && b < 0 || c > 0 || d + 1 === 0;
1572 |
1573 | // bad
1574 | const bar = a ** b - 5 % d;
1575 |
1576 | // bad
1577 | // one may be confused into thinking (a || b) && c
1578 | if (a || b && c) {
1579 | return d;
1580 | }
1581 |
1582 | // bad
1583 | const bar = a + b / c * d;
1584 |
1585 | // good
1586 | const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
1587 |
1588 | // good
1589 | const bar = a ** b - (5 % d);
1590 |
1591 | // good
1592 | if (a || (b && c)) {
1593 | return d;
1594 | }
1595 |
1596 | // good
1597 | const bar = a + (b / c) * d;
1598 | ```
1599 |
1600 | **[⬆ back to top](#table-of-contents)**
1601 |
1602 | ## Blocks
1603 |
1604 |
1605 | - [15.1](#blocks--braces) Use block statements within control structures, and break up block statements into multiple lines. eslint: [`curly`](https://eslint.org/docs/latest/rules/curly) [`brace-style`](https://eslint.org/docs/latest/rules/brace-style)
1606 |
1607 | ```javascript
1608 | // bad
1609 | if (test)
1610 | return false;
1611 |
1612 | // bad
1613 | if (test) return false;
1614 |
1615 | // good
1616 | if (test) {
1617 | return false;
1618 | }
1619 |
1620 | // bad
1621 | function foo() { return false; }
1622 |
1623 | // good
1624 | function bar() {
1625 | return false;
1626 | }
1627 | ```
1628 |
1629 |
1630 | - [15.2](#blocks--cuddled-elses) If you’re using multiline blocks with `if` and `else`, put `else` on the same line as your `if` block’s closing brace. eslint: [`brace-style`](https://eslint.org/docs/rules/brace-style)
1631 |
1632 | ```javascript
1633 | // bad
1634 | if (test) {
1635 | thing1();
1636 | thing2();
1637 | }
1638 | else {
1639 | thing3();
1640 | }
1641 |
1642 | // good
1643 | if (test) {
1644 | thing1();
1645 | thing2();
1646 | } else {
1647 | thing3();
1648 | }
1649 | ```
1650 |
1651 |
1652 | - [15.3](#blocks--no-else-return) If an `if` block always executes a `return` statement, the subsequent `else` block is unnecessary. A `return` in an `else if` block following an `if` block that contains a `return` can be separated into multiple `if` blocks. eslint: [`no-else-return`](https://eslint.org/docs/rules/no-else-return)
1653 |
1654 | ```javascript
1655 | // bad
1656 | function foo() {
1657 | if (x) {
1658 | return x;
1659 | } else {
1660 | return y;
1661 | }
1662 | }
1663 |
1664 | // bad
1665 | function cats() {
1666 | if (x) {
1667 | return x;
1668 | } else if (y) {
1669 | return y;
1670 | }
1671 | }
1672 |
1673 | // bad
1674 | function dogs() {
1675 | if (x) {
1676 | return x;
1677 | } else {
1678 | if (y) {
1679 | return y;
1680 | }
1681 | }
1682 | }
1683 |
1684 | // good
1685 | function foo() {
1686 | if (x) {
1687 | return x;
1688 | }
1689 |
1690 | return y;
1691 | }
1692 |
1693 | // good
1694 | function cats() {
1695 | if (x) {
1696 | return x;
1697 | }
1698 |
1699 | if (y) {
1700 | return y;
1701 | }
1702 | }
1703 |
1704 | // good
1705 | function dogs(x) {
1706 | if (x) {
1707 | if (z) {
1708 | return y;
1709 | }
1710 | } else {
1711 | return z;
1712 | }
1713 | }
1714 | ```
1715 |
1716 | **[⬆ back to top](#table-of-contents)**
1717 |
1718 | ## Control Statements
1719 |
1720 |
1721 | - [16.1](#control-statements) In case your control statement (`if`, `while` etc.) gets too long or exceeds the maximum line length, each (grouped) condition could be put into a new line. The logical operator should begin the line.
1722 |
1723 | > Why? Requiring operators at the beginning of the line keeps the operators aligned and follows a pattern similar to method chaining. This also improves readability by making it easier to visually follow complex logic.
1724 |
1725 | ```javascript
1726 | // bad
1727 | if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
1728 | thing1();
1729 | }
1730 |
1731 | // bad
1732 | if (foo === 123 &&
1733 | bar === 'abc') {
1734 | thing1();
1735 | }
1736 |
1737 | // bad
1738 | if (foo === 123
1739 | && bar === 'abc') {
1740 | thing1();
1741 | }
1742 |
1743 | // bad
1744 | if (
1745 | foo === 123 &&
1746 | bar === 'abc'
1747 | ) {
1748 | thing1();
1749 | }
1750 |
1751 | // good
1752 | if (
1753 | foo === 123
1754 | && bar === 'abc'
1755 | ) {
1756 | thing1();
1757 | }
1758 |
1759 | // good
1760 | if (
1761 | (foo === 123 || bar === 'abc')
1762 | && doesItLookGoodWhenItBecomesThatLong()
1763 | && isThisReallyHappening()
1764 | ) {
1765 | thing1();
1766 | }
1767 |
1768 | // good
1769 | if (foo === 123 && bar === 'abc') {
1770 | thing1();
1771 | }
1772 | ```
1773 |
1774 |
1775 | - [16.2](#control-statement--value-selection) Don't use selection operators in place of control statements.
1776 |
1777 | ```javascript
1778 | // bad
1779 | !isRunning && startRunning();
1780 |
1781 | // good
1782 | if (!isRunning) {
1783 | startRunning();
1784 | }
1785 | ```
1786 |
1787 | **[⬆ back to top](#table-of-contents)**
1788 |
1789 | ## Comments
1790 |
1791 |
1792 | - [17.1](#comments--multiline) Use `/** ... */` for multiline comments.
1793 |
1794 | ```javascript
1795 | // bad
1796 | // make() returns a new element
1797 | // based on the passed in tag name
1798 | //
1799 | // @param {String} tag
1800 | // @return {Element} element
1801 | function make(tag) {
1802 |
1803 | // ...
1804 |
1805 | return element;
1806 | }
1807 |
1808 | // good
1809 | /**
1810 | * make() returns a new element
1811 | * based on the passed-in tag name
1812 | */
1813 | function make(tag) {
1814 |
1815 | // ...
1816 |
1817 | return element;
1818 | }
1819 | ```
1820 |
1821 |
1822 | - [17.2](#comments--singleline) Use `//` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment unless it’s on the first line of a block.
1823 |
1824 | ```javascript
1825 | // bad
1826 | const active = true; // is current tab
1827 |
1828 | // good
1829 | // is current tab
1830 | const active = true;
1831 |
1832 | // bad
1833 | function getType() {
1834 | console.log('fetching type...');
1835 | // set the default type to 'no type'
1836 | const type = this.type || 'no type';
1837 |
1838 | return type;
1839 | }
1840 |
1841 | // good
1842 | function getType() {
1843 | console.log('fetching type...');
1844 |
1845 | // set the default type to 'no type'
1846 | const type = this.type || 'no type';
1847 |
1848 | return type;
1849 | }
1850 |
1851 | // also good
1852 | function getType() {
1853 | // set the default type to 'no type'
1854 | const type = this.type || 'no type';
1855 |
1856 | return type;
1857 | }
1858 | ```
1859 |
1860 |
1861 | - [17.3](#comments--spaces) Start all comments with a space to make it easier to read. eslint: [`spaced-comment`](https://eslint.org/docs/rules/spaced-comment)
1862 |
1863 | ```javascript
1864 | // bad
1865 | //is current tab
1866 | const active = true;
1867 |
1868 | // good
1869 | // is current tab
1870 | const active = true;
1871 |
1872 | // bad
1873 | /**
1874 | *make() returns a new element
1875 | *based on the passed-in tag name
1876 | */
1877 | function make(tag) {
1878 |
1879 | // ...
1880 |
1881 | return element;
1882 | }
1883 |
1884 | // good
1885 | /**
1886 | * make() returns a new element
1887 | * based on the passed-in tag name
1888 | */
1889 | function make(tag) {
1890 |
1891 | // ...
1892 |
1893 | return element;
1894 | }
1895 | ```
1896 |
1897 |
1898 | - [17.4](#comments--actionitems) Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you’re pointing out a problem that needs to be revisited, or if it should be fixed in the current pull-request.
1899 | ```javascript
1900 | // author name is not required
1901 | // for future changes
1902 | // TODO(): blablabla
1903 |
1904 | // for changes required in the current pull request
1905 | // FIXME(): blablabla
1906 | ```
1907 | - [17.6](#comments-jsdoc) To force right style of comments every eslint config should connect [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc). With all recommended rules + `jsdoc/require-file-overview`
1908 |
1909 | **[⬆ back to top](#table-of-contents)**
1910 |
1911 | ## Whitespace
1912 |
1913 |
1914 | - [18.1](#whitespace--spaces) Use soft tabs (space character) set to 4 spaces. eslint: [`indent`](https://eslint.org/docs/rules/indent)
1915 |
1916 | ```javascript
1917 | // bad
1918 | function foo() {
1919 | ∙∙∙∙let name;
1920 | }
1921 |
1922 | // bad
1923 | function bar() {
1924 | ∙let name;
1925 | }
1926 |
1927 | // good
1928 | function baz() {
1929 | ∙∙let name;
1930 | }
1931 | ```
1932 |
1933 |
1934 | - [18.2](#whitespace--before-blocks) Place 1 space before the leading brace. eslint: [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks)
1935 |
1936 | ```javascript
1937 | // bad
1938 | function test(){
1939 | console.log('test');
1940 | }
1941 |
1942 | // good
1943 | function test() {
1944 | console.log('test');
1945 | }
1946 |
1947 | // bad
1948 | dog.set('attr',{
1949 | age: '1 year',
1950 | breed: 'Bernese Mountain Dog',
1951 | });
1952 |
1953 | // good
1954 | dog.set('attr', {
1955 | age: '1 year',
1956 | breed: 'Bernese Mountain Dog',
1957 | });
1958 | ```
1959 |
1960 |
1961 | - [18.3](#whitespace--around-keywords) Place 1 space before the opening parenthesis in control statements (`if`, `while` etc.). Place no space between the argument list and the function name in function calls and declarations. eslint: [`keyword-spacing`](https://eslint.org/docs/rules/keyword-spacing)
1962 |
1963 | ```javascript
1964 | // bad
1965 | if(isJedi) {
1966 | fight ();
1967 | }
1968 |
1969 | // good
1970 | if (isJedi) {
1971 | fight();
1972 | }
1973 |
1974 | // bad
1975 | function fight () {
1976 | console.log ('Swooosh!');
1977 | }
1978 |
1979 | // good
1980 | function fight() {
1981 | console.log('Swooosh!');
1982 | }
1983 | ```
1984 |
1985 |
1986 | - [18.4](#whitespace--infix-ops) Set off operators with spaces. eslint: [`space-infix-ops`](https://eslint.org/docs/rules/space-infix-ops)
1987 |
1988 | ```javascript
1989 | // bad
1990 | const x=y+5;
1991 |
1992 | // good
1993 | const x = y + 5;
1994 | ```
1995 |
1996 |
1997 | - [18.5](#whitespace--newline-at-end)End files with a single newline character. eslint: [`eol-last`](https://eslint.org/docs/rules/eol-last)
1998 |
1999 | ```javascript
2000 | // bad
2001 | import { es6 } from './AirbnbStyleGuide';
2002 | // ...
2003 | export default es6;
2004 | ```
2005 |
2006 | ```javascript
2007 | // bad
2008 | import { es6 } from './AirbnbStyleGuide';
2009 | // ...
2010 | export default es6;
2011 | ```
2012 |
2013 | ```javascript
2014 | // good
2015 | import { es6 } from './AirbnbStyleGuide';
2016 | // ...
2017 | export default es6;
2018 | ```
2019 |
2020 |
2021 | - [18.6](#whitespace--chains) Use indentation when making long method chains (more than 2 method chains). Use a leading dot, which
2022 | emphasizes that the line is a method call, not a new statement. eslint: [`newline-per-chained-call`](https://eslint.org/docs/rules/newline-per-chained-call) [`no-whitespace-before-property`](https://eslint.org/docs/rules/no-whitespace-before-property)
2023 |
2024 | ```javascript
2025 | // bad
2026 | $('#items').find('.selected').highlight().end().find('.open').updateCount();
2027 |
2028 | // bad
2029 | $('#items').
2030 | find('.selected').
2031 | highlight().
2032 | end().
2033 | find('.open').
2034 | updateCount();
2035 |
2036 | // good
2037 | $('#items')
2038 | .find('.selected')
2039 | .highlight()
2040 | .end()
2041 | .find('.open')
2042 | .updateCount();
2043 |
2044 | // bad
2045 | const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
2046 | .attr('width', (radius + margin) * 2).append('svg:g')
2047 | .attr('transform', `translate(${radius + margin},${radius + margin})`)
2048 | .call(tron.led);
2049 |
2050 | // good
2051 | const leds = stage.selectAll('.led')
2052 | .data(data)
2053 | .enter().append('svg:svg')
2054 | .classed('led', true)
2055 | .attr('width', (radius + margin) * 2)
2056 | .append('svg:g')
2057 | .attr('transform', `translate(${radius + margin},${radius + margin})`)
2058 | .call(tron.led);
2059 |
2060 | // good
2061 | const leds = stage.selectAll('.led').data(data);
2062 | const svg = leds.enter().append('svg:svg');
2063 | svg.classed('led', true).attr('width', (radius + margin) * 2);
2064 | const g = svg.append('svg:g');
2065 | g.attr('transform', `translate(${radius + margin},${radius + margin})`).call(tron.led);
2066 | ```
2067 |
2068 |
2069 | - [18.7](#whitespace--after-blocks) Leave a blank line after blocks and before the next statement.
2070 |
2071 | ```javascript
2072 | // bad
2073 | if (foo) {
2074 | return bar;
2075 | }
2076 | return baz;
2077 |
2078 | // good
2079 | if (foo) {
2080 | return bar;
2081 | }
2082 |
2083 | return baz;
2084 |
2085 | // bad
2086 | const obj = {
2087 | foo() {
2088 | },
2089 | bar() {
2090 | },
2091 | };
2092 | return obj;
2093 |
2094 | // good
2095 | const obj = {
2096 | foo() {
2097 | },
2098 |
2099 | bar() {
2100 | },
2101 | };
2102 |
2103 | return obj;
2104 |
2105 | // bad
2106 | const arr = [
2107 | function foo() {
2108 | },
2109 | function bar() {
2110 | },
2111 | ];
2112 | return arr;
2113 |
2114 | // good
2115 | const arr = [
2116 | function foo() {
2117 | },
2118 |
2119 | function bar() {
2120 | },
2121 | ];
2122 |
2123 | return arr;
2124 | ```
2125 |
2126 |
2127 | - [18.8](#whitespace--padded-blocks) Do not pad your blocks with blank lines. eslint: [`padded-blocks`](https://eslint.org/docs/rules/padded-blocks)
2128 |
2129 | ```javascript
2130 | // bad
2131 | function bar() {
2132 |
2133 | console.log(foo);
2134 |
2135 | }
2136 |
2137 | // bad
2138 | if (baz) {
2139 |
2140 | console.log(qux);
2141 | } else {
2142 | console.log(foo);
2143 |
2144 | }
2145 |
2146 | // bad
2147 | class Foo {
2148 |
2149 | constructor(bar) {
2150 | this.bar = bar;
2151 | }
2152 | }
2153 |
2154 | // good
2155 | function bar() {
2156 | console.log(foo);
2157 | }
2158 |
2159 | // good
2160 | if (baz) {
2161 | console.log(qux);
2162 | } else {
2163 | console.log(foo);
2164 | }
2165 | ```
2166 |
2167 |
2168 | - [18.9](#whitespace--no-multiple-blanks) Do not use multiple blank lines to pad your code. eslint: [`no-multiple-empty-lines`](https://eslint.org/docs/rules/no-multiple-empty-lines)
2169 |
2170 |
2171 | ```javascript
2172 | // bad
2173 | class Person {
2174 | constructor(fullName, email, birthday) {
2175 | this.fullName = fullName;
2176 |
2177 |
2178 | this.email = email;
2179 |
2180 |
2181 | this.setAge(birthday);
2182 | }
2183 |
2184 |
2185 | setAge(birthday) {
2186 | const today = new Date();
2187 |
2188 |
2189 | const age = this.getAge(today, birthday);
2190 |
2191 |
2192 | this.age = age;
2193 | }
2194 |
2195 |
2196 | getAge(today, birthday) {
2197 | // ..
2198 | }
2199 | }
2200 |
2201 | // good
2202 | class Person {
2203 | constructor(fullName, email, birthday) {
2204 | this.fullName = fullName;
2205 | this.email = email;
2206 | this.setAge(birthday);
2207 | }
2208 |
2209 | setAge(birthday) {
2210 | const today = new Date();
2211 | const age = getAge(today, birthday);
2212 | this.age = age;
2213 | }
2214 |
2215 | getAge(today, birthday) {
2216 | // ..
2217 | }
2218 | }
2219 | ```
2220 |
2221 |
2222 | - [18.10](#whitespace--in-parens) Do not add spaces inside parentheses. eslint: [`space-in-parens`](https://eslint.org/docs/rules/space-in-parens)
2223 |
2224 | ```javascript
2225 | // bad
2226 | function bar( foo ) {
2227 | return foo;
2228 | }
2229 |
2230 | // good
2231 | function bar(foo) {
2232 | return foo;
2233 | }
2234 |
2235 | // bad
2236 | if ( foo ) {
2237 | console.log(foo);
2238 | }
2239 |
2240 | // good
2241 | if (foo) {
2242 | console.log(foo);
2243 | }
2244 | ```
2245 |
2246 |
2247 | - [18.11](#whitespace--in-brackets) Do not add spaces inside brackets. eslint: [`array-bracket-spacing`](https://eslint.org/docs/rules/array-bracket-spacing)
2248 |
2249 | ```javascript
2250 | // bad
2251 | const foo = [ 1, 2, 3 ];
2252 | console.log(foo[ 0 ]);
2253 |
2254 | // good
2255 | const foo = [1, 2, 3];
2256 | console.log(foo[0]);
2257 | ```
2258 |
2259 |
2260 | - [18.12](#whitespace--in-braces) Add spaces inside curly braces. eslint: [`object-curly-spacing`](https://eslint.org/docs/rules/object-curly-spacing)
2261 |
2262 | ```javascript
2263 | // bad
2264 | const foo = {clark: 'kent'};
2265 |
2266 | // good
2267 | const foo = { clark: 'kent' };
2268 | ```
2269 |
2270 |
2271 | - [18.13](#whitespace--max-len) Avoid having lines of code that are longer than 120 characters (including whitespace). Note: per [above](#strings--line-length), long strings are exempt from this rule, and should not be broken up. eslint: [`max-len`](https://eslint.org/docs/rules/max-len)
2272 |
2273 | > Why? This ensures readability and maintainability.
2274 |
2275 | ```javascript
2276 | // bad
2277 | const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy;
2278 |
2279 | // bad
2280 | $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));
2281 |
2282 | // good
2283 | const foo = jsonData
2284 | && jsonData.foo
2285 | && jsonData.foo.bar
2286 | && jsonData.foo.bar.baz
2287 | && jsonData.foo.bar.baz.quux
2288 | && jsonData.foo.bar.baz.quux.xyzzy;
2289 |
2290 | // good
2291 | $.ajax({
2292 | method: 'POST',
2293 | url: 'https://airbnb.com/',
2294 | data: { name: 'John' },
2295 | })
2296 | .done(() => console.log('Congratulations!'))
2297 | .fail(() => console.log('You have failed this city.'));
2298 | ```
2299 |
2300 |
2301 | - [18.14](#whitespace--block-spacing) Require consistent spacing inside an open block token and the next token on the same line. This rule also enforces consistent spacing inside a close block token and previous token on the same line. eslint: [`block-spacing`](https://eslint.org/docs/rules/block-spacing)
2302 |
2303 | ```javascript
2304 | // bad
2305 | function foo() {return true;}
2306 | if (foo) { bar = 0;}
2307 |
2308 | // good
2309 | function foo() { return true; }
2310 | if (foo) { bar = 0; }
2311 | ```
2312 |
2313 |
2314 | - [18.15](#whitespace--comma-spacing) Avoid spaces before commas and require a space after commas. eslint: [`comma-spacing`](https://eslint.org/docs/rules/comma-spacing)
2315 |
2316 | ```javascript
2317 | // bad
2318 | var foo = 1,bar = 2;
2319 | var arr = [1 , 2];
2320 |
2321 | // good
2322 | var foo = 1, bar = 2;
2323 | var arr = [1, 2];
2324 | ```
2325 |
2326 |
2327 | - [18.16](#whitespace--computed-property-spacing) Enforce spacing inside of computed property brackets. eslint: [`computed-property-spacing`](https://eslint.org/docs/rules/computed-property-spacing)
2328 |
2329 | ```javascript
2330 | // bad
2331 | obj[foo ]
2332 | obj[ 'foo']
2333 | var x = {[ b ]: a}
2334 | obj[foo[ bar ]]
2335 |
2336 | // good
2337 | obj[foo]
2338 | obj['foo']
2339 | var x = { [b]: a }
2340 | obj[foo[bar]]
2341 | ```
2342 |
2343 |
2344 | - [18.17](#whitespace--func-call-spacing) Avoid spaces between functions and their invocations. eslint: [`func-call-spacing`](https://eslint.org/docs/rules/func-call-spacing)
2345 |
2346 | ```javascript
2347 | // bad
2348 | func ();
2349 |
2350 | func
2351 | ();
2352 |
2353 | // good
2354 | func();
2355 | ```
2356 |
2357 |
2358 | - [18.18](#whitespace--key-spacing) Enforce spacing between keys and values in object literal properties. eslint: [`key-spacing`](https://eslint.org/docs/rules/key-spacing)
2359 |
2360 | ```javascript
2361 | // bad
2362 | var obj = { foo : 42 };
2363 | var obj2 = { foo:42 };
2364 |
2365 | // good
2366 | var obj = { foo: 42 };
2367 | ```
2368 |
2369 |
2370 | - [18.19](#whitespace--no-trailing-spaces) Avoid trailing spaces at the end of lines. eslint: [`no-trailing-spaces`](https://eslint.org/docs/rules/no-trailing-spaces)
2371 |
2372 |
2373 | - [18.20](#whitespace--no-multiple-empty-lines) Avoid multiple empty lines, only allow one newline at the end of files, and avoid a newline at the beginning of files. eslint: [`no-multiple-empty-lines`](https://eslint.org/docs/rules/no-multiple-empty-lines)
2374 |
2375 |
2376 | ```javascript
2377 | // bad - multiple empty lines
2378 | var x = 1;
2379 |
2380 |
2381 | var y = 2;
2382 |
2383 | // bad - 2+ newlines at end of file
2384 | var x = 1;
2385 | var y = 2;
2386 |
2387 |
2388 | // bad - 1+ newline(s) at beginning of file
2389 |
2390 | var x = 1;
2391 | var y = 2;
2392 |
2393 | // good
2394 | var x = 1;
2395 | var y = 2;
2396 |
2397 | ```
2398 |
2399 |
2400 | **[⬆ back to top](#table-of-contents)**
2401 |
2402 | ## Commas
2403 |
2404 |
2405 | - [19.1](#commas--leading-trailing) Leading commas: **Nope.** eslint: [`comma-style`](https://eslint.org/docs/rules/comma-style)
2406 |
2407 | ```javascript
2408 | // bad
2409 | const story = [
2410 | once
2411 | , upon
2412 | , aTime
2413 | ];
2414 |
2415 | // good
2416 | const story = [
2417 | once,
2418 | upon,
2419 | aTime,
2420 | ];
2421 |
2422 | // bad
2423 | const hero = {
2424 | firstName: 'Ada'
2425 | , lastName: 'Lovelace'
2426 | , birthYear: 1815
2427 | , superPower: 'computers'
2428 | };
2429 |
2430 | // good
2431 | const hero = {
2432 | firstName: 'Ada',
2433 | lastName: 'Lovelace',
2434 | birthYear: 1815,
2435 | superPower: 'computers',
2436 | };
2437 | ```
2438 |
2439 |
2440 | - [19.2](#commas--dangling) Additional trailing comma: **Yup.** eslint: [`comma-dangle`](https://eslint.org/docs/rules/comma-dangle)
2441 |
2442 | > Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don’t have to worry about the [trailing comma problem](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas) in legacy browsers.
2443 |
2444 | ```diff
2445 | // bad - git diff without trailing comma
2446 | const hero = {
2447 | firstName: 'Florence',
2448 | - lastName: 'Nightingale'
2449 | + lastName: 'Nightingale',
2450 | + inventorOf: ['coxcomb chart', 'modern nursing']
2451 | };
2452 |
2453 | // good - git diff with trailing comma
2454 | const hero = {
2455 | firstName: 'Florence',
2456 | lastName: 'Nightingale',
2457 | + inventorOf: ['coxcomb chart', 'modern nursing'],
2458 | };
2459 | ```
2460 |
2461 | ```javascript
2462 | // bad
2463 | const hero = {
2464 | firstName: 'Dana',
2465 | lastName: 'Scully'
2466 | };
2467 |
2468 | const heroes = [
2469 | 'Batman',
2470 | 'Superman'
2471 | ];
2472 |
2473 | // good
2474 | const hero = {
2475 | firstName: 'Dana',
2476 | lastName: 'Scully',
2477 | };
2478 |
2479 | const heroes = [
2480 | 'Batman',
2481 | 'Superman',
2482 | ];
2483 |
2484 | // bad
2485 | function createHero(
2486 | firstName,
2487 | lastName,
2488 | inventorOf
2489 | ) {
2490 | // does nothing
2491 | }
2492 |
2493 | // good
2494 | function createHero(
2495 | firstName,
2496 | lastName,
2497 | inventorOf,
2498 | ) {
2499 | // does nothing
2500 | }
2501 |
2502 | // good (note that a comma must not appear after a "rest" element)
2503 | function createHero(
2504 | firstName,
2505 | lastName,
2506 | inventorOf,
2507 | ...heroArgs
2508 | ) {
2509 | // does nothing
2510 | }
2511 |
2512 | // bad
2513 | createHero(
2514 | firstName,
2515 | lastName,
2516 | inventorOf
2517 | );
2518 |
2519 | // good
2520 | createHero(
2521 | firstName,
2522 | lastName,
2523 | inventorOf,
2524 | );
2525 |
2526 | // good (note that a comma must not appear after a "rest" element)
2527 | createHero(
2528 | firstName,
2529 | lastName,
2530 | inventorOf,
2531 | ...heroArgs
2532 | );
2533 | ```
2534 |
2535 | **[⬆ back to top](#table-of-contents)**
2536 |
2537 | ## Semicolons
2538 |
2539 |
2540 | - [20.1](#semicolons--required) **Yup.** eslint: [`semi`](https://eslint.org/docs/rules/semi)
2541 |
2542 | > Why? When JavaScript encounters a line break without a semicolon, it uses a set of rules called [Automatic Semicolon Insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion) to determine whether it should regard that line break as the end of a statement, and (as the name implies) place a semicolon into your code before the line break if it thinks so. ASI contains a few eccentric behaviors, though, and your code will break if JavaScript misinterprets your line break. These rules will become more complicated as new features become a part of JavaScript. Explicitly terminating your statements and configuring your linter to catch missing semicolons will help prevent you from encountering issues.
2543 |
2544 | ```javascript
2545 | // bad - raises exception
2546 | const luke = {}
2547 | const leia = {}
2548 | [luke, leia].forEach((jedi) => jedi.father = 'vader')
2549 |
2550 | // bad - raises exception
2551 | const reaction = "No! That’s impossible!"
2552 | (async function meanwhileOnTheFalcon() {
2553 | // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
2554 | // ...
2555 | }())
2556 |
2557 | // bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI!
2558 | function foo() {
2559 | return
2560 | 'search your feelings, you know it to be foo'
2561 | }
2562 |
2563 | // good
2564 | const luke = {};
2565 | const leia = {};
2566 | [luke, leia].forEach((jedi) => {
2567 | jedi.father = 'vader';
2568 | });
2569 |
2570 | // good
2571 | const reaction = "No! That’s impossible!";
2572 | (async function meanwhileOnTheFalcon() {
2573 | // handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
2574 | // ...
2575 | }());
2576 |
2577 | // good
2578 | function foo() {
2579 | return 'search your feelings, you know it to be foo';
2580 | }
2581 | ```
2582 |
2583 | [Read more](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214#7365214).
2584 |
2585 | **[⬆ back to top](#table-of-contents)**
2586 |
2587 | ## Type Casting & Coercion
2588 |
2589 |
2590 | - [21.1](#coercion--explicit) Perform type coercion at the beginning of the statement.
2591 |
2592 |
2593 | - [21.2](#coercion--strings) Strings: eslint: [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers)
2594 |
2595 | ```javascript
2596 | // => this.reviewScore = 9;
2597 |
2598 | // bad
2599 | const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string"
2600 |
2601 | // bad
2602 | const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf()
2603 |
2604 | // bad
2605 | const totalScore = this.reviewScore.toString(); // isn’t guaranteed to return a string
2606 |
2607 | // good
2608 | const totalScore = String(this.reviewScore);
2609 | ```
2610 |
2611 |
2612 | - [21.3](#coercion--numbers) Numbers: Use `Number` for type casting and `parseInt` always with a radix for parsing strings. eslint: [`radix`](https://eslint.org/docs/rules/radix) [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers)
2613 |
2614 | > Why? The `parseInt` function produces an integer value dictated by interpretation of the contents of the string argument according to the specified radix. Leading whitespace in string is ignored. If radix is `undefined` or `0`, it is assumed to be `10` except when the number begins with the character pairs `0x` or `0X`, in which case a radix of 16 is assumed. This differs from ECMAScript 3, which merely discouraged (but allowed) octal interpretation. Many implementations have not adopted this behavior as of 2013. And, because older browsers must be supported, always specify a radix.
2615 |
2616 | ```javascript
2617 | const inputValue = '4';
2618 |
2619 | // bad
2620 | const val = new Number(inputValue);
2621 |
2622 | // bad
2623 | const val = +inputValue;
2624 |
2625 | // bad
2626 | const val = inputValue >> 0;
2627 |
2628 | // bad
2629 | const val = parseInt(inputValue);
2630 |
2631 | // good
2632 | const val = Number(inputValue);
2633 |
2634 | // good
2635 | const val = parseInt(inputValue, 10);
2636 | ```
2637 |
2638 |
2639 | - [21.4](#coercion--comment-deviations) If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](https://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you’re doing.
2640 |
2641 | ```javascript
2642 | // good
2643 | /**
2644 | * parseInt was the reason my code was slow.
2645 | * Bitshifting the String to coerce it to a
2646 | * Number made it a lot faster.
2647 | */
2648 | const val = inputValue >> 0;
2649 | ```
2650 |
2651 |
2652 | - [21.5](#coercion--bitwise) **Note:** Be careful when using bitshift operations. Numbers are represented as [64-bit values](https://es5.github.io/#x4.3.19), but bitshift operations always return a 32-bit integer ([source](https://es5.github.io/#x11.7)). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. [Discussion](https://github.com/airbnb/javascript/issues/109). Largest signed 32-bit Int is 2,147,483,647:
2653 |
2654 | ```javascript
2655 | 2147483647 >> 0; // => 2147483647
2656 | 2147483648 >> 0; // => -2147483648
2657 | 2147483649 >> 0; // => -2147483647
2658 | ```
2659 |
2660 |
2661 | - [21.6](#coercion--booleans) Booleans: eslint: [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers)
2662 |
2663 | ```javascript
2664 | const age = 0;
2665 |
2666 | // bad
2667 | const hasAge = new Boolean(age);
2668 |
2669 | // good
2670 | const hasAge = Boolean(age);
2671 |
2672 | // best
2673 | const hasAge = !!age;
2674 | ```
2675 |
2676 | **[⬆ back to top](#table-of-contents)**
2677 |
2678 | ## Naming Conventions
2679 |
2680 |
2681 | - [22.1](#naming--descriptive) Avoid single letter names. Be descriptive with your naming. eslint: [`id-length`](https://eslint.org/docs/rules/id-length)
2682 |
2683 | ```javascript
2684 | // bad
2685 | function q() {
2686 | // ...
2687 | }
2688 |
2689 | // good
2690 | function query() {
2691 | // ...
2692 | }
2693 | ```
2694 |
2695 |
2696 | - [22.2](#naming--camelCase) Use camelCase when naming objects, functions, and instances. eslint: [`camelcase`](https://eslint.org/docs/rules/camelcase)
2697 |
2698 | ```javascript
2699 | // bad
2700 | const OBJEcttsssss = {};
2701 | const this_is_my_object = {};
2702 | function c() {}
2703 |
2704 | // good
2705 | const thisIsMyObject = {};
2706 | function thisIsMyFunction() {}
2707 | ```
2708 |
2709 |
2710 | - [22.3](#naming--PascalCase) Use PascalCase only when naming constructors or classes. eslint: [`new-cap`](https://eslint.org/docs/rules/new-cap)
2711 |
2712 | ```javascript
2713 | // bad
2714 | function user(options) {
2715 | this.name = options.name;
2716 | }
2717 |
2718 | const bad = new user({
2719 | name: 'nope',
2720 | });
2721 |
2722 | // good
2723 | class User {
2724 | constructor(options) {
2725 | this.name = options.name;
2726 | }
2727 | }
2728 |
2729 | const good = new User({
2730 | name: 'yup',
2731 | });
2732 | ```
2733 |
2734 |
2735 | - [22.4](#naming--leading-underscore) Do not use trailing or leading underscores. eslint: [`no-underscore-dangle`](https://eslint.org/docs/rules/no-underscore-dangle)
2736 |
2737 | > Why? JavaScript does not have the concept of privacy in terms of properties or methods. Although a leading underscore is a common convention to mean “private”, in fact, these properties are fully public, and as such, are part of your public API contract. This convention might lead developers to wrongly think that a change won’t count as breaking, or that tests aren’t needed. tl;dr: if you want something to be “private”, it must not be observably present.
2738 |
2739 | ```javascript
2740 | // bad
2741 | this.__firstName__ = 'Panda';
2742 | this.firstName_ = 'Panda';
2743 | this._firstName = 'Panda';
2744 |
2745 | // good
2746 | this.firstName = 'Panda';
2747 |
2748 | // good, in environments where WeakMaps are available
2749 | // see https://kangax.github.io/compat-table/es6/#test-WeakMap
2750 | const firstNames = new WeakMap();
2751 | firstNames.set(this, 'Panda');
2752 | ```
2753 |
2754 |
2755 | - [22.5](#naming--self-this) Don’t save references to `this`. Use arrow functions or [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
2756 |
2757 | ```javascript
2758 | // bad
2759 | function foo() {
2760 | const self = this;
2761 | return function () {
2762 | console.log(self);
2763 | };
2764 | }
2765 |
2766 | // bad
2767 | function foo() {
2768 | const that = this;
2769 | return function () {
2770 | console.log(that);
2771 | };
2772 | }
2773 |
2774 | // good
2775 | function foo() {
2776 | return () => {
2777 | console.log(this);
2778 | };
2779 | }
2780 | ```
2781 |
2782 |
2783 | - [22.6](#naming--PascalCase-singleton) Use PascalCase when you export a constructor / class / singleton / function library / bare object.
2784 |
2785 | ```javascript
2786 | const AirbnbStyleGuide = {
2787 | es6: {
2788 | },
2789 | };
2790 |
2791 | export { AirbnbStyleGuide };
2792 | ```
2793 |
2794 |
2795 | - [22.7](#naming--Acronyms-and-Initialisms) Acronyms and initialisms should always be all uppercased, or all lowercased.
2796 |
2797 | > Why? Names are for readability, not to appease a computer algorithm.
2798 |
2799 | ```javascript
2800 | // bad
2801 | import SmsContainer from './containers/SmsContainer';
2802 |
2803 | // bad
2804 | const HttpRequests = [
2805 | // ...
2806 | ];
2807 |
2808 | // good
2809 | import SMSContainer from './containers/SMSContainer';
2810 |
2811 | // good
2812 | const HTTPRequests = [
2813 | // ...
2814 | ];
2815 |
2816 | // also good
2817 | const httpRequests = [
2818 | // ...
2819 | ];
2820 |
2821 | // best
2822 | import TextMessageContainer from './containers/TextMessageContainer';
2823 |
2824 | // best
2825 | const requests = [
2826 | // ...
2827 | ];
2828 | ```
2829 |
2830 |
2831 | - [22.7](#naming--constants) Use UPPER_SNAKE_CASE when naming constants.
2832 |
2833 | ```javascript
2834 | // bad
2835 | const childCombinator = '>';
2836 |
2837 | // good
2838 | const CHILD_COMBINATOR = '>';
2839 | ```
2840 |
2841 | **[⬆ back to top](#table-of-contents)**
2842 |
2843 | ## Accessors
2844 |
2845 |
2846 | - [23.1](#accessors--not-required) Accessor functions for properties are not required.
2847 |
2848 |
2849 | - [23.2](#accessors--no-getters-setters) Do not use JavaScript getters/setters as they cause unexpected side effects and are harder to test, maintain, and reason about. Instead, if you do make accessor functions, use `getVal()` and `setVal('hello')`.
2850 |
2851 | ```javascript
2852 | // bad
2853 | class Dragon {
2854 | get age() {
2855 | // ...
2856 | }
2857 |
2858 | set age(value) {
2859 | // ...
2860 | }
2861 | }
2862 |
2863 | // good
2864 | class Dragon {
2865 | getAge() {
2866 | // ...
2867 | }
2868 |
2869 | setAge(value) {
2870 | // ...
2871 | }
2872 | }
2873 | ```
2874 |
2875 |
2876 | - [23.3](#accessors--boolean-prefix) If the property/method is a `boolean`, use `isVal()` or `hasVal()`.
2877 |
2878 | ```javascript
2879 | // bad
2880 | if (!dragon.age()) {
2881 | return false;
2882 | }
2883 |
2884 | // good
2885 | if (!dragon.hasAge()) {
2886 | return false;
2887 | }
2888 | ```
2889 |
2890 |
2891 | - [23.4](#accessors--consistent) It’s okay to create `get()` and `set()` functions, but be consistent.
2892 |
2893 | ```javascript
2894 | class Jedi {
2895 | constructor(options = {}) {
2896 | const lightsaber = options.lightsaber || 'blue';
2897 | this.set('lightsaber', lightsaber);
2898 | }
2899 |
2900 | set(key, val) {
2901 | this[key] = val;
2902 | }
2903 |
2904 | get(key) {
2905 | return this[key];
2906 | }
2907 | }
2908 | ```
2909 |
2910 | **[⬆ back to top](#table-of-contents)**
2911 |
2912 | ## Standard Library
2913 |
2914 | The [Standard Library](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects)
2915 | contains utilities that are functionally broken but remain for legacy reasons.
2916 |
2917 |
2918 | - [24.1](#standard-library--isnan) Use `Number.isNaN` instead of global `isNaN`.
2919 | eslint: [`no-restricted-globals`](https://eslint.org/docs/rules/no-restricted-globals)
2920 |
2921 | > Why? The global `isNaN` coerces non-numbers to numbers, returning true for anything that coerces to NaN.
2922 | > If this behavior is desired, make it explicit.
2923 |
2924 | ```javascript
2925 | // bad
2926 | isNaN('1.2'); // false
2927 | isNaN('1.2.3'); // true
2928 |
2929 | // good
2930 | Number.isNaN('1.2.3'); // false
2931 | Number.isNaN(Number('1.2.3')); // true
2932 | ```
2933 |
2934 |
2935 | - [24.2](#standard-library--isfinite) Use `Number.isFinite` instead of global `isFinite`.
2936 | eslint: [`no-restricted-globals`](https://eslint.org/docs/rules/no-restricted-globals)
2937 |
2938 | > Why? The global `isFinite` coerces non-numbers to numbers, returning true for anything that coerces to a finite number.
2939 | > If this behavior is desired, make it explicit.
2940 |
2941 | ```javascript
2942 | // bad
2943 | isFinite('2e3'); // true
2944 |
2945 | // good
2946 | Number.isFinite('2e3'); // false
2947 | Number.isFinite(parseInt('2e3', 10)); // true
2948 | ```
2949 |
2950 | **[⬆ back to top](#table-of-contents)**
2951 |
2952 | ## Sample eslint config
2953 | Sample config can be found [here](./.eslintrc.js). Feel free to copy it and thoughtfully modify it to your needs.
2954 |
2955 | **[⬆ back to top](#table-of-contents)**
2956 |
2957 | ## TypeScript
2958 | In TypeScript we use the same rules as in JavaScript, but we also have some additional guidelines that are specific to typescript.
2959 |
2960 | ### Naming conventions
2961 |
2962 |
2963 | - [25.1](#typescript--enum-naming-conventions) Use `PascalCase` enum names and enum properties.
2964 |
2965 | ```typescript
2966 | // PascalCase
2967 | // bad
2968 | enum COLOR {
2969 | RED = 'RED',
2970 | GREEN = 'GREEN',
2971 | BLUE = 'BLUE',
2972 | }
2973 |
2974 | // good
2975 | enum Color {
2976 | Red = 'Red', // PascalCase for values is optional
2977 | Green = 'Green',
2978 | Blue = 'Blue',
2979 | }
2980 |
2981 | ```
2982 | Use singular names for enums.
2983 | ```typescript
2984 | // singular names
2985 | // bad
2986 | enum Colors {
2987 | Red = 'Red',
2988 | Green = 'Green',
2989 | Blue = 'Blue',
2990 | }
2991 |
2992 | // good
2993 | enum Color {
2994 | Red = 'Red',
2995 | Green = 'Green',
2996 | Blue = 'Blue',
2997 | }
2998 | ```
2999 |
3000 | ### tsconfig flags
3001 |
3002 |
3003 | - [26.1](#typescript--tsconfig-strict) Use `strict: true` in your `tsconfig.json` file.
3004 |
3005 | ```json
3006 | {
3007 | "compilerOptions": {
3008 | "strict": true
3009 | }
3010 | }
3011 | ```
3012 |
3013 |
3014 | - [26.2](#typescript--tsconfig-nouncheckedindexedaccess) Use `"noUncheckedIndexedAccess": true` in your `tsconfig.json` file. This is a good practice because it prevents you from accessing elements that don't exist. Read more about it [here](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess).
3015 |
3016 | ```json
3017 | {
3018 | "compilerOptions": {
3019 | "noUncheckedIndexedAccess": true
3020 | }
3021 | }
3022 | ```
3023 |
3024 | ### Caught error type
3025 |
3026 |
3027 | - [25.3](#typescript--caught-error-type) Use `unknown` type for caught errors.
3028 |
3029 | ```typescript
3030 | // bad
3031 | try {
3032 | throwingErrorFunction();
3033 | } catch (error: any) {
3034 | console.error(error);
3035 | }
3036 |
3037 | // good
3038 | try {
3039 | throwingErrorFunction();
3040 | } catch (error: unknown) {
3041 | console.error(error);
3042 | }
3043 | ```
3044 |
3045 | **[⬆ back to top](#table-of-contents)**
3046 |
3047 | ## Changelog
3048 | For the changelog we follow [keep a changelog](https://keepachangelog.com/en/1.0.0/) guide.
3049 |
3050 | **[⬆ back to top](#table-of-contents)**
3051 |
--------------------------------------------------------------------------------
/PHP.md:
--------------------------------------------------------------------------------
1 | PHP projects should follow [Symfony style guide](https://symfony.com/doc/current/contributing/code/standards.html).
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AdGuard Code Guidelines
2 |
3 | * [C#](C%23.md)
4 | * [Go](Go)
5 | * [Git](Go/Git.md)
6 | * [Go Code](Go/Go.md)
7 | * [Markdown](Go/Markdown.md)
8 | * [Shell](Go/Shell.md)
9 | * [Text, Including Comments](Go/Text.md)
10 | * [YAML](Go/YAML.md)
11 | * [Java](Java.md)
12 | * [Javascript](JavaScript/Javascript.md)
13 | * [PHP](PHP.md)
14 |
--------------------------------------------------------------------------------