|
| 1 | +<!-- usage-rules-start --> |
| 2 | +<!-- usage-rules-header --> |
| 3 | +# Usage Rules |
| 4 | + |
| 5 | +**IMPORTANT**: Consult these usage rules early and often when working with the packages listed below. |
| 6 | +Before attempting to use any of these packages or to discover if you should use them, review their |
| 7 | +usage rules to understand the correct patterns, conventions, and best practices. |
| 8 | +<!-- usage-rules-header-end --> |
| 9 | + |
| 10 | +<!-- usage_rules-start --> |
| 11 | +## usage_rules usage |
| 12 | +_A dev tool for Elixir projects to gather LLM usage rules from dependencies_ |
| 13 | + |
| 14 | +## Using Usage Rules |
| 15 | + |
| 16 | +Many packages have usage rules, which you should *thoroughly* consult before taking any |
| 17 | +action. These usage rules contain guidelines and rules *directly from the package authors*. |
| 18 | +They are your best source of knowledge for making decisions. |
| 19 | + |
| 20 | +## Modules & functions in the current app and dependencies |
| 21 | + |
| 22 | +When looking for docs for modules & functions that are dependencies of the current project, |
| 23 | +or for Elixir itself, use `mix usage_rules.docs` |
| 24 | + |
| 25 | +``` |
| 26 | +# Search a whole module |
| 27 | +mix usage_rules.docs Enum |
| 28 | +
|
| 29 | +# Search a specific function |
| 30 | +mix usage_rules.docs Enum.zip |
| 31 | +
|
| 32 | +# Search a specific function & arity |
| 33 | +mix usage_rules.docs Enum.zip/1 |
| 34 | +``` |
| 35 | + |
| 36 | + |
| 37 | +## Searching Documentation |
| 38 | + |
| 39 | +You should also consult the documentation of any tools you are using, early and often. The best |
| 40 | +way to accomplish this is to use the `usage_rules.search_docs` mix task. Once you have |
| 41 | +found what you are looking for, use the links in the search results to get more detail. For example: |
| 42 | + |
| 43 | +``` |
| 44 | +# Search docs for all packages in the current application, including Elixir |
| 45 | +mix usage_rules.search_docs Enum.zip |
| 46 | +
|
| 47 | +# Search docs for specific packages |
| 48 | +mix usage_rules.search_docs Req.get -p req |
| 49 | +
|
| 50 | +# Search docs for multi-word queries |
| 51 | +mix usage_rules.search_docs "making requests" -p req |
| 52 | +
|
| 53 | +# Search only in titles (useful for finding specific functions/modules) |
| 54 | +mix usage_rules.search_docs "Enum.zip" --query-by title |
| 55 | +``` |
| 56 | + |
| 57 | + |
| 58 | +<!-- usage_rules-end --> |
| 59 | +<!-- usage_rules:elixir-start --> |
| 60 | +## usage_rules:elixir usage |
| 61 | +# Elixir Core Usage Rules |
| 62 | + |
| 63 | +## Pattern Matching |
| 64 | +- Use pattern matching over conditional logic when possible |
| 65 | +- Prefer to match on function heads instead of using `if`/`else` or `case` in function bodies |
| 66 | +- `%{}` matches ANY map, not just empty maps. Use `map_size(map) == 0` guard to check for truly empty maps |
| 67 | + |
| 68 | +## Error Handling |
| 69 | +- Use `{:ok, result}` and `{:error, reason}` tuples for operations that can fail |
| 70 | +- Avoid raising exceptions for control flow |
| 71 | +- Use `with` for chaining operations that return `{:ok, _}` or `{:error, _}` |
| 72 | + |
| 73 | +## Common Mistakes to Avoid |
| 74 | +- Elixir has no `return` statement, nor early returns. The last expression in a block is always returned. |
| 75 | +- Don't use `Enum` functions on large collections when `Stream` is more appropriate |
| 76 | +- Avoid nested `case` statements - refactor to a single `case`, `with` or separate functions |
| 77 | +- Don't use `String.to_atom/1` on user input (memory leak risk) |
| 78 | +- Lists and enumerables cannot be indexed with brackets. Use pattern matching or `Enum` functions |
| 79 | +- Prefer `Enum` functions like `Enum.reduce` over recursion |
| 80 | +- When recursion is necessary, prefer to use pattern matching in function heads for base case detection |
| 81 | +- Using the process dictionary is typically a sign of unidiomatic code |
| 82 | +- Only use macros if explicitly requested |
| 83 | +- There are many useful standard library functions, prefer to use them where possible |
| 84 | + |
| 85 | +## Function Design |
| 86 | +- Use guard clauses: `when is_binary(name) and byte_size(name) > 0` |
| 87 | +- Prefer multiple function clauses over complex conditional logic |
| 88 | +- Name functions descriptively: `calculate_total_price/2` not `calc/2` |
| 89 | +- Predicate function names should not start with `is` and should end in a question mark. |
| 90 | +- Names like `is_thing` should be reserved for guards |
| 91 | + |
| 92 | +## Data Structures |
| 93 | +- Use structs over maps when the shape is known: `defstruct [:name, :age]` |
| 94 | +- Prefer keyword lists for options: `[timeout: 5000, retries: 3]` |
| 95 | +- Use maps for dynamic key-value data |
| 96 | +- Prefer to prepend to lists `[new | list]` not `list ++ [new]` |
| 97 | + |
| 98 | +## Mix Tasks |
| 99 | + |
| 100 | +- Use `mix help` to list available mix tasks |
| 101 | +- Use `mix help task_name` to get docs for an individual task |
| 102 | +- Read the docs and options fully before using tasks |
| 103 | + |
| 104 | +## Testing |
| 105 | +- Run tests in a specific file with `mix test test/my_test.exs` and a specific test with the line number `mix test path/to/test.exs:123` |
| 106 | +- Limit the number of failed tests with `mix test --max-failures n` |
| 107 | +- Use `@tag` to tag specific tests, and `mix test --only tag` to run only those tests |
| 108 | +- Use `assert_raise` for testing expected exceptions: `assert_raise ArgumentError, fn -> invalid_function() end` |
| 109 | +- Use `mix help test` to for full documentation on running tests |
| 110 | + |
| 111 | +## Debugging |
| 112 | + |
| 113 | +- Use `dbg/1` to print values while debugging. This will display the formatted value and other relevant information in the console. |
| 114 | + |
| 115 | +<!-- usage_rules:elixir-end --> |
| 116 | +<!-- usage_rules:otp-start --> |
| 117 | +## usage_rules:otp usage |
| 118 | +# OTP Usage Rules |
| 119 | + |
| 120 | +## GenServer Best Practices |
| 121 | +- Keep state simple and serializable |
| 122 | +- Handle all expected messages explicitly |
| 123 | +- Use `handle_continue/2` for post-init work |
| 124 | +- Implement proper cleanup in `terminate/2` when necessary |
| 125 | + |
| 126 | +## Process Communication |
| 127 | +- Use `GenServer.call/3` for synchronous requests expecting replies |
| 128 | +- Use `GenServer.cast/2` for fire-and-forget messages. |
| 129 | +- When in doubt, use `call` over `cast`, to ensure back-pressure |
| 130 | +- Set appropriate timeouts for `call/3` operations |
| 131 | + |
| 132 | +## Fault Tolerance |
| 133 | +- Set up processes such that they can handle crashing and being restarted by supervisors |
| 134 | +- Use `:max_restarts` and `:max_seconds` to prevent restart loops |
| 135 | + |
| 136 | +## Task and Async |
| 137 | +- Use `Task.Supervisor` for better fault tolerance |
| 138 | +- Handle task failures with `Task.yield/2` or `Task.shutdown/2` |
| 139 | +- Set appropriate task timeouts |
| 140 | +- Use `Task.async_stream/3` for concurrent enumeration with back-pressure |
| 141 | + |
| 142 | +<!-- usage_rules:otp-end --> |
| 143 | +<!-- usage-rules-end --> |
0 commit comments