Curated library of 38 atomic skills, 7 personas, and 1 orchestrator for Elixir and Phoenix development. Organized by category: fundamentals, phoenix, database, testing, auth, infrastructure, quality, security, integrations, tooling, frameworks, personas, and orchestration. Covers core Elixir patterns, Phoenix LiveView, Ecto, OTP, Oban, testing, security, deployment, real-time, and modern tooling (Req, Swoosh, Cachex, Broadway, Ash).
91
91%
Does it follow best practices?
Impact
91%
1.37xAverage score across 56 eval scenarios
Advisory
Suggest reviewing before use
Use this skill before writing ANY telemetry, logging, or metrics code.
Logger.info("action", key: value)) — never string interpolation in log messagesApplication.start/2 — not in modules that may restart, and never in GenServer initEcto.Repo telemetry events for query monitoring — Ecto already emits events; don't manually instrument queriesPhoenix.LiveDashboard in dev/staging — free observability with zero code:debug level in production — it includes query parameters and PII; use :info level instead❌ Bad — unsearchable:
Logger.info("User #{user.id} created order #{order.id} for $#{order.total}")✅ Good — searchable, parseable:
Logger.info("Order created", user_id: user.id, order_id: order.id, total: order.total)# In a Plug
defmodule MyAppWeb.Plugs.RequestMetadata do
import Plug.Conn
def call(conn, _opts) do
Logger.metadata(
request_id: conn.assigns[:request_id] || Ecto.UUID.generate(),
remote_ip: to_string(:inet.ntoa(conn.remote_ip))
)
conn
end
end# Emit a custom event
:telemetry.execute(
[:my_app, :orders, :created], # event name
%{count: 1, total_cents: 4999}, # measurements
%{user_id: user.id, source: :web} # metadata
)See assets/telemetry_handler_snippets.ex for a copy-paste template that attaches handlers in start/2, detaches them in stop/1, and implements handle_event/4.
✅ Good — in application.ex:
defmodule MyApp.Application do
use Application
@impl true
def start(_type, _args) do
MyApp.Telemetry.attach_handlers()
children = [MyApp.Repo, MyAppWeb.Endpoint]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
defmodule MyApp.Telemetry do
require Logger
def attach_handlers do
:telemetry.attach_many("my-app-handlers", [
[:my_app, :orders, :created],
[:my_app, :payments, :processed]
], &handle_event/4, nil)
end
def handle_event([:my_app, :orders, :created], measurements, metadata, _config) do
Logger.info("Order created",
total_cents: measurements.count,
user_id: metadata.user_id
)
end
endAfter attaching handlers, confirm everything is wired up in iex:
# In iex -S mix
:telemetry.execute([:my_app, :orders, :created], %{count: 1, total_cents: 4999}, %{user_id: 42, source: :web})
# Expected: Logger output like — [info] Order created total_cents=1 user_id=42If no log line appears, verify attach_handlers/0 was called before the event and that the event name matches exactly.
def process_order(order) do
:telemetry.span([:my_app, :orders, :process], %{order_id: order.id}, fn ->
result = do_process(order)
{result, %{order_id: order.id, status: :completed}}
end)
enddef handle_slow_query(_event, measurements, metadata, %{threshold_ms: threshold}) do
duration_ms = System.convert_time_unit(measurements.total_time, :native, :millisecond)
if duration_ms > threshold do
Logger.warning("Slow query",
duration_ms: duration_ms,
source: metadata.source,
query: metadata.query
)
end
end# router.ex
import Phoenix.LiveDashboard.Router
scope "/" do
pipe_through :browser
live_dashboard "/dashboard",
metrics: MyAppWeb.Telemetry,
ecto_repos: [MyApp.Repo]
endDeeper topics: For performance profiling and benchmarking, see the
benchee-profilingskill. For exporting metrics to external tools (Prometheus, Datadog) and production deployment considerations, see thedeployment-gotchasskill. For low-level metric aggregation and reporter configuration, consult the Telemetry.Metrics and TelemetryMetricsPrometheus library docs.
| ❌ Don't | ✅ Do |
|---|---|
| Interpolate values into the log message string | Pass structured metadata: Logger.info("event", key: value) |
Attach handlers inside a module init / GenServer that restarts | Attach once in Application.start/2, detach in stop/1 |
Run heavy work inside handle_event/4 (blocks the caller) | Keep handlers fast; offload slow work to a process/queue |
| Manually instrument every Ecto query | Consume Ecto's built-in [:my_app, :repo, :query] events |
| Emit events without correlation metadata | Tag events with user_id / request_id for traceable logs |
Leave :debug logging on in production | Use :info; :debug leaks query params and PII |
| Predecessor | This Skill | Successor |
|---|---|---|
| otp-essentials | telemetry-essentials | deployment-gotchas |
| otp-essentials | telemetry-essentials | benchee-profiling |
| security-essentials | telemetry-essentials | deployment-gotchas |
.tessl-plugin
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10
scenario-11
scenario-12
scenario-13
scenario-14
scenario-15
scenario-16
scenario-17
scenario-18
scenario-19
scenario-20
scenario-21
scenario-22
scenario-23
scenario-24
scenario-25
scenario-26
scenario-27
scenario-28
scenario-29
scenario-30
scenario-31
scenario-32
scenario-33
scenario-34
scenario-35
scenario-36
scenario-37
scenario-38
scenario-39
scenario-40
scenario-41
scenario-42
scenario-43
scenario-44
scenario-45
scenario-46
scenario-47
scenario-48
scenario-49
scenario-50
scenario-51
scenario-52
scenario-53
scenario-54
scenario-55
scenario-56
skills
frameworks
ash-framework
infrastructure
orchestration
elixir-skill-router
personas
phoenix
quality
security
security-essentials
tooling
mix-tasks-generators