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."
|
**Why:** Hides from docs. Signals "this is implementation, not API."
|
||||||
The Elixir team uses this for 30+ internal modules.
|
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
|
**When NOT to use:** If ANYONE outside your team might call it. Public
|
||||||
API must have docs.
|
API must have docs.
|
||||||
|
|
||||||
@@ -69,6 +72,9 @@ end
|
|||||||
extension point for "I made a new data structure and want it to work
|
extension point for "I made a new data structure and want it to work
|
||||||
with Enum."
|
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
|
**When NOT to use:** When you control all implementations. Use
|
||||||
behaviours instead. Protocols are for open extension; behaviours are
|
behaviours instead. Protocols are for open extension; behaviours are
|
||||||
for closed contracts.
|
for closed contracts.
|
||||||
@@ -88,6 +94,9 @@ only 6 in 15 years.
|
|||||||
**Why:** Each protocol is a permanent API commitment. Once defined,
|
**Why:** Each protocol is a permanent API commitment. Once defined,
|
||||||
every type in the ecosystem may implement it.
|
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
|
**When NOT to use:** Don't define a protocol for something only your
|
||||||
app needs. A behaviour or function argument is cheaper.
|
app needs. A behaviour or function argument is cheaper.
|
||||||
|
|
||||||
@@ -114,6 +123,9 @@ end
|
|||||||
**Why:** Pattern matching makes handling explicit. The caller MUST
|
**Why:** Pattern matching makes handling explicit. The caller MUST
|
||||||
decide what to do with errors — they can't accidentally ignore them.
|
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.
|
**When NOT to use:** For programmer errors (bugs). Those should raise.
|
||||||
`File.read!` raises; `File.read` returns tuples.
|
`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
|
it doesn't, crash." This is intentional — crashing is the correct
|
||||||
response to unexpected errors in OTP.
|
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
|
**When NOT to use:** When failure is expected and the caller should
|
||||||
handle it (use tagged tuples).
|
handle it (use tagged tuples).
|
||||||
|
|
||||||
@@ -163,6 +178,9 @@ end
|
|||||||
**Why:** Inheritance via `use` is how Phoenix's ConnCase/DataCase work.
|
**Why:** Inheritance via `use` is how Phoenix's ConnCase/DataCase work.
|
||||||
This pattern comes directly from ExUnit itself.
|
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.
|
**When NOT to use:** For helpers that don't need lifecycle callbacks.
|
||||||
Simple `import` is cleaner for utility functions.
|
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
|
most tests CAN be async — only database/file/process tests need
|
||||||
serialization.
|
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
|
**When NOT to use:** Tests that modify global state, shared files, or
|
||||||
named processes.
|
named processes.
|
||||||
|
|
||||||
@@ -197,6 +218,9 @@ def all?(enumerable) when is_list(enumerable) do
|
|||||||
**Why:** Specs enable Dialyzer checking. Docs generate ExDoc pages. The
|
**Why:** Specs enable Dialyzer checking. Docs generate ExDoc pages. The
|
||||||
Elixir stdlib has zero undocumented public functions.
|
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
|
**When NOT to use:** `@moduledoc false` modules skip docs (they're
|
||||||
internal).
|
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.
|
**Why:** A module IS a thing. Functions are what you DO with that thing.
|
||||||
`String.split/2` reads as "take a String, split it."
|
`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`).
|
**When NOT to use:** Mix tasks (they're commands: `Mix.Tasks.Deps.Get`).
|
||||||
|
|
||||||
### Functions Are Verbs
|
### Functions Are Verbs
|
||||||
@@ -269,6 +295,9 @@ end
|
|||||||
**Why:** GenServer gives you: message serialization, supervision tree
|
**Why:** GenServer gives you: message serialization, supervision tree
|
||||||
integration, hot code upgrade, `:sys` debugging.
|
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).
|
**When NOT to use:** Stateless transformations (just use functions).
|
||||||
One-off concurrent work (use Task). Accumulating state within a request
|
One-off concurrent work (use Task). Accumulating state within a request
|
||||||
(use recursion or reduce).
|
(use recursion or reduce).
|
||||||
@@ -288,6 +317,9 @@ end
|
|||||||
**Why:** Agent is GenServer with the common case optimized. Less
|
**Why:** Agent is GenServer with the common case optimized. Less
|
||||||
boilerplate for "I just need a mutable box."
|
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
|
**When NOT to use:** When you need `handle_info`, timeouts, or multiple
|
||||||
operations that must be atomic.
|
operations that must be atomic.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user