When defining a behaviour for a module that wraps a GenServer, should start_link/1 be included as a @callback?
Argument against including it
start_link is called by the supervisor, which is configured with the concrete module directly ({QuoteFeed, opts} or {QuoteFeed.Fake, opts}). Nobody calls start_link polymorphically through the behaviour interface.
Behaviours exist to define the interface that callers use — the functions that application code dispatches through. subscribe/2 and unsubscribe/2 are genuine polymorphic interfaces. start_link is lifecycle boilerplate.
Including it adds noise to the contract without enabling any real polymorphism that the supervision tree doesn't already handle.
Argument for including it
Completeness: the behaviour documents the full public API
Supervisor configuration helpers (e.g., child_spec/1 wrappers) might want to call start_link through a module variable
Consistency: if it's public, it's in the contract
If we decide yes (exclude start_link)
Update patterns/behaviours.md with guidance
Audit gargoyle's existing behaviours for consistency
Ensure Fake modules and any other implementors are updated
Context
Came up during PR #662 (QuoteFeed @behaviour). The PR currently includes start_link as a callback. If this becomes a best practice, we'd remove it and update the pattern.
## Question
When defining a behaviour for a module that wraps a GenServer, should `start_link/1` be included as a `@callback`?
## Argument against including it
`start_link` is called by the **supervisor**, which is configured with the concrete module directly (`{QuoteFeed, opts}` or `{QuoteFeed.Fake, opts}`). Nobody calls `start_link` polymorphically through the behaviour interface.
Behaviours exist to define the interface that **callers** use — the functions that application code dispatches through. `subscribe/2` and `unsubscribe/2` are genuine polymorphic interfaces. `start_link` is lifecycle boilerplate.
Including it adds noise to the contract without enabling any real polymorphism that the supervision tree doesn't already handle.
## Argument for including it
- Completeness: the behaviour documents the full public API
- Supervisor configuration helpers (e.g., `child_spec/1` wrappers) might want to call `start_link` through a module variable
- Consistency: if it's public, it's in the contract
## If we decide yes (exclude start_link)
1. Update `patterns/behaviours.md` with guidance
2. Audit gargoyle's existing behaviours for consistency
3. Ensure Fake modules and any other implementors are updated
## Context
Came up during PR #662 (QuoteFeed @behaviour). The PR currently includes `start_link` as a callback. If this becomes a best practice, we'd remove it and update the pattern.
Look at what the ecosystem has settled on through practice, not just theory. If mature libraries consistently exclude start_link from their behaviour definitions, that's strong evidence for the convention.
## Evaluation approach
Search popular/robust Elixir applications and identify what they do:
- **Oban** — defines behaviours for workers, plugins, engines. Does `start_link` appear in their callbacks?
- **Broadway** — producer/processor behaviours. Same question.
- **Ecto** — adapter behaviour (`Ecto.Adapter`). Does it include lifecycle functions?
- **Phoenix** — endpoint, channel, socket behaviours.
- **Commanded** — aggregate, event handler, process manager behaviours.
- **Nerves** — hardware abstraction behaviours.
- **Horde** — distributed supervisor/registry behaviours.
Look at what the ecosystem has settled on through practice, not just theory. If mature libraries consistently exclude `start_link` from their behaviour definitions, that's strong evidence for the convention.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Question
When defining a behaviour for a module that wraps a GenServer, should
start_link/1be included as a@callback?Argument against including it
start_linkis called by the supervisor, which is configured with the concrete module directly ({QuoteFeed, opts}or{QuoteFeed.Fake, opts}). Nobody callsstart_linkpolymorphically through the behaviour interface.Behaviours exist to define the interface that callers use — the functions that application code dispatches through.
subscribe/2andunsubscribe/2are genuine polymorphic interfaces.start_linkis lifecycle boilerplate.Including it adds noise to the contract without enabling any real polymorphism that the supervision tree doesn't already handle.
Argument for including it
child_spec/1wrappers) might want to callstart_linkthrough a module variableIf we decide yes (exclude start_link)
patterns/behaviours.mdwith guidanceContext
Came up during PR #662 (QuoteFeed @behaviour). The PR currently includes
start_linkas a callback. If this becomes a best practice, we'd remove it and update the pattern.Evaluation approach
Search popular/robust Elixir applications and identify what they do:
start_linkappear in their callbacks?Ecto.Adapter). Does it include lifecycle functions?Look at what the ecosystem has settled on through practice, not just theory. If mature libraries consistently exclude
start_linkfrom their behaviour definitions, that's strong evidence for the convention.