The reading order
-
1.
Elixir's Concurrency Model Is the One You Actually Want
The foundation: processes, supervision, and why the concurrency model other stacks fake with queues and retries is the default here.
-
2.
Elixir's BEAM Is the Runtime AI Agents Want
Agents are long-running, stateful, failure-prone processes — exactly the workload the BEAM was designed for four decades ago.
-
3.
Cisco's Network Config Engine Is Secretly Erlang
The proof nobody cites: Cisco NSO and ConfD — the config engine telcos push transactional changes through — run their core on an Erlang VM, built by Tail-f, founded by Ericsson's original Erlang team and acquired for roughly $175M in 2014. Why open-source network automation went Python anyway, and the bounded side tool that's the right first move.
-
4.
Why We'd Pick Elixir for an AI Startup Backend
An AI backend is agents holding a session for seconds or for hours, token streams pushing model output to a live UI, six flaky tool calls per turn, and durable multi-step jobs — the property Python and Node concurrency are weakest at. The costs stated plainly: the model and ML layer is still Python's, the hiring pool is smaller, and mostly-stateless CRUD over a hosted model API buys you little.
-
5.
What Elixir Gives a Coding Harness for Free
José Valim's three mechanics for a coding harness, taken seriously: hot-code reload that keeps a session's state through a plugin swap, client/server as a byproduct of the actor model, and Node.connect/1 plus Node.spawn/3 splitting the brains (model + session) from the hands (sandbox + tools) — the shape Livebook's attached-node runtime already ships. The tax he names only in passing: distribution moves execution without deciding what that execution may do, so Docker or a microVM is still yours to build, plugin authors write TypeScript, and provider protocol churn reaches Python and TS first. Internal harness on a stack you already run, yes; general-market harness, the ecosystem tax compounds on every axis adoption depends on.
-
6.
Why Every AI Agent Framework Is Written in Go (And What That Costs You)
The steelman: why every agent framework picked Go, and the supervision, hot-code, and state-recovery costs hiding in that choice.
-
7.
Elixir Is the Language AI Codes Best
Pattern matching, immutability, and a small surface area — why LLMs generate better Elixir than Python or TypeScript.
-
8.
TDD With Claude Code in Elixir: What Holds Up
Three tests in this repo, three different answers to "is this safe concurrently" — and marking a new test async: true without checking is the ExUnit mistake an agent makes unprompted. The argument: mix precommit, compile --warnings-as-errors plus the full suite, is the instruction Claude actually follows, and the Oban idempotency test gets written before the job body, because an agent generating job code from a prompt has no way to know retries are in play.
-
9.
Ruby Isn't Dead, It Got Boring — And Boring Is Why It Ships
The counterweight: boring is a feature. If the team knows Rails, the right AI stack might be the one that ships this quarter.
-
10.
What Together AI's $800M Round Says About Elixir
Together AI's $800M Series C at an $8.3B post-money valuation, read off the Greenhouse listing instead of the press release: authentication flows including SSO and OAuth, organizations, projects, API keys, and role-based access controls, on Elixir/Phoenix services. Elixir sits at 2.7% of respondents in the 2025 Stack Overflow Developer Survey — raw adoption share answers the wrong question. Corrected September 2026: the listing was later removed and the present-tense hiring claim no longer holds.
-
11.
Build an AI Agent Loop in 50 Lines of Elixir
The whole argument, executable: observe-decide-act in 50 lines with no framework — a GenServer doing what agent frameworks abstract.
-
12.
I Read Oban's Source Code So You Don't Have To
Read from oban-bg/oban at v2.23.1 rather than the README: job fetching is a CTE wrapping FOR UPDATE SKIP LOCKED to fence the planner, the Stager polls once a second on the leader and notifies queues rather than having workers poll, and leader election is an INSERT ... ON CONFLICT whose WHERE clause matches only the inserting node — which is what makes a challenger fail and the sitting leader renew. The cost on the same line: high UPDATE churn on one table, dead tuples outpacing autovacuum, and a bloated table slowing the fetch itself.
-
13.
Testing AI Agent Outputs in Elixir with ExUnit
Two seams make the 50-line agent loop testable: thread opts down to Req.post/1 so a test can pass plug: {Req.Test, Agent.LLM}, and emit an [:agent, :tool_call] telemetry event so you can assert on tool-call sequences, not just the final string. Tribunal's faithfulness and hallucination checks cost a model call and tag themselves :eval, which test_helper.exs excludes by default — mix test --only eval runs them.
-
14.
15 Elixir Libraries I Reach For in 2026
The day-one Hex install list — what each library earns its place doing, and the ones that got dropped along the way.
-
15.
RAG in Phoenix: Hand-Rolled pgvector or Arcana?
You don't need a vector database — the Postgres you already run does RAG fine. The hand-rolled path, and when Arcana earns its place.
-
16.
Streaming LLM Tokens in LiveView, the 2026 Way
The naive version — accumulate each token into an assign and re-render — keeps paying the model after the user closes the tab, rebuilds an ever-growing binary on every token, and sits on half a sentence when OpenAI returns a 500 four tokens in. start_async/4 gives a real terminal event through handle_async/3, and Req's function-form :into drives the request inside the task so the upstream socket dies with it — into: :self is the trap that spawns a helper which outlives your task and keeps draining. Buffer 50ms or 20 tokens and flush one update: the rate the model emits tokens should not be the rate you re-render.
-
17.
Oban as a Durable AI Agent Runtime in Elixir
One Oban job per ReAct step, enqueued in the same Ecto.Multi transaction as the state write, so a deploy at step 23 resumes from an agent_runs row instead of dying with a GenServer's heap. {:snooze, seconds} for a rate limit, {:cancel, reason} for a 400 or 422 that will fail identically every time, and an idempotency key derived from run plus step so a retried charge doesn't double-bill.
-
18.
Instrumenting LLM Calls in Phoenix with Telemetry
ReqLLM already emits token counts, calculated cost, and request duration on every call, so the work is five lines of Telemetry.Metrics connecting an event that already exists to a dashboard Phoenix already ships. The hand-rolled :telemetry.span/3 version for raw Req/Finch, the tag_values step that pulls .id out of ReqLLM's LLMDB.Model struct before it reaches a label, and the two numbers worth alerting on: cost per day trending, p95 latency per provider.
-
19.
Build an MCP Server in Phoenix With Hermes
Hermes 0.14 in a Phoenix app: tools as components, one forward to the StreamableHTTP transport plug, and authentication in the server's init/2 — a Plug.Conn assign does not reach a Hermes tool. Every tool maps to a named function in one of your contexts, Billing.customers_over_limit/2, never the lazy single run_sql tool that hands the model your database connection and calls it flexible. Skip the protocol entirely if the only thing calling your app is a script you also wrote.
-
20.
The Ruby to Elixir Migration That Cut Our Service Footprint From Ten to Six
The production receipts: ten services to six at InsideTrack, the migration order that worked, and when not to migrate.
-
21.
Validating LLM Tool Call Arguments With Ecto
Not another output-shape validator — the trust boundary on the arguments a model hands a tool BEFORE it executes. A schemaless changeset over a `{data, types}` tuple fits because tool args arrive as a plain map with a shape per tool and nobody wants an Ecto.Schema module for data they never persist. Three tiers: type, then range and enum, then authorization — and the third is the one no schema can express, because "does this row belong to this caller" is a property of the argument AND a database lookup AND the caller's identity at once. MCP 2026-07-28 put tool inputSchema on full JSON Schema 2020-12, so oneOf composition is now expressible in a schema and still isn't in a changeset; the spec's $ref rule is a MUST NOT and its depth bound only a SHOULD, and both defend the validator against a hostile schema rather than a hostile argument.
-
22.
Throttling LLM Calls in Elixir Before You Hit 429
LLM rate limits are two-dimensional and only one dimension is countable in advance: providers cap requests per minute AND tokens per minute, and a response's token cost isn't knowable until it lands, so a request-counting limiter sails straight past a TPM ceiling. Start-tier Claude Sonnet 5 is 1,000 RPM / 2,000,000 ITPM / 400,000 OTPM; Build takes RPM to 5,000 but the token limits only to 5M and 1M, so scaling every number by the same factor gets you 429'd on the two that matter. Hammer defaults to a fixed window — set `algorithm: :token_bucket` to match Anthropic's own limiter. Reserve an estimate, settle the difference from real usage, and never refund: a denied hit still spends its increment, and there is no safe decrement under concurrent writers.
-
23.
Wiring LiveView Uploads to Claude Vision
max_file_size: 7_500_000 is derived, not round — Claude's base64 image cap is 10MB and encoding inflates raw bytes by roughly 4/3, so a 9.5MB JPEG passes a naive 10MB check and is rejected after the upload finishes. The gate no vendor doc writes for you is page count: a 6MB PDF can carry 80 pages against a 100-page ceiling when the context window is under 1M tokens, and each page costs 1,500 to 3,000 text tokens plus the image tokens of a rendered screenshot. The consume callback returns bytes, not meta.path — the temp file is gone by the time an async task reads it.
-
24.
Structured LLM Output in Elixir: Which Library, When
Four options scored against each other, which the HexDocs-and-GitHub SERP never does. InstructorEx has the name and 781 stars and a repo unpushed since 2025-06-07; InstructorLite is the fork actually shipping. The finding came from reading source instead of a README: ReqLLM's generate_object does not re-prompt the model on a validation failure the way both Instructor libraries do — it coerces locally and, when that fails, hands back the malformed data as a success.
-
25.
Metering LLM Usage Per Customer in Phoenix
A dashboard tolerates an undercount; an invoice line is a claim you make to a paying customer. Cost lands in integer micro-dollars — Sonnet 4.6 at $3 and $15 per million tokens is exactly 3 and 15 micros per token — and rounds to cents once, at invoice time: 1,055,250 micros is 105.525 cents, so 106. Append-only, with a unique index on request_id so a retried job can't double-bill. tenant_id is a required argument on the wrapper, because there is no honest way to reconstruct whose bill a call belongs to afterward.
-
26.
Phoenix Secrets: runtime.exs, Fly.io, and LLM Keys
config.exs freezes whatever the build machine happened to have; runtime.exs reads the environment the process is actually running in. fetch_env!/1 over get_env/2 with a fallback, so a missing key raises at boot and Fly stops the rollout rather than surfacing as a 401 a customer finds first. Dotenvy never calls System.put_env, so a dev .env can't bleed into the production read path. And an LLM key is a bearer credential against a metered bill — GitGuardian has AI-service secrets up 81% year over year, with sk-ant- and sk-proj- now as fingerprintable as AKIA.
-
27.
10 Ecto Query Patterns for N+1 and Beyond
Separate queries for has_many, a join for belongs_to — a row-count argument, not a style preference. Join a post to its ten comments and Postgres repeats the post's columns ten times; join it to its author and nothing multiplies. Most of the N+1s worth unwinding were never missing preloads at all; they were a has_many joined the way you'd correctly join a belongs_to. The other nine patterns are the exceptions, each with its own trigger.
-
28.
Isolating Parallel Claude Code Agents in Phoenix
A worktree isolates the file tree and the branch. It does nothing about the port, the database, or the asset watchers, which is exactly where a Phoenix app breaks. Give each worktree its own database named after the slot rather than the branch — Ecto's Sandbox fences concurrent tests inside one node and has no opinion at all about two mix phx.server processes writing the same table. Practical ceiling is four or five, and coupled work is still lazier run one at a time.
-
29.
12 Phoenix LiveView Patterns for Production
Twelve named failure modes verified against LiveView 1.1, and eleven are one insight in different clothes: the dead render and the connected socket are two different worlds, and the client side of that boundary is hostile. mount runs twice, so an unguarded query bills the database twice and a PubSub subscribe in the dead pass leaks. And mount authorization gates viewing the page and nothing else — a client can push any event with any payload down the socket, ids belonging to other tenants included.
Other topic guides
- AI engineering What production AI engineering actually looks like in 2026 — the autonomy ladder for agents, the workflow shift, telemetry, and team sizing.
- Security for startups A guided reading order for startup security — what to read first on SOC 2 as a revenue tool, vCISO hiring, and securing AI-native products.
- Engineering leadership Engineering leadership at startup scale — hiring from one engineer to fifteen, rituals that work at small teams, the staff-engineer interview loop.
- Omarchy on Apple Silicon Running Omarchy on an M1 MacBook with coding agents — the install, what breaks, the keyboard, and what a security lead inherits when agents run unattended.