Skip to content

ADR-0011: Migration to Elixir/Phoenix + PostgreSQL

Status: Accepted Date: 2026-04-14 Supersedes: ADR-0001 (Modular Monolith — Laravel), ADR-0006 (Livewire over Hotwire) Supporting documents: _bmad-output/brainstorming-tech-stack-review-2026-04-13.md, _bmad-output/brainstorming-elixir-migration-plan-2026-04-14.md Project codename: Finnest Repo: AgenticAI-finnest

Context

AgenticAI is an agent-native workforce management platform for the labour hire industry, spanning recruitment (Scout), verification (Verify), communications (Reach), operations (Pulse), and future HR/Payroll/Finance domains. The architecture must support a swarm of concurrent AI agents performing atomic tasks, a hybrid UI model (structured screens + agent-composed views + autonomous inbox), and self-healing fault tolerance.

The current codebase is a Laravel 13 monolith (25K LOC, 148 stories, 19 build days). It has not launched and has no customers. This makes the cost of a full rewrite bounded and known — there is no production data migration, no customer disruption, and no legacy API contract to honour.

The platform's defining characteristic — a self-healing swarm of concurrent agents — is an architectural concern, not a feature concern. The choice of runtime determines whether this is structural (built into the platform) or bolted on (assembled from external tools). This ADR evaluates that choice.

Decision

Migrate the AgenticAI platform from Laravel 13 / MySQL / Blade + Livewire to Elixir / Phoenix / PostgreSQL / LiveView. The current AgenticAI-app repository enters maintenance mode after Sprint 46. All new development occurs in the AgenticAI-finnest repository.

Why Now

  • Product has not launched — no customers, no production data, no API contracts
  • The 25K LOC codebase was built in 19 days; estimated rebuild is 28-32 days
  • Delaying means every sprint adds migration cost (more code to port, more tests to rewrite)
  • The agent layer (the product's core differentiator) hasn't been built yet — building it on the right runtime avoids a future re-platform

Alternatives Considered

Technology Stack Evaluation

Eight frameworks were evaluated across 11 capabilities weighted by agent-architecture importance. Two factors were eliminated: hiring pool (irrelevant at 2-person team scale) and MCP support (not needed for a purpose-built agent platform where we control both agents and tools).

Stack Weighted Score Primary Strength Fatal Weakness
Elixir/Phoenix 69 BEAM VM: concurrent, fault-tolerant, self-healing processes by design No official Anthropic/OpenAI SDK (closable: ~200 LOC HTTP wrapper)
TypeScript/Next.js 70 Best AI SDK ecosystem; Vercel AI SDK for generative UI Single-threaded event loop; no native supervision (architectural — permanent)
Python/FastAPI 59 Deepest AI/ML library ecosystem No UI framework — requires two-language stack
Go 55 Goroutine concurrency; Google ADK No UI framework — always a two-language stack
Rust/Axum 48 Extreme performance Borrow checker confuses LLMs; 3-5x slower development velocity
Ruby on Rails Eliminated Same tier as Laravel for CRUD No agent architecture advantage
Kotlin/Spring Boot Eliminated JVM enterprise maturity Enterprise bloat; no agent paradigm
C#/.NET Eliminated Azure ecosystem Vendor lock-in; overkill for team size

The Tiebreaker: Nature of Gaps

Elixir (69) and TypeScript (70) scored within 1 point — effectively tied. The decision rests on the nature of each stack's weaknesses:

Elixir's gaps are ecosystem gaps (closing over time): - AI SDK: HTTP wrapper (~200 LOC) bridges it today; official SDK likely within 12 months - AI-assisted development: ~70% as effective as TypeScript today; improving as LLM training data grows

TypeScript's gaps are architectural limitations (permanent): - No native concurrency model for agent swarms (async/await is concurrent I/O, not parallel computation) - No built-in self-healing (PM2/K8s are external assembly, not architecture) - Single-threaded event loop bottlenecks under CPU-heavy agent workloads

Ecosystem gaps shrink. Architectural gaps require re-platforming.

Functional Actor Model vs Microservices

The BEAM VM's actor model was evaluated against a microservices architecture for delivering the agent swarm:

Dimension Actor Model (BEAM) Microservices
Operational complexity Low — one deploy, built-in distribution High — K8s, service mesh, observability stack
Latency Microsecond message passing Millisecond network calls + serialisation
Fault isolation Per-process (granular, cheap) Per-service (coarse, expensive)
Team scaling Harder to split across many teams Designed for independent team ownership
Distributed state Built-in (ETS, Mnesia, CRDT libs) Requires external stores + cache layers

Microservices solve an organisational problem (independent teams deploying independently) by paying a massive technical tax (network complexity, distributed failure modes). For a two-person team, this is the wrong tradeoff. The actor model provides microservice-like fault isolation with monolith-like operational simplicity.

Functional Actor Model vs OOP for HR/Payroll/Finance

The HR/Payroll/Finance domain was specifically evaluated for paradigm fit, since these are traditionally OOP-dominated domains.

Where the functional actor model wins:

Domain Characteristic Functional/Actor Advantage
Payroll calculations Pure function pipelines: gross → deductions → tax → super → net. Same input always produces same output. No hidden state mutations. Deterministic, auditable, testable in isolation.
Lifecycle state machines Employee, pay run, leave request lifecycles map directly to supervised GenServer processes. Each instance is isolated — one employee's bad data doesn't crash the whole pay run.
Bulk operations "Process payroll for 5,000 employees" becomes 5,000 independent processes. Structural fault isolation without defensive coding.
Audit trails Immutable data + event sourcing gives point-in-time reconstruction for free. OOP's default of mutating objects in place fights this pattern.
Compliance rules Pattern matching makes each rule clause explicit and exhaustive. The compiler warns on missing cases. Tax brackets, leave entitlements, award interpretation — all benefit from exhaustive matching.

Where OOP feels more natural:

Aspect OOP Advantage
Domain modelling intuition Business stakeholders think in nouns (Employee, PayRun). OOP maps 1:1. Functional requires a mindset shift to data + transformations.
DDD ecosystem The entire DDD body of knowledge (aggregates, entities, value objects) assumes OOP. Functional DDD exists but has less institutional knowledge.
Industry precedent Every major HR/payroll system (SAP, Workday, MYOB, Xero, ADP) is built on OOP.

Assessment: OOP's advantages are knowledge problems (learning curve, familiarity), not technical limitations. They're solved by learning Elixir's idioms for domain modelling. The functional actor model's advantages are structural properties (correctness, concurrency, auditability) that cannot be bolted onto OOP after the fact. For financial/HR software where correctness is non-negotiable, this tradeoff favours functional.

Database: PostgreSQL over MySQL

Feature MySQL PostgreSQL Agent-Native Need
Geospatial queries Basic spatial index PostGIS (industry standard) "Workers within 30km of site"
Concurrent booking FOR UPDATE FOR UPDATE SKIP LOCKED 50 workers competing for 3 shifts
JSON operations Basic jsonb + GIN indexes Agent memory, skill matching, AI responses
LISTEN/NOTIFY No Yes Real-time agent coordination, dashboard updates
Array columns No Native integer[], text[] Multi-skill matching, tag queries
Partial indexes No Yes "Available workers only" index

Ecto (Elixir's database library) has excellent PostgreSQL support with compile-time query validation and native support for PostgreSQL-specific types.

Migration Plan

Transition Strategy

Environment Current (Laravel) Future (Elixir)
integration.agentic-ai.au CI auto-deploy Repurpose for AgenticAI-finnest
staging.agentic-ai.au CI auto-deploy Repurpose for AgenticAI-finnest
app.agentic-ai.au Production Stays on Laravel until cutover

Build Order

Phase Scope Sprints
1. Platform Foundation Phoenix scaffold, PostgreSQL, auth, multi-tenancy, component library, CI basics 1-2
2. Scout Domain Port domain model, scoring, pipelines (business logic unchanged) 3+
3. Verify Domain Port verification pipeline, AI provider integration N+
4. Agent Layer GenServer-based orchestrator, tool definitions, prompt bar, agent-composed UI M+

Rewrite Estimate

Factor Multiplier Rationale
AI-assist quality ~1.3x slower Claude writes ~70% as effective Elixir vs PHP
Ecosystem gap ~1.2x slower Auth, admin, feature flags need alternatives to Laravel packages
Learning curve ~1.3x slower (front-loaded) Functional paradigm shift; week 1 rough, week 3 idiomatic
Code density ~0.7x Elixir is typically 30-40% less LOC for equivalent logic
Combined ~1.5-1.7x
Scenario Timeline
Laravel (actual) 19 days
Elixir (estimated) 28-32 days (~4.5 weeks)

Toolchain Translation

Laravel Elixir Equivalent
Pint (code style) mix format (built-in, zero config)
PHPStan (static analysis) Dialyzer (type specs + success typing)
Deptrac (domain boundaries) Boundary library or custom mix task
PHPMD (mess detection) Credo (code analysis)
Pest (testing) ExUnit (built-in)
Horizon (queues) Oban + Oban Web
Livewire (reactive UI) LiveView (built-in)
Eloquent (ORM) Ecto (data mapping)
Form Requests (validation) Ecto changesets
preventLazyLoading() Ecto doesn't lazy-load by default

Consequences

Positive: - Agent swarm architecture is structural — GenServer processes ARE agents, supervisor trees provide self-healing, message passing enables coordination - LiveView handles all three UI modes natively (structured screens, agent-composed views, autonomous inbox) without a JavaScript framework - Pure function pipelines for payroll/financial calculations — deterministic, auditable, testable in isolation - Per-process fault isolation means one bad record doesn't crash a bulk operation - Pattern matching provides exhaustive compliance rule dispatch with compiler warnings on missing cases - PostgreSQL's jsonb, LISTEN/NOTIFY, PostGIS, and array columns are purpose-built for agent state, real-time coordination, and geospatial matching - Hot code upgrades possible (deploy new agent logic without dropping connections) - No future re-platforming needed — the agent runtime is the application runtime

Negative: - 28-32 day rebuild investment before new feature delivery resumes - AI-assisted development ~30% less effective initially (improving over time) - Thinner package ecosystem — some Laravel conveniences require manual implementation - Functional programming learning curve (front-loaded, ~1 week to productive) - Team must maintain two codebases briefly during transition (Laravel in production, Elixir in development) - Smaller Elixir community means fewer Stack Overflow answers and blog posts for edge cases

Risks and mitigations: - Risk: Elixir learning curve stalls velocity → Mitigation: Day 2 learning spike (throwaway Phoenix app) before production code; AI bridges knowledge gaps in real-time - Risk: Missing ecosystem library blocks feature → Mitigation: Core dependencies identified upfront (Oban, Ueberauth, ExAws, Guardian); thin HTTP wrappers for AI APIs - Risk: Flutter app API contract breaks → Mitigation: Define Phoenix API contract early; REST endpoints are framework-agnostic

Tipping points for re-evaluation: AI SDK ecosystem for Elixir fails to materialise within 12 months, or LiveView proves insufficient for the agent-composed UI mode at production scale.