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