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:
2026-05-01 22:13:35 -07:00
parent b33accf37c
commit 10218813d3
13 changed files with 356 additions and 87 deletions
+23
View File
@@ -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 -->