├── .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 | ## Commenting 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 | --------------------------------------------------------------------------------