Agent is an Elixir and OTP abstraction for managing state inside a process. It reduces GenServer boilerplate when the process only needs initialization, state reads, and state updates, while remaining suitable for supervision trees. section 10.0 section 10.2
Use the Agent API to expose state operations:
- Start an agent with Agent.start_link/1; the initialization function runs in the new process, and its return value becomes the initial state. section 10.2.1
- Read state with Agent.get/2, which evaluates a function in the agent process and returns that function’s result to the caller. section 10.2.1
- Change state synchronously with Agent.update/2 or asynchronously with Agent.cast/2. section 10.2.1
- Rely on process serialization when multiple clients access the same agent; operations execute one at a time and later clients observe completed changes. section 10.2.2
A shared counter
Start an agent with state 0, update it from another process using Agent.update(counter, fn count -> count + 1 end), then read the resulting value with Agent.get/2. The shell observes 1 because the state belongs to the agent process rather than any client. section 10.2.2
Listing 10.4 Agent-powered to-do server (todo_agent/lib/todo/server.ex)
defmodule Todo.Server do
use Agent, restart: :temporary
def start_link(name) do
Agent.start_link(
fn ->
IO.puts("Starting to-do server for #{name}")
{name, Todo.Database.get(name) || Todo.List.new()}
end,
name: via_tuple(name)
)
end
def add_entry(todo_server, new_entry) do
Agent.cast(todo_server, fn {name, todo_list} ->
new_list = Todo.List.add_entry(todo_list, new_entry)
Todo.Database.store(name, new_list)
{name, new_list}
end)
end
def entries(todo_server, date) do
Agent.get(
todo_server,
fn {_name, todo_list} -> Todo.List.entries(todo_list, date) end
)
end
defp via_tuple(name) do
Todo.ProcessRegistry.via_tuple({__MODULE__, name})
end
end
A production-shaped to-do server wraps Agent behind a dedicated module, uses Agent.cast/2 for asynchronous additions, Agent.get/2 for queries, and remains usable through the existing server interface. section 10.2.3
Wrap agent code in a dedicated module. Agent functions accept arbitrary lambdas, leaving the process state more exposed to accidental corruption than GenServer state. Choose GenServer when the process must handle plain messages, termination logic, or idle timeouts; agents cannot implement those behaviors. section 10.2.3 section 10.2.4
