We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Using `@impl Phoenix.LiveView` instead of `@impl true` for clear callback contracts
almirsarajcic
Stop using @impl true
in your Phoenix modules. The Elixir compiler provides much better safety and clarity when you specify the exact behaviour module name.
defmodule MyLive do
use Phoenix.LiveView
# ❌ WRONG - Lazy and unclear
@impl true
def mount(_params, _session, socket) do
{:ok, socket}
end
# ✅ CORRECT - Crystal clear which behaviour
@impl Phoenix.LiveView
def mount(_params, _session, socket) do
{:ok, socket}
end
end
Why explicit module names matter
Multiple behaviours create ambiguity. When your module implements several behaviours, @impl true
becomes confusing:
defmodule MyServer do
@behaviour GenServer
@behaviour Phoenix.Channel
@impl true # Which behaviour does this implement??
def init(args), do: {:ok, args}
# Both GenServer AND Phoenix.Channel have init/1 callbacks!
# Compiler warning: conflicting behaviours found
end
Compile-time safety kicks in. The compiler actively validates your @impl
declarations:
defmodule WrongExample do
use Phoenix.LiveView
@impl Phoenix.LiveComponent # Wrong behaviour!
def mount(_params, _session, socket), do: {:ok, socket}
# WARNING: got "@impl Phoenix.LiveComponent" for function mount/3
# but this behaviour was not declared with @behaviour
end
Consistency enforcement. If you mark one callback with @impl
, you must mark ALL callbacks:
defmodule InconsistentExample do
use Phoenix.LiveView
@impl Phoenix.LiveView # Marked this one...
def mount(_params, _session, socket), do: {:ok, socket}
# But forgot this one - COMPILER WARNING!
def handle_event("save", _params, socket), do: {:noreply, socket}
# WARNING: module attribute @impl was not set for function handle_event/3
end
Common Phoenix patterns
# LiveView modules
@impl Phoenix.LiveView
def mount(_params, _session, socket), do: {:ok, socket}
@impl Phoenix.LiveView
def handle_event("save", _params, socket), do: {:noreply, socket}
@impl Phoenix.LiveView
def render(assigns), do: ~H"<div>LiveView</div>"
# LiveComponent modules
@impl Phoenix.LiveComponent
def update(assigns, socket), do: {:ok, assign(socket, assigns)}
@impl Phoenix.LiveComponent
def render(assigns), do: ~H"<div>Component</div>"
# GenServer modules
@impl GenServer
def init(state), do: {:ok, state}
@impl GenServer
def handle_call(msg, _from, state), do: {:reply, msg, state}
Using explicit behaviour names makes your code self-documenting. Future developers (including yourself) immediately understand which contract each function fulfills without scrolling to find behaviour declarations.
The compiler becomes your friend, catching mismatched signatures and missing implementations before they reach production.
copied to clipboard