Skip to content
Back to skills

Elixir

ASecurity

Elixir is a functional language on the Erlang VM (BEAM) for concurrent, fault-tolerant and distributed systems. Use when someone asks to "write Elixir", "build a Phoenix app", "Phoenix LiveView", "GenServer", "supervision tree", "Ecto schema or changeset", "mix project", or needs lightweight processes, "let it crash" reliability and real-time web UIs.

  • 142 stars
  • 0 votes
  • 0 copies
  • 10 views
  • Added May 27, 2026
developmentjavascriptgojavabashsqlnodegitapidatabase

Works with

  • terminal
  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill elixir --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Elixir?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Elixir
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-elixir/badge)](https://www.skillsdirectory.com/skills/terminalskills-elixir)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: elixir
description: >-
  Elixir is a functional language on the Erlang VM (BEAM) for concurrent, fault-tolerant and
  distributed systems. Use when someone asks to "write Elixir", "build a Phoenix app",
  "Phoenix LiveView", "GenServer", "supervision tree", "Ecto schema or changeset", "mix project",
  or needs lightweight processes, "let it crash" reliability and real-time web UIs.
license: Apache-2.0
compatibility: "Elixir 1.17+ (current 1.20) on Erlang/OTP 27+; Phoenix 1.8 needs Elixir 1.17+ and PostgreSQL by default"
metadata:
  author: terminal-skills
  version: 1.1.0
  category: development
  repository: "https://github.com/elixir-lang/elixir"
  tags:
    - elixir
    - erlang
    - beam
    - phoenix
    - concurrency
---

# Elixir — Functional Language for Scalable Applications

## Overview

Elixir runs on the BEAM virtual machine, where millions of cheap isolated processes talk by message passing and are restarted by supervisors when they crash. Typical stack: Phoenix (web framework, current 1.8), LiveView (server-rendered real-time UI), Ecto (database layer), Phoenix.PubSub (events across nodes), `:telemetry` (metrics). Build tool is `mix`; packages come from Hex.

## Instructions

### Install and create a project

```bash
brew install elixir                        # macOS; Ubuntu/Fedora/Arch/Windows options: elixir-lang.org/install
elixir -v                                  # check Elixir and OTP versions

mix new rate_limiter --sup                 # plain OTP app with a supervision tree
mix test                                   # run ExUnit tests

mix archive.install hex phx_new            # Phoenix project generator
mix phx.new shop_app                       # add --database sqlite3 to skip PostgreSQL
cd shop_app && mix setup                   # deps.get, ecto.create, ecto.migrate, assets
mix phx.server                             # http://localhost:4000
```

On Linux install `inotify-tools` so Phoenix live reload works. `mix help phx.new` lists generator options; `mix phx.gen.auth` generates authentication.

### Language basics

```elixir
# Pattern matching
{:ok, user} = fetch_user(id)
%{name: name, email: email} = user

# Pipeline operator (chain transformations)
result =
  data
  |> Enum.filter(&(&1.active))
  |> Enum.map(&transform/1)
  |> Enum.sort_by(& &1.score, :desc)
  |> Enum.take(10)

# Modules and functions
defmodule MyApp.Accounts do
  def create_user(attrs) do
    %User{}
    |> User.changeset(attrs)
    |> Repo.insert()
  end

  def authenticate(email, password) do
    with {:ok, user} <- get_user_by_email(email),
         true <- Bcrypt.verify_pass(password, user.password_hash) do
      {:ok, user}
    else
      _ -> {:error, :invalid_credentials}
    end
  end
end
```

### GenServer (stateful processes)

```elixir
defmodule MyApp.RateLimiter do
  use GenServer

  # Client API
  def start_link(opts) do
    GenServer.start_link(__MODULE__, opts, name: __MODULE__)
  end

  def check_rate(user_id, limit \\ 100) do
    GenServer.call(__MODULE__, {:check, user_id, limit})
  end

  # Server callbacks
  @impl true
  def init(_opts) do
    schedule_cleanup()
    {:ok, %{}}                            # Initial state: empty map
  end

  @impl true
  def handle_call({:check, user_id, limit}, _from, state) do
    now = System.monotonic_time(:second)
    requests = Map.get(state, user_id, [])
    recent = Enum.filter(requests, &(&1 > now - 60))

    if length(recent) < limit do
      {:reply, :ok, Map.put(state, user_id, [now | recent])}
    else
      {:reply, {:error, :rate_limited}, state}
    end
  end

  @impl true
  def handle_info(:cleanup, state) do
    now = System.monotonic_time(:second)
    cleaned = Map.new(state, fn {k, v} ->
      {k, Enum.filter(v, &(&1 > now - 60))}
    end)
    schedule_cleanup()
    {:noreply, cleaned}
  end

  defp schedule_cleanup, do: Process.send_after(self(), :cleanup, 60_000)
end
```

### Phoenix LiveView (real-time UI)

```elixir
defmodule MyAppWeb.DashboardLive do
  use MyAppWeb, :live_view

  @impl true
  def mount(_params, _session, socket) do
    if connected?(socket) do
      Phoenix.PubSub.subscribe(MyApp.PubSub, "metrics")
      :timer.send_interval(5000, :tick)
    end

    {:ok, assign(socket,
      users_online: 0,
      orders_today: 0,
      revenue: Decimal.new(0)
    )}
  end

  @impl true
  def handle_info(:tick, socket) do
    {:noreply, assign(socket,
      users_online: MyApp.Presence.count(),
      orders_today: MyApp.Orders.count_today(),
      revenue: MyApp.Orders.revenue_today()
    )}
  end

  @impl true
  def handle_info({:new_order, order}, socket) do
    {:noreply, update(socket, :orders_today, &(&1 + 1))
    |> update(:revenue, &Decimal.add(&1, order.total))}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div class="grid grid-cols-3 gap-4">
      <.stat_card title="Users Online" value={@users_online} />
      <.stat_card title="Orders Today" value={@orders_today} />
      <.stat_card title="Revenue" value={"$#{@revenue}"} />
    </div>
    """
  end
end
```

### Supervision trees

```elixir
defmodule MyApp.Application do
  use Application

  def start(_type, _args) do
    children = [
      MyApp.Repo,                         # Database (auto-restarts on crash)
      {Phoenix.PubSub, name: MyApp.PubSub},
      MyApp.RateLimiter,                  # Custom GenServer
      MyAppWeb.Endpoint,                  # Web server
      {Task.Supervisor, name: MyApp.TaskSupervisor},
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
    # If any child crashes, only that child restarts (one_for_one)
  end
end
```

## Examples

### Example 1: Add a rate limiter that survives crashes

Request: "Limit each user to 100 API calls a minute, and don't lose the service if it crashes."

Create `lib/shop_app/rate_limiter.ex` with the GenServer above, add `ShopApp.RateLimiter` to the supervisor's `children`, then call `ShopApp.RateLimiter.check_rate(user.id)` in a plug. Result: `:ok` for the first 100 calls in a window, `{:error, :rate_limited}` after; if the process dies the supervisor restarts it with empty state.

### Example 2: Live dashboard without JavaScript

Request: "Show orders today and revenue on an admin page that updates itself."

```bash
mix phx.gen.live Orders Order orders total:decimal status:string   # context, schema, LiveViews, migration
mix ecto.migrate
```

Add the `DashboardLive` module above to `router.ex` inside a `live_session`, broadcast `{:new_order, order}` with `Phoenix.PubSub.broadcast(ShopApp.PubSub, "metrics", {:new_order, order})` after each insert. The page updates over the WebSocket for every open browser.

## Guidelines

- **Let it crash**: do not wrap everything in try/rescue; put state in supervised processes and pick the strategy deliberately (`:one_for_one` restarts only the failed child).
- **Pattern match** on function heads and `{:ok, _}` / `{:error, _}` tuples instead of if/else chains; use `with` for chains of fallible steps.
- **Do not hold all state in one GenServer**: a single process serializes every call. Use ETS, Registry or a process per entity when throughput matters; `GenServer.call` times out after 5 s by default.
- **Ecto changesets** validate and cast all external input; never pass raw params to `Repo.insert`.
- **LiveView**: subscribe and start timers only inside `if connected?(socket)`; keep assigns small, use streams (`stream/3`) for long lists.
- **Never** create atoms from user input (`String.to_atom/1`); atoms are not garbage collected.
- **Observability**: attach to `:telemetry` events (Ecto and Phoenix emit them); Phoenix ships a `LiveDashboard` for dev.
- CPU-heavy numeric work is not the BEAM's strength; use Nx or a NIF/port for it.

Files in this skill

  • SKILL.md5.8 KB
  • _scores.json1.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.