6.7 KiB
Elixir Core vs Phoenix: Side-by-Side Comparison
How the same concepts are approached differently (or similarly) between Elixir core and Phoenix.
Process Lifecycle
| Aspect | Elixir Core | Phoenix |
|---|---|---|
| Default restart | :permanent (GenServer, Supervisor) |
:temporary (Channel) |
| Hibernation | Not set by default | 15s idle → hibernate (Channel) |
| Process identity | Registry :via tuples |
Topic-based (channels identified by topic) |
| Supervision | Direct supervisor reference | Endpoint supervisor manages all |
Source (Elixir): lib/elixir/lib/gen_server.ex:911-919 (child_spec defaults to :permanent via Supervisor.child_spec)
Source (Phoenix): lib/phoenix/channel.ex:464-472 (explicit restart: :temporary)
Error Handling
| Aspect | Elixir Core | Phoenix |
|---|---|---|
| Exception design | Minimal struct fields | HTTP-aware (plug_status) |
| Bang functions | File.read! raises |
broadcast! raises |
| Failure response | {:error, reason} tuple |
{:error, reason} + HTTP status |
| Recovery | Supervisor restart | Client reconnection |
Source (Elixir): lib/elixir/lib/agent.ex:187 (standard on_start type: {:ok, pid} | {:error, ...})
Source (Phoenix): lib/phoenix/router.ex:2-6 (NoRouteError with plug_status: 404)
Behaviour Design
| Aspect | Elixir Core | Phoenix |
|---|---|---|
| Required callbacks | Most are optional | Only join/3 required (Channel) |
__using__ generates |
child_spec/1 + @behaviour |
child_spec + behaviour + config + imports |
| Configuration | Via use Module, opts |
Via use Module, opts + module attributes |
| Before-compile | Rarely used | Heavily used (routes, intercepts) |
Source (Elixir): lib/elixir/lib/gen_server.ex:899-919 (using generates child_spec + @behaviour)
Source (Phoenix): lib/phoenix/channel.ex:450-485 (using generates child_spec + behaviour + DSL setup)
Macro Usage
| Aspect | Elixir Core | Phoenix |
|---|---|---|
| Philosophy | Minimal, prefer functions | Justified by performance |
__using__ |
Generates 1-2 functions | Generates functions + sets up DSL |
| DSL creation | Avoided (except Kernel/SpecialForms) | Embraced (Router DSL) |
| Attribute accumulation | Rare | Central pattern (routes, sockets) |
Source (Elixir): lib/elixir/lib/gen_server.ex:899 — simple __using__ (behaviour + child_spec + defaults)
Source (Phoenix): lib/phoenix/router.ex:288-312 — complex DSL setup with attribute accumulation, imports, and @before_compile
Module Organization
| Aspect | Elixir Core | Phoenix |
|---|---|---|
| File naming | gen_server.ex (snake_case) |
controller.ex (snake_case) |
| Nesting | 2 levels max (Task.Supervised) |
2-3 levels (Phoenix.Channel.Server) |
| Internal modules | @moduledoc false |
@moduledoc false |
| Public API | Functions on the main module | Functions + macros on the main module |
Both follow the same convention: public API on the parent module, implementation details in nested submodules with @moduledoc false.
State Management
| Aspect | Elixir Core | Phoenix |
|---|---|---|
| Agent | Simple state, function-based access | Socket assigns (assign/2) |
| GenServer | Full control, handle_call/cast/info | Channel handles (same callbacks) |
| State shape | Any term (developer's choice) | %Socket{} struct (framework-defined) |
| State access | Direct in callbacks | Via socket.assigns |
Source (Elixir): lib/elixir/lib/agent.ex:62-82 (compute in server vs client pattern)
Source (Phoenix): lib/phoenix/channel.ex:463 (import Phoenix.Socket, only: [assign: 3, assign: 2])
Documentation
| Aspect | Elixir Core | Phoenix |
|---|---|---|
| Moduledoc size | Very large (GenServer: 530 lines) | Large (Router: ~260 lines) |
| Examples | Doctests (verified by tests) | Examples in docs (not always doctests) |
| Admonitions | Info blocks for use |
Info blocks for use |
| Guides | Linked from moduledoc | Linked from moduledoc |
| Deprecation | @doc deprecated: "Use X instead" |
Inline comments (TODO markers) |
Both use the same documentation infrastructure (ExDoc), but Elixir core tends toward more exhaustive docs (GenServer's moduledoc is essentially a tutorial).
Configuration
| Aspect | Elixir Core | Phoenix |
|---|---|---|
| Compile-time | Module attributes | Application.compile_env |
| Runtime | Application env / init args | config/2 callback + Application env |
| Per-instance | Options to start_link |
Endpoint config per environment |
Source (Phoenix): lib/phoenix/endpoint.ex:422-430 (compile-time config checking)
var!(code_reloading?) =
Application.compile_env(@otp_app, [__MODULE__, :code_reloader], false)
This pattern — reading config at compile time and validating it against runtime — is Phoenix-specific. Elixir core reads config only at runtime.
Telemetry
| Aspect | Elixir Core | Phoenix |
|---|---|---|
| Built-in events | None (telemetry is a separate library) | Extensive event catalog |
| Instrumentation | Manual by library authors | Baked into router, endpoint, socket |
| Event naming | Varies by library | [:phoenix, :component, :phase] convention |
| Logging | Logger calls |
Telemetry → Logger adapter (Phoenix.Logger) |
Source (Phoenix): lib/phoenix/logger.ex:7-50 (telemetry event catalog)
Source (Phoenix): lib/phoenix/router.ex:400-438 (telemetry in router dispatch)
Phoenix wraps every request dispatch in telemetry start/stop/exception events. This provides distributed tracing, monitoring, and logging without any application code changes.
Testing
| Aspect | Elixir Core | Phoenix |
|---|---|---|
| Test helper | ExUnit.Case |
Phoenix.ConnTest, Phoenix.ChannelTest |
| Test subject | Module functions | Endpoint (full plug pipeline) |
| Communication | Direct function calls | HTTP verbs (ConnTest), messages (ChannelTest) |
| Isolation | Process per test | Process per test + sandbox (Ecto) |
Source (Phoenix): lib/phoenix/test/conn_test.ex:1-30 (endpoint-based integration testing)
Source (Phoenix): lib/phoenix/test/channel_test.ex:1-30 (process-based channel testing)
Phoenix test helpers test at the integration level by default — ConnTest dispatches through the full plug pipeline, ChannelTest exercises the full channel lifecycle via message passing. This catches middleware bugs that unit tests miss.