Packages
nous
0.14.0
0.17.0
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.17
0.12.16
0.12.15
0.12.14
0.12.13
0.12.12
0.12.11
0.12.9
0.12.7
0.12.6
0.12.5
0.12.3
0.12.2
0.12.0
0.11.3
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.5.0
AI agent framework for Elixir with multi-provider LLM support
Current section
Files
Jump to
Current section
Files
lib/nous/skills/otp_patterns.ex
defmodule Nous.Skills.OtpPatterns do
@moduledoc "Built-in skill for OTP supervision, GenServer, and concurrency patterns."
use Nous.Skill, tags: [:elixir, :otp, :genserver, :supervisor, :concurrency], group: :coding
@impl true
def name, do: "otp_patterns"
@impl true
def description, do: "OTP supervision trees, GenServer patterns, and concurrency design"
@impl true
def instructions(_agent, _ctx) do
"""
You are an OTP specialist. Follow these patterns:
1. **Use GenServer ONLY when you need**: mutable runtime state, concurrency/isolation, or failure/restart handling. Never use GenServer for code organization — use plain modules.
2. **Let it crash**: Write happy-path code. Let unexpected failures crash the process. Supervisors handle restart. Don't wrap everything in try/catch:
```elixir
# Wrong: defensive try/catch everywhere
# Right: let it crash, supervisor restarts
def process(data), do: transform!(data) |> store!()
```
3. **Supervision strategies**:
- `:one_for_one` — restart only the failed child (default, most common)
- `:one_for_all` — restart all children (tightly coupled processes)
- `:rest_for_one` — restart failed + all started after it (dependency chain)
4. **Wrap GenServer in client API**:
```elixir
def get_value, do: GenServer.call(__MODULE__, :get_value)
def set_value(v), do: GenServer.cast(__MODULE__, {:set_value, v})
```
5. **Use Task.Supervisor for one-off async work** — not bare `Task.async/1`:
```elixir
Task.Supervisor.start_child(MyApp.TaskSupervisor, fn -> do_work() end)
```
6. **GenServer is a bottleneck by design**: All calls are serialized. For high-throughput, consider ETS, `:persistent_term`, or a pool of workers.
7. **Restart policies**: `:permanent` for services, `:temporary` for one-off tasks, `:transient` for tasks that should only restart on abnormal exit.
8. **Never store domain entities (User, Order) as processes**: Use the database with optimistic locking. Processes are for runtime concerns.
"""
end
@impl true
def match?(input) do
input = String.downcase(input)
String.contains?(input, [
"genserver",
"supervisor",
"otp",
"supervision tree",
"task.async",
"process",
"gen_server",
"dynamic_supervisor",
"let it crash"
])
end
end