fix: add 'When to use' to every pattern (was missing)
This commit is contained in:
@@ -42,6 +42,9 @@ defmodule Code.Formatter do
|
||||
**Why:** Hides from docs. Signals "this is implementation, not API."
|
||||
The Elixir team uses this for 30+ internal modules.
|
||||
|
||||
**When to use:** Modules that are implementation details — adapters,
|
||||
internal state machines, compiler passes.
|
||||
|
||||
**When NOT to use:** If ANYONE outside your team might call it. Public
|
||||
API must have docs.
|
||||
|
||||
@@ -69,6 +72,9 @@ end
|
||||
extension point for "I made a new data structure and want it to work
|
||||
with Enum."
|
||||
|
||||
**When to use:** When third-party code needs to extend your library
|
||||
for new data types.
|
||||
|
||||
**When NOT to use:** When you control all implementations. Use
|
||||
behaviours instead. Protocols are for open extension; behaviours are
|
||||
for closed contracts.
|
||||
@@ -88,6 +94,9 @@ only 6 in 15 years.
|
||||
**Why:** Each protocol is a permanent API commitment. Once defined,
|
||||
every type in the ecosystem may implement it.
|
||||
|
||||
**When to use:** When you need a small set of polymorphic operations
|
||||
that ANY type in the ecosystem should be able to implement.
|
||||
|
||||
**When NOT to use:** Don't define a protocol for something only your
|
||||
app needs. A behaviour or function argument is cheaper.
|
||||
|
||||
@@ -114,6 +123,9 @@ end
|
||||
**Why:** Pattern matching makes handling explicit. The caller MUST
|
||||
decide what to do with errors — they can't accidentally ignore them.
|
||||
|
||||
**When to use:** Any operation that interacts with the outside world
|
||||
(files, network, databases) where failure is a normal outcome.
|
||||
|
||||
**When NOT to use:** For programmer errors (bugs). Those should raise.
|
||||
`File.read!` raises; `File.read` returns tuples.
|
||||
|
||||
@@ -133,6 +145,9 @@ File.read!("path") # => "..." | raises File.Error
|
||||
it doesn't, crash." This is intentional — crashing is the correct
|
||||
response to unexpected errors in OTP.
|
||||
|
||||
**When to use:** Pipeline code where you want to crash on unexpected
|
||||
failure rather than propagate error tuples.
|
||||
|
||||
**When NOT to use:** When failure is expected and the caller should
|
||||
handle it (use tagged tuples).
|
||||
|
||||
@@ -163,6 +178,9 @@ end
|
||||
**Why:** Inheritance via `use` is how Phoenix's ConnCase/DataCase work.
|
||||
This pattern comes directly from ExUnit itself.
|
||||
|
||||
**When to use:** When 3+ test modules share the same setup callbacks,
|
||||
imports, or lifecycle management.
|
||||
|
||||
**When NOT to use:** For helpers that don't need lifecycle callbacks.
|
||||
Simple `import` is cleaner for utility functions.
|
||||
|
||||
@@ -174,6 +192,9 @@ Simple `import` is cleaner for utility functions.
|
||||
most tests CAN be async — only database/file/process tests need
|
||||
serialization.
|
||||
|
||||
**When to use:** Pure unit tests, tests with isolated process state,
|
||||
tests that only read shared resources.
|
||||
|
||||
**When NOT to use:** Tests that modify global state, shared files, or
|
||||
named processes.
|
||||
|
||||
@@ -197,6 +218,9 @@ def all?(enumerable) when is_list(enumerable) do
|
||||
**Why:** Specs enable Dialyzer checking. Docs generate ExDoc pages. The
|
||||
Elixir stdlib has zero undocumented public functions.
|
||||
|
||||
**When to use:** Every public function in every public module.
|
||||
No exceptions.
|
||||
|
||||
**When NOT to use:** `@moduledoc false` modules skip docs (they're
|
||||
internal).
|
||||
|
||||
@@ -228,6 +252,8 @@ Without `@type t`, struct fields are effectively untyped.
|
||||
**Why:** A module IS a thing. Functions are what you DO with that thing.
|
||||
`String.split/2` reads as "take a String, split it."
|
||||
|
||||
**When to use:** Every module you define. Period.
|
||||
|
||||
**When NOT to use:** Mix tasks (they're commands: `Mix.Tasks.Deps.Get`).
|
||||
|
||||
### Functions Are Verbs
|
||||
@@ -269,6 +295,9 @@ end
|
||||
**Why:** GenServer gives you: message serialization, supervision tree
|
||||
integration, hot code upgrade, `:sys` debugging.
|
||||
|
||||
**When to use:** When you need mutable state that survives across
|
||||
multiple requests or events.
|
||||
|
||||
**When NOT to use:** Stateless transformations (just use functions).
|
||||
One-off concurrent work (use Task). Accumulating state within a request
|
||||
(use recursion or reduce).
|
||||
@@ -288,6 +317,9 @@ end
|
||||
**Why:** Agent is GenServer with the common case optimized. Less
|
||||
boilerplate for "I just need a mutable box."
|
||||
|
||||
**When to use:** When your process only needs get/update operations
|
||||
on a single piece of state — no complex message handling.
|
||||
|
||||
**When NOT to use:** When you need `handle_info`, timeouts, or multiple
|
||||
operations that must be atomic.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user