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…