├── .gitignore ├── CONTRIBUTING.md ├── README.md ├── Rakefile └── mix.exs /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | deps 3 | doc 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the Elixir Style Guide 2 | 3 | First of all, thanks for wanting to contribute! :heart: 4 | 5 | You can contribute in several ways: 6 | 7 | * open up an issue if you find something plain old wrong in the style guide 8 | (e.g., typos or formatting); 9 | * open up an issue if you find inconsistencies in the style guide. This way, we 10 | can discuss the better way to eliminate those inconsistencies; 11 | * if you have any suggestions or opinions, open up an issue or (even better!) a 12 | pull request. 13 | 14 | If you edit the `README.md` file, please stick to this set of 15 | formatting/markup/style rules so that the style remains consistent: 16 | 17 | * don't make lines longer than 80 characters (most editors have an auto-wrapping 18 | functionality, for example [Emacs][Emacs LineWrap] or [Vim][Vim word wrap]); 19 | * use reference-style links, like `[an example][Example]`. Put the links in 20 | alphabetical order at the end of the document, and capitalize the first word 21 | of the link label. 22 | 23 | Use Ruby and [Markdownlint] to check your changes: 24 | 25 | ```sh 26 | gem install mdl rake 27 | rake test 28 | ``` 29 | 30 | **IMPORTANT**: By submitting a patch, you agree that your work will be 31 | licensed under the license used by the project. 32 | 33 | ## The Project Board 34 | 35 | If you are looking for issues to work on, the [project board][Project KanBan] 36 | is the place to go. Usually, you look at the issues from right to left, as 37 | the ones in the rightmost part are the closer to get merged and have higher 38 | priority. 39 | 40 | If you just want to dive in and start writing, the backlog has the 41 | 'ready to be picked up' issues. These issues have been discussed already and 42 | are most likely just waiting for someone to make a PR. Just look for the 43 | issues with the `enhancement` and/or `help wanted` labels. 44 | 45 | ## Collaborators 46 | 47 | If you have contributed to the repository you can be appointed as a collaborator 48 | after submitting a change and getting it merged. Collaborators are invited to 49 | manage issues, make corrections to the style guide, review pull requests, and 50 | merge approved changes. 51 | 52 | 1. All changes must pass automatic checks before being merged. 53 | 1. Minor changes and corrections can be merged without review. 54 | 1. Significant changes or new style rules should be discussed and approved in a 55 | pull request. 56 | 57 | ## Translations 58 | 59 | If you would like to help translate the Style Guide, check if there is 60 | an [existing translation][Translations] to contribute to. To create a new 61 | translation: 62 | 63 | 1. [Fork] this repository. 64 | 1. Clone your fork locally. 65 | 1. Copy `README.md` to a new file named after the country/language, like 66 | `README-jaJP.md`, and commit your translations there. 67 | 1. Add the main [repo][Repo] as an upstream remote, to fetch and merge changes. 68 | 69 | 70 | [Emacs LineWrap]: http://emacswiki.org/emacs/LineWrap 71 | [Fork]: https://github.com/christopheradams/elixir_style_guide#fork-destination-box 72 | [Markdownlint]: https://github.com/mivok/markdownlint 73 | [Project KanBan]: https://github.com/christopheradams/elixir_style_guide/projects/1 74 | [Repo]: https://github.com/christopheradams/elixir_style_guide.git 75 | [Translations]: README.md#translations 76 | [Vim word wrap]: http://vim.wikia.com/wiki/Automatic_word_wrapping 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [The Elixir Style Guide][Elixir Style Guide] 2 | 3 | ## Table of Contents 4 | 5 | * __[Prelude](#prelude)__ 6 | * __[About](#about)__ 7 | * __[Formatting](#formatting)__ 8 | * [Whitespace](#whitespace) 9 | * [Indentation](#indentation) 10 | * [Parentheses](#parentheses) 11 | * __[The Guide](#the-guide)__ 12 | * [Expressions](#expressions) 13 | * [Naming](#naming) 14 | * [Comments](#comments) 15 | * [Comment Annotations](#comment-annotations) 16 | * [Modules](#modules) 17 | * [Documentation](#documentation) 18 | * [Typespecs](#typespecs) 19 | * [Structs](#structs) 20 | * [Exceptions](#exceptions) 21 | * [Collections](#collections) 22 | * [Strings](#strings) 23 | * _Regular Expressions_ 24 | * [Metaprogramming](#metaprogramming) 25 | * [Testing](#testing) 26 | * __[Resources](#resources)__ 27 | * [Alternative Style Guides](#alternative-style-guides) 28 | * [Tools](#tools) 29 | * __[Getting Involved](#getting-involved)__ 30 | * [Contributing](#contributing) 31 | * [Spread the Word](#spread-the-word) 32 | * __[Copying](#copying)__ 33 | * [License](#license) 34 | * [Attribution](#attribution) 35 | 36 | ## Prelude 37 | 38 | > Liquid architecture. It's like jazz — you improvise, you work together, you 39 | > play off each other, you make something, they make something. 40 | > 41 | > —Frank Gehry 42 | 43 | Style matters. 44 | [Elixir] has plenty of style but like all languages it can be stifled. 45 | Don't stifle the style. 46 | 47 | ## About 48 | 49 | This is community style guide for the [Elixir programming language][Elixir]. 50 | Please feel free to make pull requests and suggestions, and be a part of 51 | Elixir's vibrant community. 52 | 53 | If you're looking for other projects to contribute to please see the 54 | [Hex package manager site][Hex]. 55 | 56 | 57 | Translations of the guide are available in the following languages: 58 | 59 | * [Chinese Simplified] 60 | * [Chinese Traditional] 61 | * [French] 62 | * [Japanese] 63 | * [Korean] 64 | * [Portuguese] 65 | * [Russian] 66 | * [Spanish] 67 | * [Thai] 68 | 69 | ## Formatting 70 | 71 | Elixir v1.6 introduced a [Code Formatter] and [Mix format] task. 72 | The formatter should be preferred for all new projects and source code. 73 | 74 | The rules in this section are applied automatically by the code formatter, but 75 | are provided here as examples of the preferred style. 76 | 77 | ### Whitespace 78 | 79 | * 80 | Avoid trailing whitespace. 81 | [[link](#trailing-whitespace)] 82 | 83 | * 84 | End each file with a newline. 85 | [[link](#newline-eof)] 86 | 87 | * 88 | Use Unix-style line endings (\*BSD/Solaris/Linux/OSX users are covered by 89 | default, Windows users have to be extra careful). 90 | [[link](#line-endings)] 91 | 92 | * 93 | If you're using Git you might want to add the following configuration 94 | setting to protect your project from Windows line endings creeping in: 95 | [[link](#autocrlf)] 96 | 97 | ```sh 98 | git config --global core.autocrlf true 99 | ``` 100 | 101 | * 102 | Limit lines to 98 characters. 103 | Otherwise, set the `:line_length` option in your `.formatter.exs` file. 104 | [[link](#line-length)] 105 | 106 | * 107 | Use spaces around operators, after commas, colons and semicolons. 108 | Do not put spaces around matched pairs like brackets, parentheses, etc. 109 | Whitespace might be (mostly) irrelevant to the Elixir runtime, but its proper 110 | use is the key to writing easily readable code. 111 | [[link](#spaces)] 112 | 113 | ```elixir 114 | sum = 1 + 2 115 | {a, b} = {2, 3} 116 | [first | rest] = [1, 2, 3] 117 | Enum.map(["one", <<"two">>, "three"], fn num -> IO.puts(num) end) 118 | ``` 119 | 120 | * 121 | Do not use spaces after non-word operators that only take one argument; or 122 | around the range operator. 123 | [[link](#no-spaces)] 124 | 125 | ```elixir 126 | 0 - 1 == -1 127 | ^pinned = some_func() 128 | 5 in 1..10 129 | ``` 130 | 131 | * 132 | Use blank lines between `def`s to break up a function into logical 133 | paragraphs. 134 | [[link](#def-spacing)] 135 | 136 | ```elixir 137 | def some_function(some_data) do 138 | some_data |> other_function() |> List.first() 139 | end 140 | 141 | def some_function do 142 | result 143 | end 144 | 145 | def some_other_function do 146 | another_result 147 | end 148 | 149 | def a_longer_function do 150 | one 151 | two 152 | 153 | three 154 | four 155 | end 156 | ``` 157 | 158 | * 159 | Don't put a blank line after `defmodule`. 160 | [[link](#defmodule-spacing)] 161 | 162 | * 163 | If the function head and `do:` clause are too long to fit on the same line, put 164 | `do:` on a new line, indented one level more than the previous line. 165 | [[link](#long-dos)] 166 | 167 | ```elixir 168 | def some_function([:foo, :bar, :baz] = args), 169 | do: Enum.map(args, fn arg -> arg <> " is on a very long line!" end) 170 | ``` 171 | 172 | When the `do:` clause starts on its own line, treat it as a multiline 173 | function by separating it with blank lines. 174 | 175 | ```elixir 176 | # not preferred 177 | def some_function([]), do: :empty 178 | def some_function(_), 179 | do: :very_long_line_here 180 | 181 | # preferred 182 | def some_function([]), do: :empty 183 | 184 | def some_function(_), 185 | do: :very_long_line_here 186 | ``` 187 | 188 | * 189 | Add a blank line after a multiline assignment as a 190 | visual cue that the assignment is 'over'. 191 | [[link](#add-blank-line-after-multiline-assignment)] 192 | 193 | ```elixir 194 | # not preferred 195 | some_string = 196 | "Hello" 197 | |> String.downcase() 198 | |> String.trim() 199 | another_string <> some_string 200 | 201 | # preferred 202 | some_string = 203 | "Hello" 204 | |> String.downcase() 205 | |> String.trim() 206 | 207 | another_string <> some_string 208 | ``` 209 | 210 | ```elixir 211 | # also not preferred 212 | something = 213 | if x == 2 do 214 | "Hi" 215 | else 216 | "Bye" 217 | end 218 | String.downcase(something) 219 | 220 | # preferred 221 | something = 222 | if x == 2 do 223 | "Hi" 224 | else 225 | "Bye" 226 | end 227 | 228 | String.downcase(something) 229 | ``` 230 | 231 | * 232 | If a list, map, or struct spans multiple lines, put each element, as well as 233 | the opening and closing brackets, on its own line. 234 | Indent each element one level, but not the brackets. 235 | [[link](#multiline-enums)] 236 | 237 | ```elixir 238 | # not preferred 239 | [:first_item, :second_item, :next_item, 240 | :final_item] 241 | 242 | # preferred 243 | [ 244 | :first_item, 245 | :second_item, 246 | :next_item, 247 | :final_item 248 | ] 249 | ``` 250 | 251 | * 252 | When assigning a list, map, or struct, keep the opening bracket on the same 253 | line as the assignment. 254 | [[link](#multiline-list-assign)] 255 | 256 | ```elixir 257 | # not preferred 258 | list = 259 | [ 260 | :first_item, 261 | :second_item 262 | ] 263 | 264 | # preferred 265 | list = [ 266 | :first_item, 267 | :second_item 268 | ] 269 | ``` 270 | 271 | * 272 | If any `case` or `cond` clause needs more than one line (due to line length, 273 | multiple expressions in the clause body, etc.), use multi-line syntax for all 274 | clauses, and separate each one with a blank line. 275 | [[link](#multiline-case-clauses)] 276 | 277 | ```elixir 278 | # not preferred 279 | case arg do 280 | true -> IO.puts("ok"); :ok 281 | false -> :error 282 | end 283 | 284 | # not preferred 285 | case arg do 286 | true -> 287 | IO.puts("ok") 288 | :ok 289 | false -> :error 290 | end 291 | 292 | # preferred 293 | case arg do 294 | true -> 295 | IO.puts("ok") 296 | :ok 297 | 298 | false -> 299 | :error 300 | end 301 | ``` 302 | 303 | * 304 | Place comments above the line they comment on. 305 | [[link](#comments-above-line)] 306 | 307 | ```elixir 308 | String.first(some_string) # not preferred 309 | 310 | # preferred 311 | String.first(some_string) 312 | ``` 313 | 314 | * 315 | Use one space between the leading `#` character of the comment and the text of 316 | the comment. 317 | [[link](#comment-leading-spaces)] 318 | 319 | ```elixir 320 | #not preferred 321 | String.first(some_string) 322 | 323 | # preferred 324 | String.first(some_string) 325 | ``` 326 | 327 | ### Indentation 328 | 329 | * 330 | Indent and align successive `with` clauses. 331 | Put the `do:` argument on a new line, aligned with the previous clauses. 332 | [[link](#with-clauses)] 333 | 334 | ```elixir 335 | with {:ok, foo} <- fetch(opts, :foo), 336 | {:ok, my_var} <- fetch(opts, :my_var), 337 | do: {:ok, foo, my_var} 338 | ``` 339 | 340 | * 341 | If the `with` expression has a `do` block with more than one line, or has an 342 | `else` option, use multiline syntax. 343 | [[link](#with-else)] 344 | 345 | ```elixir 346 | with {:ok, foo} <- fetch(opts, :foo), 347 | {:ok, my_var} <- fetch(opts, :my_var) do 348 | {:ok, foo, my_var} 349 | else 350 | :error -> 351 | {:error, :bad_arg} 352 | end 353 | ``` 354 | 355 | ### Parentheses 356 | 357 | * 358 | Use parentheses for one-arity functions when using the pipe operator (`|>`). 359 | [[link](#parentheses-pipe-operator)] 360 | 361 | ```elixir 362 | # not preferred 363 | some_string |> String.downcase |> String.trim 364 | 365 | # preferred 366 | some_string |> String.downcase() |> String.trim() 367 | ``` 368 | 369 | * 370 | Never put a space between a function name and the opening parenthesis. 371 | [[link](#function-names-with-parentheses)] 372 | 373 | ```elixir 374 | # not preferred 375 | f (3 + 2) 376 | 377 | # preferred 378 | f(3 + 2) 379 | ``` 380 | 381 | * 382 | Use parentheses in function calls, especially inside a pipeline. 383 | [[link](#function-calls-and-parentheses)] 384 | 385 | ```elixir 386 | # not preferred 387 | f 3 388 | 389 | # preferred 390 | f(3) 391 | 392 | # not preferred and parses as rem(2, (3 |> g)), which is not what you want. 393 | 2 |> rem 3 |> g 394 | 395 | # preferred 396 | 2 |> rem(3) |> g() 397 | ``` 398 | 399 | * 400 | Omit square brackets from keyword lists whenever they are optional. 401 | [[link](#keyword-list-brackets)] 402 | 403 | ```elixir 404 | # not preferred 405 | some_function(foo, bar, [a: "baz", b: "qux"]) 406 | 407 | # preferred 408 | some_function(foo, bar, a: "baz", b: "qux") 409 | ``` 410 | 411 | ## The Guide 412 | 413 | The rules in this section may not be applied by the code formatter, but they are 414 | generally preferred practice. 415 | 416 | ### Expressions 417 | 418 | * 419 | Run single-line `def`s that match for the same function together, but separate 420 | multiline `def`s with a blank line. 421 | [[link](#single-line-defs)] 422 | 423 | ```elixir 424 | def some_function(nil), do: {:error, "No Value"} 425 | def some_function([]), do: :ok 426 | 427 | def some_function([first | rest]) do 428 | some_function(rest) 429 | end 430 | ``` 431 | 432 | * 433 | If you have more than one multiline `def`, do not use single-line `def`s. 434 | [[link](#multiple-function-defs)] 435 | 436 | ```elixir 437 | def some_function(nil) do 438 | {:error, "No Value"} 439 | end 440 | 441 | def some_function([]) do 442 | :ok 443 | end 444 | 445 | def some_function([first | rest]) do 446 | some_function(rest) 447 | end 448 | 449 | def some_function([first | rest], opts) do 450 | some_function(rest, opts) 451 | end 452 | ``` 453 | 454 | * 455 | Use the pipe operator to chain functions together. 456 | [[link](#pipe-operator)] 457 | 458 | ```elixir 459 | # not preferred 460 | String.trim(String.downcase(some_string)) 461 | 462 | # preferred 463 | some_string |> String.downcase() |> String.trim() 464 | 465 | # Multiline pipelines are not further indented 466 | some_string 467 | |> String.downcase() 468 | |> String.trim() 469 | 470 | # Multiline pipelines on the right side of a pattern match 471 | # should be indented on a new line 472 | sanitized_string = 473 | some_string 474 | |> String.downcase() 475 | |> String.trim() 476 | ``` 477 | 478 | While this is the preferred method, take into account that copy-pasting 479 | multiline pipelines into IEx might result in a syntax error, as IEx will 480 | evaluate the first line without realizing that the next line has a pipeline. 481 | To avoid this, you can wrap the pasted code in parentheses. 482 | 483 | * 484 | Avoid using the pipe operator just once. 485 | [[link](#avoid-single-pipelines)] 486 | 487 | ```elixir 488 | # not preferred 489 | some_string |> String.downcase() 490 | 491 | System.version() |> Version.parse() 492 | 493 | # preferred 494 | String.downcase(some_string) 495 | 496 | Version.parse(System.version()) 497 | ``` 498 | 499 | * 500 | Use _bare_ variables in the first part of a function chain. 501 | [[link](#bare-variables)] 502 | 503 | ```elixir 504 | # not preferred 505 | String.trim(some_string) |> String.downcase() |> String.codepoints() 506 | 507 | # preferred 508 | some_string |> String.trim() |> String.downcase() |> String.codepoints() 509 | ``` 510 | 511 | * 512 | Use parentheses when a `def` has arguments, and omit them when it doesn't. 513 | [[link](#fun-def-parentheses)] 514 | 515 | ```elixir 516 | # not preferred 517 | def some_function arg1, arg2 do 518 | # body omitted 519 | end 520 | 521 | def some_function() do 522 | # body omitted 523 | end 524 | 525 | # preferred 526 | def some_function(arg1, arg2) do 527 | # body omitted 528 | end 529 | 530 | def some_function do 531 | # body omitted 532 | end 533 | ``` 534 | 535 | * 536 | Use `do:` for single line `if/unless` statements. 537 | [[link](#do-with-single-line-if-unless)] 538 | 539 | ```elixir 540 | # preferred 541 | if some_condition, do: # some_stuff 542 | ``` 543 | 544 | * 545 | Never use `unless` with `else`. 546 | Rewrite these with the positive case first. 547 | [[link](#unless-with-else)] 548 | 549 | ```elixir 550 | # not preferred 551 | unless success do 552 | IO.puts('failure') 553 | else 554 | IO.puts('success') 555 | end 556 | 557 | # preferred 558 | if success do 559 | IO.puts('success') 560 | else 561 | IO.puts('failure') 562 | end 563 | ``` 564 | 565 | * 566 | Use `true` as the last condition of the `cond` special form when you need a 567 | clause that always matches. 568 | [[link](#true-as-last-condition)] 569 | 570 | ```elixir 571 | # not preferred 572 | cond do 573 | 1 + 2 == 5 -> 574 | "Nope" 575 | 576 | 1 + 3 == 5 -> 577 | "Uh, uh" 578 | 579 | :else -> 580 | "OK" 581 | end 582 | 583 | # preferred 584 | cond do 585 | 1 + 2 == 5 -> 586 | "Nope" 587 | 588 | 1 + 3 == 5 -> 589 | "Uh, uh" 590 | 591 | true -> 592 | "OK" 593 | end 594 | ``` 595 | 596 | * 597 | Use parentheses for calls to functions with zero arity, so they can be 598 | distinguished from variables. 599 | Starting in Elixir 1.4, the compiler will warn you about 600 | locations where this ambiguity exists. 601 | [[link](#parentheses-and-functions-with-zero-arity)] 602 | 603 | ```elixir 604 | defp do_stuff, do: ... 605 | 606 | # not preferred 607 | def my_func do 608 | # is this a variable or a function call? 609 | do_stuff 610 | end 611 | 612 | # preferred 613 | def my_func do 614 | # this is clearly a function call 615 | do_stuff() 616 | end 617 | ``` 618 | 619 | ### Naming 620 | 621 | This guide follows the [Naming Conventions] from the Elixir docs, 622 | including the use of `snake_case` and `CamelCase` to describe the casing 623 | rules. 624 | 625 | * 626 | Use `snake_case` for atoms, functions and variables. 627 | [[link](#snake-case)] 628 | 629 | ```elixir 630 | # not preferred 631 | :"some atom" 632 | :SomeAtom 633 | :someAtom 634 | 635 | someVar = 5 636 | 637 | def someFunction do 638 | ... 639 | end 640 | 641 | # preferred 642 | :some_atom 643 | 644 | some_var = 5 645 | 646 | def some_function do 647 | ... 648 | end 649 | ``` 650 | 651 | * 652 | Use `CamelCase` for modules (keep acronyms like HTTP, RFC, XML uppercase). 653 | [[link](#camel-case)] 654 | 655 | ```elixir 656 | # not preferred 657 | defmodule Somemodule do 658 | ... 659 | end 660 | 661 | defmodule Some_Module do 662 | ... 663 | end 664 | 665 | defmodule SomeXml do 666 | ... 667 | end 668 | 669 | # preferred 670 | defmodule SomeModule do 671 | ... 672 | end 673 | 674 | defmodule SomeXML do 675 | ... 676 | end 677 | ``` 678 | 679 | * 680 | Functions that return a boolean (`true` or `false`) should be named 681 | with a trailing question mark. 682 | [[link](#predicate-function-trailing-question-mark)] 683 | 684 | ```elixir 685 | def cool?(var) do 686 | String.contains?(var, "cool") 687 | end 688 | ``` 689 | 690 | * 691 | Boolean checks that can be used in guard clauses should be named with 692 | an `is_` prefix. 693 | For a list of allowed expressions, see the [Guard][Guard Expressions] docs. 694 | [[link](#predicate-function-is-prefix)] 695 | 696 | ```elixir 697 | defguard is_cool(var) when var == "cool" 698 | defguard is_very_cool(var) when var == "very cool" 699 | ``` 700 | 701 | * 702 | Private functions should not have the same name as public functions. 703 | Also, the `def name` and `defp do_name` pattern is discouraged. 704 | 705 | Usually one can try to find more descriptive names focusing on the differences. 706 | [[link](#private-functions-with-same-name-as-public)] 707 | 708 | ```elixir 709 | def sum(list), do: sum_total(list, 0) 710 | 711 | # private functions 712 | defp sum_total([], total), do: total 713 | defp sum_total([head | tail], total), do: sum_total(tail, head + total) 714 | ``` 715 | 716 | ### Comments 717 | 718 | * 719 | Write expressive code and try to convey your program's intention through 720 | control-flow, structure and naming. 721 | [[link](#expressive-code)] 722 | 723 | * 724 | Comments longer than a word are capitalized, and sentences use punctuation. 725 | Use [one space][Sentence Spacing] after periods. 726 | [[link](#comment-grammar)] 727 | 728 | ```elixir 729 | # not preferred 730 | # these lowercase comments are missing punctuation 731 | 732 | # preferred 733 | # Capitalization example 734 | # Use punctuation for complete sentences. 735 | ``` 736 | 737 | * 738 | Limit comment lines to 100 characters. 739 | [[link](#comment-line-length)] 740 | 741 | #### Comment Annotations 742 | 743 | * 744 | Annotations should usually be written on the line immediately above the 745 | relevant code. 746 | [[link](#annotations)] 747 | 748 | * 749 | The annotation keyword is uppercase, and is followed by a colon and a space, 750 | then a note describing the problem. 751 | [[link](#annotation-keyword)] 752 | 753 | ```elixir 754 | # TODO: Deprecate in v1.5. 755 | def some_function(arg), do: {:ok, arg} 756 | ``` 757 | 758 | * 759 | In cases where the problem is so obvious that any documentation would be 760 | redundant, annotations may be left with no note. 761 | This usage should be the exception and not the rule. 762 | [[link](#exceptions-to-annotations)] 763 | 764 | ```elixir 765 | start_task() 766 | 767 | # FIXME 768 | Process.sleep(5000) 769 | ``` 770 | 771 | * 772 | Use `TODO` to note missing features or functionality that should be added at a 773 | later date. 774 | [[link](#todo-notes)] 775 | 776 | * 777 | Use `FIXME` to note broken code that needs to be fixed. 778 | [[link](#fixme-notes)] 779 | 780 | * 781 | Use `OPTIMIZE` to note slow or inefficient code that may cause performance 782 | problems. 783 | [[link](#optimize-notes)] 784 | 785 | * 786 | Use `HACK` to note code smells where questionable coding practices were used 787 | and should be refactored away. 788 | [[link](#hack-notes)] 789 | 790 | * 791 | Use `REVIEW` to note anything that should be looked at to confirm it is 792 | working as intended. 793 | For example: `REVIEW: Are we sure this is how the client does X currently?` 794 | [[link](#review-notes)] 795 | 796 | * 797 | Use other custom annotation keywords if it feels appropriate, but be sure to 798 | document them in your project's `README` or similar. 799 | [[link](#custom-keywords)] 800 | 801 | ### Modules 802 | 803 | * 804 | Use one module per file unless the module is only used internally by another 805 | module (such as a test). 806 | [[link](#one-module-per-file)] 807 | 808 | * 809 | Use `snake_case` file names for `CamelCase` module names. 810 | [[link](#underscored-filenames)] 811 | 812 | ```elixir 813 | # file is called some_module.ex 814 | 815 | defmodule SomeModule do 816 | end 817 | ``` 818 | 819 | * 820 | Represent each level of nesting within a module name as a directory. 821 | [[link](#module-name-nesting)] 822 | 823 | ```elixir 824 | # file is called parser/core/xml_parser.ex 825 | 826 | defmodule Parser.Core.XMLParser do 827 | end 828 | ``` 829 | 830 | * 831 | List module attributes, directives, and macros in the following order: 832 | [[link](#module-attribute-ordering)] 833 | 834 | 1. `@moduledoc` 835 | 1. `@behaviour` 836 | 1. `use` 837 | 1. `import` 838 | 1. `require` 839 | 1. `alias` 840 | 1. `@module_attribute` 841 | 1. `defstruct` 842 | 1. `@type` 843 | 1. `@callback` 844 | 1. `@macrocallback` 845 | 1. `@optional_callbacks` 846 | 1. `defmacro`, `defmodule`, `defguard`, `def`, etc. 847 | 848 | Add a blank line between each grouping, and sort the terms (like module names) 849 | alphabetically. 850 | Here's an overall example of how you should order things in your modules: 851 | 852 | ```elixir 853 | defmodule MyModule do 854 | @moduledoc """ 855 | An example module 856 | """ 857 | 858 | @behaviour MyBehaviour 859 | 860 | use GenServer 861 | 862 | import Something 863 | import SomethingElse 864 | 865 | require Integer 866 | 867 | alias My.Long.Module.Name 868 | alias My.Other.Module.Example 869 | 870 | @module_attribute :foo 871 | @other_attribute 100 872 | 873 | defstruct [:name, params: []] 874 | 875 | @type params :: [{binary, binary}] 876 | 877 | @callback some_function(term) :: :ok | {:error, term} 878 | 879 | @macrocallback macro_name(term) :: Macro.t() 880 | 881 | @optional_callbacks macro_name: 1 882 | 883 | @doc false 884 | defmacro __using__(_opts), do: :no_op 885 | 886 | @doc """ 887 | Determines when a term is `:ok`. Allowed in guards. 888 | """ 889 | defguard is_ok(term) when term == :ok 890 | 891 | @impl true 892 | def init(state), do: {:ok, state} 893 | 894 | # Define other functions here. 895 | end 896 | ``` 897 | 898 | * 899 | Use the `__MODULE__` pseudo variable when a module refers to itself. This 900 | avoids having to update any self-references when the module name changes. 901 | [[link](#module-pseudo-variable)] 902 | 903 | ```elixir 904 | defmodule SomeProject.SomeModule do 905 | defstruct [:name] 906 | 907 | def name(%__MODULE__{name: name}), do: name 908 | end 909 | ``` 910 | 911 | * 912 | If you want a prettier name for a module self-reference, set up an alias. 913 | [[link](#alias-self-referencing-modules)] 914 | 915 | ```elixir 916 | defmodule SomeProject.SomeModule do 917 | alias __MODULE__, as: SomeModule 918 | 919 | defstruct [:name] 920 | 921 | def name(%SomeModule{name: name}), do: name 922 | end 923 | ``` 924 | 925 | * 926 | Avoid repeating fragments in module names and namespaces. 927 | This improves overall readability and 928 | eliminates [ambiguous aliases][Conflicting Aliases]. 929 | [[link](#repetitive-module-names)] 930 | 931 | ```elixir 932 | # not preferred 933 | defmodule Todo.Todo do 934 | ... 935 | end 936 | 937 | # preferred 938 | defmodule Todo.Item do 939 | ... 940 | end 941 | ``` 942 | 943 | ### Documentation 944 | 945 | Documentation in Elixir (when read either in `iex` with `h` or generated with 946 | [ExDoc]) uses the [Module Attributes] `@moduledoc` and `@doc`. 947 | 948 | * 949 | Always include a `@moduledoc` attribute in the line right after `defmodule` in 950 | your module. 951 | [[link](#moduledocs)] 952 | 953 | ```elixir 954 | # not preferred 955 | 956 | defmodule AnotherModule do 957 | use SomeModule 958 | 959 | @moduledoc """ 960 | About the module 961 | """ 962 | ... 963 | end 964 | 965 | # preferred 966 | 967 | defmodule AThirdModule do 968 | @moduledoc """ 969 | About the module 970 | """ 971 | 972 | use SomeModule 973 | ... 974 | end 975 | ``` 976 | 977 | * 978 | Use `@moduledoc false` if you do not intend on documenting the module. 979 | [[link](#moduledoc-false)] 980 | 981 | ```elixir 982 | defmodule SomeModule do 983 | @moduledoc false 984 | ... 985 | end 986 | ``` 987 | 988 | * 989 | Separate code after the `@moduledoc` with a blank line. 990 | [[link](#moduledoc-spacing)] 991 | 992 | ```elixir 993 | # not preferred 994 | defmodule SomeModule do 995 | @moduledoc """ 996 | About the module 997 | """ 998 | use AnotherModule 999 | end 1000 | 1001 | # preferred 1002 | defmodule SomeModule do 1003 | @moduledoc """ 1004 | About the module 1005 | """ 1006 | 1007 | use AnotherModule 1008 | end 1009 | ``` 1010 | 1011 | * 1012 | Use heredocs with markdown for documentation. 1013 | [[link](#heredocs)] 1014 | 1015 | ```elixir 1016 | # not preferred 1017 | defmodule SomeModule do 1018 | @moduledoc "About the module" 1019 | end 1020 | 1021 | defmodule SomeModule do 1022 | @moduledoc """ 1023 | About the module 1024 | 1025 | Examples: 1026 | iex> SomeModule.some_function 1027 | :result 1028 | """ 1029 | end 1030 | 1031 | # preferred 1032 | defmodule SomeModule do 1033 | @moduledoc """ 1034 | About the module 1035 | 1036 | ## Examples 1037 | 1038 | iex> SomeModule.some_function 1039 | :result 1040 | """ 1041 | end 1042 | ``` 1043 | 1044 | ### Typespecs 1045 | 1046 | Typespecs are notation for declaring types and specifications, for 1047 | documentation or for the static analysis tool Dialyzer. 1048 | 1049 | Custom types should be defined at the top of the module with the other 1050 | directives (see [Modules](#modules)). 1051 | 1052 | * 1053 | Place `@typedoc` and `@type` definitions together, and separate each 1054 | pair with a blank line. 1055 | [[link](#typedocs)] 1056 | 1057 | ```elixir 1058 | defmodule SomeModule do 1059 | @moduledoc false 1060 | 1061 | @typedoc "The name" 1062 | @type name :: atom 1063 | 1064 | @typedoc "The result" 1065 | @type result :: {:ok, term} | {:error, term} 1066 | 1067 | ... 1068 | end 1069 | ``` 1070 | 1071 | * 1072 | If a union type is too long to fit on a single line, put each part of the 1073 | type on a separate line, indented one level past the name of the type. 1074 | [[link](#union-types)] 1075 | 1076 | ```elixir 1077 | # not preferred 1078 | @type long_union_type :: 1079 | some_type | another_type | some_other_type | one_more_type | a_final_type 1080 | 1081 | # preferred 1082 | @type long_union_type :: 1083 | some_type 1084 | | another_type 1085 | | some_other_type 1086 | | one_more_type 1087 | | a_final_type 1088 | ``` 1089 | 1090 | * 1091 | Name the main type for a module `t`, for example: the type specification for a 1092 | struct. 1093 | [[link](#naming-main-types)] 1094 | 1095 | ```elixir 1096 | defstruct [:name, params: []] 1097 | 1098 | @type t :: %__MODULE__{ 1099 | name: String.t() | nil, 1100 | params: Keyword.t() 1101 | } 1102 | ``` 1103 | 1104 | * 1105 | Place specifications right before the function definition, 1106 | after the `@doc`, 1107 | without separating them by a blank line. 1108 | [[link](#spec-spacing)] 1109 | 1110 | ```elixir 1111 | @doc """ 1112 | Some function description. 1113 | """ 1114 | @spec some_function(term) :: result 1115 | def some_function(some_data) do 1116 | {:ok, some_data} 1117 | end 1118 | ``` 1119 | 1120 | ### Structs 1121 | 1122 | * 1123 | Use a list of atoms for struct fields that default to `nil`, followed by the 1124 | other keywords. 1125 | [[link](#nil-struct-field-defaults)] 1126 | 1127 | ```elixir 1128 | # not preferred 1129 | defstruct name: nil, params: nil, active: true 1130 | 1131 | # preferred 1132 | defstruct [:name, :params, active: true] 1133 | ``` 1134 | 1135 | * 1136 | Omit square brackets when the argument of a `defstruct` is a keyword list. 1137 | [[link](#struct-def-brackets)] 1138 | 1139 | ```elixir 1140 | # not preferred 1141 | defstruct [params: [], active: true] 1142 | 1143 | # preferred 1144 | defstruct params: [], active: true 1145 | 1146 | # required - brackets are not optional, with at least one atom in the list 1147 | defstruct [:name, params: [], active: true] 1148 | ``` 1149 | 1150 | * 1151 | If a struct definition spans multiple lines, put each element on its own line, 1152 | keeping the elements aligned. 1153 | [[link](#multiline-structs)] 1154 | 1155 | ```elixir 1156 | defstruct foo: "test", 1157 | bar: true, 1158 | baz: false, 1159 | qux: false, 1160 | quux: 1 1161 | ``` 1162 | 1163 | If a multiline struct requires brackets, format it as a multiline list: 1164 | 1165 | ```elixir 1166 | defstruct [ 1167 | :name, 1168 | params: [], 1169 | active: true 1170 | ] 1171 | ``` 1172 | 1173 | ### Exceptions 1174 | 1175 | * 1176 | Make exception names end with a trailing `Error`. 1177 | [[link](#exception-names)] 1178 | 1179 | ```elixir 1180 | # not preferred 1181 | defmodule BadHTTPCode do 1182 | defexception [:message] 1183 | end 1184 | 1185 | defmodule BadHTTPCodeException do 1186 | defexception [:message] 1187 | end 1188 | 1189 | # preferred 1190 | defmodule BadHTTPCodeError do 1191 | defexception [:message] 1192 | end 1193 | ``` 1194 | 1195 | * 1196 | Use lowercase error messages when raising exceptions, with no trailing 1197 | punctuation. 1198 | [[link](#lowercase-error-messages)] 1199 | 1200 | ```elixir 1201 | # not preferred 1202 | raise ArgumentError, "This is not valid." 1203 | 1204 | # preferred 1205 | raise ArgumentError, "this is not valid" 1206 | ``` 1207 | 1208 | ### Collections 1209 | 1210 | * 1211 | Always use the special syntax for keyword lists. 1212 | [[link](#keyword-list-syntax)] 1213 | 1214 | ```elixir 1215 | # not preferred 1216 | some_value = [{:a, "baz"}, {:b, "qux"}] 1217 | 1218 | # preferred 1219 | some_value = [a: "baz", b: "qux"] 1220 | ``` 1221 | 1222 | * 1223 | Use the shorthand key-value syntax for maps when all of the keys are atoms. 1224 | [[link](#map-key-atom)] 1225 | 1226 | ```elixir 1227 | # not preferred 1228 | %{:a => 1, :b => 2, :c => 0} 1229 | 1230 | # preferred 1231 | %{a: 1, b: 2, c: 3} 1232 | ``` 1233 | 1234 | * 1235 | Use the verbose key-value syntax for maps if any key is not an atom. 1236 | [[link](#map-key-arrow)] 1237 | 1238 | ```elixir 1239 | # not preferred 1240 | %{"c" => 0, a: 1, b: 2} 1241 | 1242 | # preferred 1243 | %{:a => 1, :b => 2, "c" => 0} 1244 | ``` 1245 | 1246 | ### Strings 1247 | 1248 | * 1249 | Match strings using the string concatenator rather than binary patterns: 1250 | [[link](#strings-matching-with-concatenator)] 1251 | 1252 | ```elixir 1253 | # not preferred 1254 | <<"my"::utf8, _rest::bytes>> = "my string" 1255 | 1256 | # preferred 1257 | "my" <> _rest = "my string" 1258 | ``` 1259 | 1260 | ### Regular Expressions 1261 | 1262 | _No guidelines for regular expressions have been added yet._ 1263 | 1264 | ### Metaprogramming 1265 | 1266 | * 1267 | Avoid needless metaprogramming. 1268 | [[link](#avoid-metaprogramming)] 1269 | 1270 | ### Testing 1271 | 1272 | * 1273 | When writing [ExUnit] assertions, put the expression being tested to the left 1274 | of the operator, and the expected result to the right, unless the assertion is 1275 | a pattern match. 1276 | [[link](#testing-assert-order)] 1277 | 1278 | ```elixir 1279 | # preferred 1280 | assert actual_function(1) == true 1281 | 1282 | # not preferred 1283 | assert true == actual_function(1) 1284 | 1285 | # required - the assertion is a pattern match 1286 | assert {:ok, expected} = actual_function(3) 1287 | ``` 1288 | 1289 | ## Resources 1290 | 1291 | ### Alternative Style Guides 1292 | 1293 | * [Aleksei Magusev's Elixir Style Guide](https://github.com/lexmag/elixir-style-guide#readme) 1294 | — An opinionated Elixir style guide stemming from the coding style practiced 1295 | in the Elixir core libraries. 1296 | Developed by [Aleksei Magusev](https://github.com/lexmag) and 1297 | [Andrea Leopardi](https://github.com/whatyouhide), members of Elixir core team. 1298 | While the Elixir project doesn't adhere to any specific style guide, 1299 | this is the closest available guide to its conventions. 1300 | 1301 | * [Credo's Elixir Style Guide](https://github.com/rrrene/elixir-style-guide#readme) 1302 | — Style Guide for the Elixir language, implemented by 1303 | [Credo](http://credo-ci.org) static code analysis tool. 1304 | 1305 | ### Tools 1306 | 1307 | Refer to [Awesome Elixir][Code Analysis] for libraries and tools that can help 1308 | with code analysis and style linting. 1309 | 1310 | ## Getting Involved 1311 | 1312 | ### Contributing 1313 | 1314 | It's our hope that this will become a central hub for community discussion on 1315 | best practices in Elixir. 1316 | Feel free to open tickets or send pull requests with improvements. 1317 | Thanks in advance for your help! 1318 | 1319 | Check the [contributing guidelines][Contributing] for more information. 1320 | 1321 | ### Spread the Word 1322 | 1323 | A community style guide is meaningless without the community's support. Please 1324 | tweet, [star][Stargazers], and let any Elixir programmer know 1325 | about [this guide][Elixir Style Guide] so they can contribute. 1326 | 1327 | ## Copying 1328 | 1329 | ### License 1330 | 1331 | ![Creative Commons License](http://i.creativecommons.org/l/by/3.0/88x31.png) 1332 | This work is licensed under a 1333 | [Creative Commons Attribution 3.0 Unported License][License] 1334 | 1335 | ### Attribution 1336 | 1337 | The structure of this guide, bits of example code, and many of the initial 1338 | points made in this document were borrowed from the [Ruby community style guide]. 1339 | A lot of things were applicable to Elixir and allowed us to get _some_ document 1340 | out quicker to start the conversation. 1341 | 1342 | Here's the [list of people who have kindly contributed][Contributors] to this 1343 | project. 1344 | 1345 | 1346 | [Chinese Simplified]: https://github.com/geekerzp/elixir_style_guide/blob/master/README-zhCN.md 1347 | [Chinese Traditional]: https://github.com/elixirtw/elixir_style_guide/blob/master/README_zhTW.md 1348 | [Code Analysis]: https://github.com/h4cc/awesome-elixir#code-analysis 1349 | [Code Of Conduct]: https://github.com/elixir-lang/elixir/blob/master/CODE_OF_CONDUCT.md 1350 | [Code Formatter]: https://hexdocs.pm/elixir/Code.html#format_string!/2 1351 | [Conflicting Aliases]: https://elixirforum.com/t/using-aliases-for-fubar-fubar-named-module/1723 1352 | [Contributing]: https://github.com/christopheradams/elixir_style_guide/blob/master/CONTRIBUTING.md 1353 | [Contributors]: https://github.com/christopheradams/elixir_style_guide/graphs/contributors 1354 | [Elixir Style Guide]: https://github.com/christopheradams/elixir_style_guide 1355 | [Elixir]: http://elixir-lang.org 1356 | [ExDoc]: https://github.com/elixir-lang/ex_doc 1357 | [ExUnit]: https://hexdocs.pm/ex_unit/ExUnit.html 1358 | [French]: https://github.com/ronanboiteau/elixir_style_guide/blob/master/README_frFR.md 1359 | [Guard Expressions]: https://hexdocs.pm/elixir/patterns-and-guards.html#list-of-allowed-functions-and-operators 1360 | [Hex]: https://hex.pm/packages 1361 | [Japanese]: https://github.com/kenichirow/elixir_style_guide/blob/master/README-jaJP.md 1362 | [Korean]: https://github.com/marocchino/elixir_style_guide/blob/new-korean/README-koKR.md 1363 | [License]: http://creativecommons.org/licenses/by/3.0/deed.en_US 1364 | [Mix format]: https://hexdocs.pm/mix/Mix.Tasks.Format.html 1365 | [Module Attributes]: http://elixir-lang.org/getting-started/module-attributes.html#as-annotations 1366 | [Naming Conventions]: https://hexdocs.pm/elixir/naming-conventions.html 1367 | [Portuguese]: https://github.com/gusaiani/elixir_style_guide/blob/master/README_ptBR.md 1368 | [Ruby community style guide]: https://github.com/bbatsov/ruby-style-guide 1369 | [Russian]: https://github.com/sofialapteva/elixir_style_guide/blob/russian/README_ru.md 1370 | [Sentence Spacing]: http://en.wikipedia.org/wiki/Sentence_spacing 1371 | [Spanish]: https://github.com/iver/elixir_style_guide/blob/spanish/i18n/README_es.md 1372 | [Stargazers]: https://github.com/christopheradams/elixir_style_guide/stargazers 1373 | [Thai]: https://github.com/tamectosphere/elixir_style_guide/blob/feat/thai-translation/README_th.md 1374 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | task :test do 2 | exit_code = 0 3 | files = ["README.md", "CONTRIBUTING.md"] 4 | # 'MD036' # Emphasis used instead of a header 5 | # 'MD033' # Inline HTML - allow for anchor links in each bullet point 6 | rules = ['~MD036', '~MD033'].join(",") 7 | files.each do |file| 8 | begin 9 | sh "mdl --rules #{rules} #{file}" 10 | rescue Exception => ex 11 | exit_code = 1 12 | end 13 | end 14 | 15 | exit exit_code 16 | end 17 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule ElixirStyleGuide.Mixfile do 2 | use Mix.Project 3 | 4 | @project_description """ 5 | A community driven style guide for Elixir 6 | """ 7 | 8 | @version "0.2.0-dev" 9 | @source_url "https://github.com/christopheradams/elixir_style_guide" 10 | 11 | def project do 12 | [ 13 | app: :elixir_style_guide, 14 | version: @version, 15 | elixir: "~> 1.0", 16 | build_embedded: Mix.env() == :prod, 17 | start_permanent: Mix.env() == :prod, 18 | docs: docs(), 19 | description: @project_description, 20 | source_url: @source_url, 21 | package: package(), 22 | deps: deps() 23 | ] 24 | end 25 | 26 | def application do 27 | [applications: [:logger]] 28 | end 29 | 30 | defp deps do 31 | [] 32 | end 33 | 34 | defp docs() do 35 | [ 36 | source_ref: "v#{@version}", 37 | main: "readme", 38 | extras: [ 39 | "README.md": [title: "README"] 40 | ] 41 | ] 42 | end 43 | 44 | defp package do 45 | [ 46 | name: :elixir_style_guide, 47 | maintainers: ["Christopher Adams"], 48 | licenses: ["CC-by"], 49 | links: %{ 50 | "GitHub" => @source_url 51 | } 52 | ] 53 | end 54 | end 55 | --------------------------------------------------------------------------------