You've heard AI can write React code. But what about functional languages where the rules are different?
You'll learn:
- Where AI actually helps in Elixir/Phoenix (and where it fails)
- Real examples of GenServer code generation quality
- What to watch for when accepting AI suggestions
Time: 8 min | Level: Intermediate
Problem: Functional Code Isn't Just OOP Translated
AI models trained heavily on JavaScript and Python struggle with Elixir's immutability, pattern matching, and OTP conventions. Copy-pasting AI suggestions often leads to anti-patterns like stateful GenServers or missing supervision trees.
Common issues:
- AI generates mutable-looking code that doesn't compile
- Missing
usestatements and Phoenix context conventions - Wrong LiveView mount/handle_event patterns
- Incorrect Ecto query composition
Where AI Actually Helps
Boilerplate & Schema Generation
AI excels at repetitive Phoenix structures:
# Prompt: "Create a User schema with email, name, and timestamps"
defmodule MyApp.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :email, :string
field :name, :string
timestamps()
end
def changeset(user, attrs) do
user
|> cast(attrs, [:email, :name])
|> validate_required([:email])
|> validate_format(:email, ~r/@/)
|> unique_constraint(:email)
end
end
Expected: This is 90% correct. You'll still need to adjust validations.
Watch for: Missing indexes in migration, weak validations
Phoenix Context Functions
AI handles CRUD functions well:
# Prompt: "Add list_users/0 and get_user!/1 to Accounts context"
def list_users do
Repo.all(User)
end
def get_user!(id), do: Repo.get!(User, id)
Why this works: These are standardized patterns AI has seen thousands of times.
Where AI Struggles
GenServer State Management
AI often generates this:
# ⌠AI-generated anti-pattern
defmodule MyApp.Cache do
use GenServer
def start_link(_) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
def get(key) do
GenServer.call(__MODULE__, {:get, key})
end
def put(key, value) do
# AI forgets state updates don't persist automatically
GenServer.call(__MODULE__, {:put, key, value})
end
def handle_call({:put, key, value}, _from, state) do
{:reply, :ok, state} # ❌ Returns old state, loses data
end
end
What's wrong: State not updated in handle_call. AI treats it like OOP mutation.
Fix it yourself:
# ✅ Correct version
def handle_call({:put, key, value}, _from, state) do
{:reply, :ok, Map.put(state, key, value)} # Returns NEW state
end
If AI generates GenServers, always verify:
- State transformations return new state
- Supervision tree placement (
start_linkin supervisor) - Proper use of
:viatuples for named processes
Phoenix LiveView Event Handlers
AI mixes up mount/3 and handle_event/3 signatures:
# ⌠AI often generates
def mount(_params, _session, socket) do
{:ok, socket} # Missing assign
end
def handle_event("save", params, socket) do
# AI forgets to return proper tuple
save_data(params)
socket # ❌ Wrong return type
end
Fix:
# ✅ Correct
def mount(_params, _session, socket) do
{:ok, assign(socket, :count, 0)}
end
def handle_event("save", params, socket) do
case save_data(params) do
{:ok, _} -> {:noreply, socket}
{:error, _} -> {:noreply, put_flash(socket, :error, "Failed")}
end
end
Rule: Always return {:ok, socket} from mount, {:noreply, socket} from handle_event.
Pipe Operator Misuse
AI loves pipes but uses them incorrectly:
# ⌠AI-generated
User
|> where([u], u.active == true) # ❌ Doesn't work without Ecto.Query
|> Repo.all()
# ✅ Correct
import Ecto.Query
User
|> where([u], u.active == true)
|> Repo.all()
Watch for: Missing imports, piping into functions that don't take the piped value as first arg.
Testing AI-Generated Code
Run these checks on any AI output:
# 1. Compile check
mix compile --warnings-as-errors
# 2. Format violations
mix format --check-formatted
# 3. Linter catches common mistakes
mix credo --strict
# 4. Dialyzer for type issues (slow but catches AI logic errors)
mix dialyzer
You should see: No warnings. AI code often has unused variables or missing specs.
When to Use AI for Elixir
Good for:
- Phoenix controllers/views boilerplate
- Ecto schema definitions
- Test case templates
- Documentation comments
Avoid AI for:
- GenServer implementations
- Supervision tree architecture
- Complex Ecto queries with joins
- Custom macros or metaprogramming
Why: AI lacks understanding of OTP design principles and process lifecycle.
What You Learned
- AI handles Phoenix web layer better than OTP
- Always verify state management in GenServers
- Use
mix credoandmix dialyzerto catch AI mistakes - Boilerplate is safe, architecture is not
Limitation: Models as of early 2026 are trained mostly on Elixir 1.12-1.14 code. Phoenix 1.7+ LiveView patterns may be hit-or-miss.