|
| 1 | +--- |
| 2 | +name: elixir-clause-grouping |
| 3 | +description: Use when refactoring Elixir multi-clause functions, extracting helper functions, or fixing Credo readability warnings caused by placing `defp` helpers between clauses of the same function. Keeps function clauses contiguous and moves helpers below the full clause group. |
| 4 | +--- |
| 5 | + |
| 6 | +## Overview |
| 7 | + |
| 8 | +In Elixir modules, all clauses of the same function should stay together. Inserting a `defp` helper between clauses of a `def` or `defp` makes the function harder to read and can trigger Credo readability warnings. When shared logic needs to be extracted, keep the original clause group contiguous and place the helper after the full group. |
| 9 | + |
| 10 | +## When to Use |
| 11 | + |
| 12 | +- When refactoring a multi-clause `def` or `defp` |
| 13 | +- When extracting duplicated logic from multiple function clauses |
| 14 | +- When addressing Credo warnings about clause grouping or readability |
| 15 | +- When editing controller, view, or context modules with several clauses of the same function |
| 16 | +- During review when a helper was added in the middle of another function's clauses |
| 17 | + |
| 18 | +## Core Rule |
| 19 | + |
| 20 | +- Keep all clauses of the same function contiguous |
| 21 | +- Do not place `defp` helpers between clauses of another function |
| 22 | +- Extract shared logic into a helper placed after the full clause group |
| 23 | + |
| 24 | +## Anti-Pattern |
| 25 | + |
| 26 | +```elixir |
| 27 | +def decoded_input_data(%Transaction{to_address: nil}, _, _, _, _), do: {:error, :no_to_address} |
| 28 | + |
| 29 | +defp decode_input_data_with_fallback(data, abi, input, hash, skip_sig_provider?, options, methods_map, abi_map) do |
| 30 | + ... |
| 31 | +end |
| 32 | + |
| 33 | +def decoded_input_data(%Transaction{to_address: %NotLoaded{}}, _, _, _, _), do: {:error, :contract_not_verified, []} |
| 34 | +``` |
| 35 | + |
| 36 | +This splits the `decoded_input_data/5` clause group and makes the function harder to scan. |
| 37 | + |
| 38 | +## Preferred Pattern |
| 39 | + |
| 40 | +```elixir |
| 41 | +def decoded_input_data(%Transaction{to_address: nil}, _, _, _, _), do: {:error, :no_to_address} |
| 42 | + |
| 43 | +def decoded_input_data(%Transaction{to_address: %NotLoaded{}}, _, _, _, _), do: {:error, :contract_not_verified, []} |
| 44 | + |
| 45 | +def decoded_input_data(%Transaction{to_address: %{smart_contract: smart_contract}} = transaction, skip_sig_provider?, options, methods_map, abi_map) do |
| 46 | + ... |
| 47 | +end |
| 48 | + |
| 49 | +defp decode_input_data_with_fallback(data, abi, input, hash, skip_sig_provider?, options, methods_map, abi_map) do |
| 50 | + ... |
| 51 | +end |
| 52 | +``` |
| 53 | + |
| 54 | +## Refactoring Checklist |
| 55 | + |
| 56 | +1. Identify every clause of the function being edited. |
| 57 | +2. Keep those clauses adjacent to each other. |
| 58 | +3. Extract shared logic only after the full clause group. |
| 59 | +4. Re-check that no unrelated `def` or `defp` appears inside the group. |
| 60 | +5. Run formatting after the refactor. |
| 61 | + |
| 62 | +## Notes |
| 63 | + |
| 64 | +- This applies to both public and private multi-clause functions. |
| 65 | +- If a helper is only used by one clause group, place it immediately after that group. |
| 66 | +- Preserving clause grouping is preferred even when the extracted helper is small. |
0 commit comments