docs: backfill TOC + decision trees, fix review findings
- Add ## Contents and ## Decision Tree to all 10 existing pattern files - Fix embed_as/1 semantics inversion in types.md (:self → :dump) - Fix fabricated __meta__.changes reference in changesets.md - Fix default primary key type (:integer → :id) in schemas.md - Combine @impl subsections into single "Minimal Callback Annotation"
This commit is contained in:
@@ -2,6 +2,18 @@
|
||||
|
||||
How behaviours are designed, implemented, and used in Elixir core and Phoenix.
|
||||
|
||||
## Contents
|
||||
|
||||
1. [Behaviour Definition with `@callback`](#1-behaviour-definition-with-callback)
|
||||
2. [`@optional_callbacks` for Extensibility](#2-optional_callbacks-for-extensibility)
|
||||
3. [`@behaviour` Declaration in `__using__`](#3-behaviour-declaration-in-__using__)
|
||||
4. [Default Implementations via `defoverridable`](#4-default-implementations-via-defoverridable)
|
||||
5. [Phoenix Channel: Behaviour + Process + Protocol](#5-phoenix-channel-behaviour--process--protocol)
|
||||
6. [Callback Documentation Pattern](#6-callback-documentation-pattern)
|
||||
7. [Phoenix.Endpoint: Behaviour as Interface Contract](#7-phoenixendpoint-behaviour-as-interface-contract)
|
||||
|
||||
---
|
||||
|
||||
## 1. Behaviour Definition with `@callback`
|
||||
|
||||
**Source:** [lib/elixir/lib/gen_server.ex#L577](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L577) (all callback definitions)
|
||||
@@ -678,4 +690,15 @@ end
|
||||
|
||||
**Why:** The more code a `use` macro generates, the harder it is to debug. If users regularly need to read the generated code to understand failures, the abstraction is leaking. Reserve heavy `use` macros for well-established patterns (GenServer, Endpoint, Channel) where the community has internalized the mental model.
|
||||
|
||||
## Decision Tree
|
||||
|
||||
- If you need a contract that multiple modules will implement differently → define a behaviour with `@callback` (Pattern 1)
|
||||
- If most implementors will use a default for some callbacks → mark those `@optional_callbacks` (Pattern 2)
|
||||
- If your behaviour requires boilerplate setup (module attributes, compile hooks) → inject `@behaviour` inside `__using__` (Pattern 3)
|
||||
- If 90% of implementors want the same default for a callback → provide a `defoverridable` implementation (Pattern 4)
|
||||
- If the behaviour involves a running process with lifecycle configuration → combine behaviour + process + module attributes (Pattern 5)
|
||||
- If callback semantics are non-obvious (multiple return shapes, triggering conditions) → write comprehensive `@doc` with examples on each `@callback` (Pattern 6)
|
||||
- If the behaviour requires significant generated boilerplate (plugs, routing, supervision wiring) → use the `use` macro as the full interface contract (Pattern 7)
|
||||
- If there is only one implementation and no plans for more → skip the behaviour, use a plain module
|
||||
|
||||
<!-- PATTERN_COMPLETE -->
|
||||
|
||||
Reference in New Issue
Block a user