Skip to main content
Exception modules should consistently use the conventional Error suffix.
Mixing Unix (LF) and Windows (CRLF) line endings creates noisy diffs and inconsistent formatting.
When naming a matched parameter, place the variable consistently before or after the pattern.
Consistent whitespace around operators makes expressions easier to scan.
Do not add unnecessary spaces immediately inside parentheses.
Use spaces consistently instead of mixing tab- and space-indented lines.
Alias nested dependencies so a module’s external dependencies are visible at a glance.
FIXME tags identify known broken or unsafe behavior that should be corrected.
Replace incomplete TODO placeholders with the intended implementation.
Keep aliases alphabetically ordered so dependencies are easy to find.
Elixir function names should use lowercase snake_case and may end in ? or !.
Add underscore separators to large numeric literals.
Split long expressions into named values or multiple lines.
Module attributes should use lowercase snake_case.
Public modules should explain their purpose with @moduledoc.
Module names should use CamelCase without underscores.
Elixir conditions do not need surrounding parentheses.
Zero-arity function definitions conventionally omit parentheses.
Assign or invoke an anonymous function directly instead of hiding the call in a pipeline.
Functions returning booleans should end in ?; reserve is_ names for guards.
Function-level rescue, catch, and after clauses are clearer without a redundant try.
Use a single blank line to separate related sections.
Put expressions on separate lines instead of separating them with semicolons.
Commas should be followed by a space.
Sigils remove distracting escape characters from strings containing many quotes.
End every source file with exactly one newline for portable tooling and clean diffs.
Remove invisible spaces and tabs at the end of source lines.
Use the alias directly instead of repeating its namespace.
Variables should use lowercase snake_case.
A with containing one match and an else branch is clearer as a case.
When the target and argument count are known, call the function directly.
A cond with one condition plus a fallback should be an if/else.
Too many branches make functions difficult to understand and test. Extract named decisions.
Count matching elements in one pass with Enum.count/2.
Combining predicates avoids traversing an intermediate collection.
Too many parameters make calls error-prone. Group related values into a struct or map.
Large quoted AST blocks should delegate to smaller macros or helper functions.
Map and join in one traversal with Enum.map_join/3.
Use case when control flow depends on the shape of a value.
A negated unless condition is a double negative.
Put the positive condition first and swap the branches.
Extract nested decisions into functions or use pattern matching and early returns.
Do not match the final result only to return the identical value.
Combine predicates to avoid traversing an intermediate collection.
An unless with an else branch is easier to understand as an if.
Move ordinary assignments before with and side-effect calls into its body.
Module attributes are evaluated at compile time and can freeze environment-specific configuration.
Repeating the same boolean operand is redundant and may hide a typo.
dbg/2 is intended for local debugging and should not remain in committed application code.
Use Enum.empty?/1 instead of traversing an enumerable to count every element.
IEx.pry pauses a running process and must not remain in application code.
Replace temporary console inspection with intentional structured logging when diagnostics are needed.
Metadata passed to Logger should be included in the formatter configuration so it is not silently omitted.
Using the same expression on both sides often produces a trivial result or signals a copy-paste error.
Replace expressions whose result cannot change with the value they represent.
Use reraise/2 when translating an exception so the original stack trace is retained.
Referencing Module.t() avoids an unnecessary compile-time dependency on a struct literal.
Do not send a constructed command string through a shell. Pass a fixed executable and separate arguments.
Enum functions return new values; they do not mutate the input collection.
Handle the success or error returned by File operations.
Keyword functions return updated lists and do not mutate the original.
List functions return updated lists and do not mutate the original.
Use the path returned by Path functions.
Use the value returned by Regex operations.
Strings are immutable, so transformation results must be used.
Tuple operations return a new tuple and do not mutate the original.
ExUnit test files should use .exs so they are executed as scripts by the test runner.
Values derived from a request must not reach Code.eval_*, which executes code with the application’s privileges.
Untrusted values must not choose the module or function passed to apply/3.
EEx templates can execute Elixir code, so only application-owned template source should be evaluated.
An attacker-controlled path passed to :erlang.load_nif can execute arbitrary native code.
:os.cmd invokes a command processor and must not receive constructed or untrusted commands.
Do not let untrusted input select a command or shell string passed to Port.open.
Use a fixed executable and validate every argument before passing it to System.cmd.
System.shell invokes a system shell and must not receive untrusted input.
Disabling socket origin checks allows malicious sites to open authenticated cross-origin WebSocket connections.
A wildcard CORS policy can allow arbitrary websites to read authenticated responses.
A controller action that changes state must not also be reachable by GET, which is not protected as a state-changing request.
Detailed error pages can disclose stack traces, source code, and request data.
Production endpoints should redirect HTTP to HTTPS and enable transport protections.
Session cookies should use secure, http_only, and an appropriate SameSite policy.
Secure browser headers should include a restrictive Content Security Policy to reduce the impact of XSS.
Every browser pipeline that fetches a cookie-backed session and accepts state changes should use :protect_from_forgery.
HTML pipelines should install Phoenix’s standard security headers.
Pseudo-random generators such as :rand must not create tokens, credentials, or other security-sensitive values.
MD5 and SHA-1 are collision-prone. Use SHA-256 or stronger for integrity and a password-hashing function for passwords.
Atoms are not garbage collected, so untrusted strings must not be converted into new atoms.
Attacker-controlled patterns can trigger excessive backtracking and consume scheduler time.
:erlang.binary_to_term can allocate attacker-controlled terms and deserialize executable functions.
Do not let a request parameter select an arbitrary external redirect destination.
Do not pass request-controlled paths directly to filesystem APIs.
Resolve a trusted identifier to a server-owned file instead of accepting a download path from the request.
Only send files resolved beneath an application-owned directory.
Never interpolate request values into raw SQL. Keep SQL text fixed and pass values as parameters.
Do not let untrusted input control an outbound request URL. Resolve destinations from an allowlist.
Disabling certificate verification allows a network attacker to impersonate the remote service.
An attacker who selects text/html may turn otherwise inert response data into executable script.
Do not concatenate untrusted data into an HTML response. Render it through Phoenix’s escaping.
raw bypasses Phoenix HTML escaping and can turn request data into executable markup.
Use a template or an encoder with an explicit non-HTML content type instead of constructing response bodies from request data.