Elixir untuk Technical Leader — Bab 1 dari 10
Bukan "bahasa terbaik di dunia" — tapi bahasa yang paling cocok untuk LabaBersih. Dan kamu harus tau kenapa.
Elixir lahir tahun 2012, dibuat oleh José Valim — core contributor Ruby on Rails. Dia frustrasi: Ruby bagus untuk developer happiness, tapi jelek untuk concurrency (handle banyak hal bersamaan).
José gak bikin dari nol. Dia bangun Elixir di atas BEAM — virtual machine yang sudah proven selama 30+ tahun di industri telekomunikasi (Ericsson, WhatsApp).
| Tahun | Event | Relevansi |
|---|---|---|
| 1986 | Erlang dibuat di Ericsson | Fondasi BEAM — dipakai untuk switch telepon yang gak boleh mati |
| 1998 | Erlang di-open-source | Komunitas mulai tumbuh |
| 2009 | WhatsApp pakai Erlang | 2 juta koneksi per server. Proof bahwa BEAM scale. |
| 2012 | Elixir v0.1 rilis | Syntax modern di atas BEAM |
| 2014 | Phoenix Framework rilis | Web framework — competitor Ruby on Rails |
| 2016 | Phoenix LiveView | Real-time UI tanpa JavaScript — ini yang LabaBersih pakai |
| 2024 | Elixir 1.17+ stable | Mature, production-ready, growing |
BEAM = Bogdan/Björn's Erlang Abstract Machine. Ini virtual machine (kayak JVM untuk Java). Semua code Elixir jalan di atas BEAM.
BEAM bisa jalankan jutaan proses secara bersamaan. Bukan thread OS yang berat (1 thread = ~1MB RAM). BEAM process cuma ~2KB.
# Di LabaBersih, setiap user yang buka browser = 1 LiveView process
# 100 user online = 100 process. Ringan banget.
# Di Next.js v1: 100 user = 100 HTTP connection, masing-masing stateless
Filosofi BEAM: kalau 1 proses error, biarkan crash — supervisor akan restart otomatis. Gak perlu try-catch di mana-mana.
BEAM bisa update code tanpa mematikan server. Ericsson pakai ini untuk switch telepon yang literally gak boleh mati 24/7.
Untuk LabaBersih: saat development, kamu ubah code → browser auto-refresh (LiveView). Gak perlu restart server.
BEAM process bisa komunikasi antar server semudah komunikasi di 1 server. Ini kenapa WhatsApp bisa 2 juta koneksi per server — mereka connect banyak BEAM node.
Untuk LabaBersih sekarang: belum perlu (1 server Fly.io cukup). Tapi kalau scale ke 50.000 order/hari, tinggal tambah node. Arsitektur gak perlu berubah.
Ini bukan soal "Elixir terbaik" — ini soal match antara kebutuhan bisnis dan kemampuan teknologi.
| Kebutuhan LabaBersih | Solusi Elixir | Solusi Lain |
|---|---|---|
| 6.300 order/hari, banyak concurrent user | BEAM processes, ringan, gak butuh scaling infrastruktur | Node.js bisa, tapi butuh load balancer + multiple instances lebih cepat |
| Real-time dashboard (stok, order, laporan) | LiveView — server push ke browser via WebSocket. Built-in. | React + WebSocket = 2 layer terpisah, lebih complex |
| Background jobs (sync API, daily journal, cron) | Oban — persistent job queue, retry, scheduling. 1 dependency. | Redis + Bull/BullMQ + separate worker process |
| Atomic transactions (ship order = 5 langkah) | Ecto + Repo.transaction — kalau 1 gagal, semua rollback | Prisma/Drizzle bisa, tapi pattern-nya kurang natural |
| Pattern matching untuk status transition | Built-in di bahasa. "dikemas" → "dikirim" = 1 baris code. |
If-else chain. Error-prone. |
| Hosting murah ($10-15/bulan) | Fly.io — Phoenix optimized, Singapore region | Vercel + Supabase = $45/bulan (v1 actual cost) |
| 1 bahasa end-to-end | Elixir untuk backend + LiveView untuk frontend = 1 bahasa | TypeScript + React + SQL = 3 bahasa/paradigma |
Supaya kamu paham apa yang berubah secara fundamental:
# v1 Architecture (Next.js + Supabase)
Browser (React/Next.js)
↓ HTTP request
Supabase Edge Functions (31 functions, Deno runtime)
↓ SQL query
PostgreSQL (Supabase, + 16 RPC functions di database)
# Masalah:
# - Logic tersebar di 3 tempat (React, Edge, SQL)
# - Gak ada shared type system
# - Error baru ketemu saat runtime
# - Edge Function stateless = gak bisa share state
# v2 Architecture (Elixir/Phoenix)
Browser (HTML dari LiveView, minimal JS)
↑↓ WebSocket (persistent connection)
Phoenix LiveView + Contexts (semua logic di 1 tempat)
↓ Ecto query
PostgreSQL (Fly.io, zero stored procedures)
# Keuntungan:
# - Logic HANYA di Elixir contexts (1 tempat)
# - Compile-time error checking
# - Persistent WebSocket = real-time tanpa polling
# - Database bersih = hanya data, gak ada logic
Ini yang tadi kamu lihat dari LSP diagnostics. Elixir punya 3 lapis pengecekan sebelum code jalan di production:
Elixir compiler cek setiap kali kamu save file:
# Kamu tulis ini (typo di nama function):
Orders.shiip_order(order_id, email)
# Compiler langsung bilang:
# ** (UndefinedFunctionError) function Orders.shiip_order/2 is undefined
# Did you mean: ship_order/2
# Di v1 (JavaScript): GADA ERROR sampai user klik button dan function dipanggil
Ini yang ElixirLS pakai — analisis tipe data tanpa kamu nulis type annotation:
# Dialyzer detect: "pattern {:ok, _} can never match"
# Artinya: function itu SELALU return {:error, _}
# Code path {:ok, _} = DEAD CODE, gak pernah kepakai
# Ini yang tadi kamu lihat di sync_facebook.ex, sync_scalev.ex, sync_tiktok_ads.ex
# Kalau di v1: gak ada yang detect. Silent fail.
# 133+ automated tests di LabaBersih v2
# v1 punya: 0 (nol) automated tests
# Setiap function penting di-test:
# - ship_order happy path + edge cases
# - jurnal balanced (debit = credit)
# - FEFO lot consumption order
# - Status transition enforcement
Salah satu alasan Elixir terasa "lengkap" dibanding stack lain: hampir semua tools yang kamu butuhkan sudah built-in atau 1 dependency away. Gak perlu rakit sendiri kayak JavaScript ecosystem.
| Tool | Apa | Command | Analogi di Dunia Lain |
|---|---|---|---|
| Mix | Build tool + project manager + task runner. SATU tool untuk semuanya. | mix compile, mix deps.get, mix ecto.migrate |
npm + webpack + Makefile jadi satu |
| IEx | Interactive shell. Bisa connect ke server yang sedang jalan dan jalankan function live. | iex -S mix |
Chrome DevTools console, tapi untuk backend |
| ExUnit | Testing framework. Built-in, zero config. | mix test |
Jest (JS) — tapi gak perlu install |
| Logger | Structured logging. Level: debug, info, warning, error. | Logger.info("Order shipped") |
console.log tapi terstruktur + level |
| Hex | Package manager & registry. | mix hex.search oban |
npm registry |
| Observer | GUI monitor semua BEAM processes, memory, CPU. Bisa lihat real-time. | :observer.start() di IEx |
Chrome DevTools Performance tab |
| Tool | Apa | Command | Analogi |
|---|---|---|---|
| ExDoc | Generate website dokumentasi dari @moduledoc dan @doc di code kamu. |
mix docs → buka doc/index.html |
JSDoc / Swagger — tapi output-nya cantik |
| Credo | Code linter. Cek style, complexity, consistency. | mix credo |
ESLint — tapi tanpa config 50 baris |
| Dialyzer | Type checker. Analisis tipe data tanpa kamu nulis type annotation. Ini yang dipakai ElixirLS (LSP) untuk detect dead code. | mix dialyzer |
TypeScript compiler (tapi optional & inferred) |
| Sobelow | Security scanner. Detect SQL injection, XSS, hardcoded secrets. | mix sobelow |
npm audit + Snyk |
| Phoenix LiveDashboard | Real-time web monitoring. Buka di browser, lihat memory, query time, Oban jobs. | Buka /dev/dashboard di browser |
Vercel Analytics + Datadog — tapi gratis & built-in |
Ini perintah terminal yang bisa kamu jalankan sendiri tanpa Claude:
# ── DEVELOPMENT ──
mix phx.server # Start development server (buka localhost:4000)
mix deps.get # Install dependencies (kayak npm install)
mix compile # Compile code — detect errors SEBELUM jalankan
# ── DATABASE ──
mix ecto.create # Buat database baru
mix ecto.migrate # Jalankan migration (ubah struktur tabel)
mix ecto.rollback # Undo migration terakhir (kalau salah)
mix ecto.reset # Drop + create + migrate (HATI-HATI: hapus semua data!)
mix run priv/repo/seeds.exs # Isi data sample
# ── QUALITY ──
mix test # Jalankan semua test (133+ tests)
mix test --failed # Cuma jalankan test yang gagal terakhir
mix test test/lababersih/orders/orders_test.exs # Test 1 file aja
mix credo # Code quality check
mix sobelow # Security scan
mix dialyzer # Type analysis (lama pertama kali, cepat setelahnya)
# ── DOCS ──
mix docs # Generate HTML docs → buka doc/index.html
mix hex.docs online ecto # Buka docs library di browser
# ── DEPLOYMENT ──
mix phx.gen.release # Generate Dockerfile untuk production
mix release # Build production binary
fly deploy # Deploy ke Fly.io (ini CLI Fly, bukan Mix)
mix test (cek apakah code saya gak rusak), mix compile (cek error), mix phx.server (jalankan app lokal). 3 command itu cukup untuk 90% workflow kamu.
Ini fitur favorit Elixir community. Setiap @moduledoc dan @doc yang ditulis di code otomatis jadi halaman dokumentasi.
defmodule Lababersih.Orders do
@moduledoc """
Order management — pesanan, packing, shipment.
## Fungsi Utama
- `create_order/2` — buat pesanan baru
- `ship_order/2` — kirim pesanan (atomic 5 langkah)
- `delete_order/1` — hapus + reverse stok + void jurnal
"""
@doc """
Ship order — ATOMIC transaction (5 langkah):
1. Validasi status = dikemas
2. Update status → dikirim, set shipped_at
3. Potong stok per item (FEFO+FIFO lot consumption)
4. Generate jurnal penjualan + HPP
5. Insert audit trail
## Contoh
iex> Orders.ship_order("PS2603-00001", "nelly@lababersih.id")
{:ok, %{order: %Order{}, journal: %JournalEntry{}}}
"""
def ship_order(order_id, actor_email) do
# ... implementation
end
end
Jalankan mix docs → semua @doc di atas jadi halaman web yang bisa di-browse, lengkap dengan navigasi, search, dan cross-reference antar module. Ini yang lu lihat di hexdocs.pm untuk library Elixir manapun.
@doc. Kamu bisa cek: jalankan mix docs, buka di browser, baca apakah dokumentasinya masuk akal. Ini review tanpa baca code — baca docs-nya aja.
IEx = Interactive Elixir. Bayangkan Chrome DevTools console, tapi bisa akses seluruh backend.
# Start IEx dengan project LabaBersih loaded:
$ iex -S mix
# Sekarang kamu bisa jalankan function APAPUN:
iex> Lababersih.Orders.get_order!("PS2603-00001")
%Order{id: "PS2603-00001", customer_name: "Budi", status: "dikemas", ...}
iex> Lababersih.Inventory.list_products(org_id)
[%Product{name: "Forbest", stok: 100}, ...]
iex> Lababersih.Accounting.trial_balance(org_id)
%{total_debit: #Decimal<5516585336>, total_credit: #Decimal<5516585336>, balanced: true}
# Bahkan bisa connect ke PRODUCTION server yang sedang jalan!
# (Fly.io: fly ssh console)
Di Elixir, semua data immutable (gak bisa diubah setelah dibuat). Kalau mau ubah, harus bikin salinan baru.
# JavaScript (v1) — mutable, BAHAYA:
order.status = "dikirim" # langsung ubah object asli
# Siapa yang ubah? Kapan? Gak ada record.
# Elixir (v2) — immutable, AMAN:
new_order = %{order | status: "dikirim"}
# order asli GAK BERUBAH. new_order = salinan dengan status baru.
# Kalau ada bug, order asli masih intact.
Kenapa ini penting untuk LabaBersih:
Ini akan dibahas mendalam di Bab 2, tapi preview karena ini yang bikin Elixir sangat readable:
# Status transition di LabaBersih v2 (Elixir)
def advance_order_status("dibuat"), do: {:ok, "diproses"}
def advance_order_status("diproses"), do: {:ok, "selesai"}
def advance_order_status("selesai"), do: {:error, "sudah selesai"}
def advance_order_status(status), do: {:error, "status #{status} gak bisa maju"}
# Baca dari atas ke bawah:
# - "dibuat" → boleh maju ke "diproses"
# - "diproses" → boleh maju ke "selesai"
# - "selesai" → GAK BOLEH maju (sudah final)
# - status lain → GAK BOLEH
#
# Gak ada if-else. Gak ada switch-case. Cuma pattern matching.
# Kamu bisa BACA ini dan langsung paham business rule-nya.
function advanceStatus(status) {
if (status === "dibuat") return "diproses"
if (status === "diproses") return "selesai"
if (status === "selesai") throw new Error("sudah selesai")
throw new Error(`status ${status} gak bisa maju`)
}
def advance("dibuat"), do: {:ok, "diproses"}
def advance("diproses"), do: {:ok, "selesai"}
def advance("selesai"), do: {:error, "sudah final"}
def advance(other), do: {:error, "invalid"}
Perhatikan: Elixir gak pakai throw / exception. Semua error di-return sebagai {:error, reason}. Caller HARUS handle kedua case. Gak ada silent failure.
Elixir bukan bahasa eksperimen. Ini perusahaan yang pakai di production:
| Perusahaan | Scale | Pakai Untuk |
|---|---|---|
| 2 miliar user | Backend messaging (Erlang/BEAM) | |
| Discord | 200 juta user | Real-time messaging, 5 juta concurrent connections |
| 450 juta user | Notification system, rate limiter | |
| PepsiCo | Enterprise | E-commerce platform |
| Heroku | - | Routing layer (Erlang) |
| Brex | Fintech | Core banking system |
| Supabase | Ironic :) | Realtime engine (Elixir) |
| Fly.io | Hosting | Internal tooling + proxy (Elixir) |
Sebagai leader, kamu HARUS tau risikonya. Ini bukan bahasa sempurna.
| Risiko | Level | Mitigasi |
|---|---|---|
| Developer pool kecil Lebih sedikit Elixir dev dibanding JS/Python |
Medium | Kamu pakai AI (Claude) sebagai developer. Dan Elixir dev yang ada biasanya berkualitas tinggi (self-selected community). |
| Library ecosystem lebih kecil Gak sebanyak npm |
Medium | Hex.pm punya 15.000+ package. Untuk kebutuhan LabaBersih: Ecto, Phoenix, Oban, Swoosh, Req — semua mature. Yang niche, bikin sendiri. |
| BEAM hosting terbatas Gak semua hosting support BEAM |
Low | Fly.io, Gigalixir, Render, AWS (Docker), GCP (Docker). Worst case: Docker container jalan di mana aja. |
| Fly.io tiba-tiba tutup | Low | Phoenix release = Docker image standard. Migrate ke provider lain dalam hitungan jam. Gak ada vendor lock-in. |
| BEAM discontinued | Very Low | BEAM di-maintain oleh Ericsson (perusahaan telekomunikasi $25B). Erlang/OTP di-develop aktif sejak 1986. Open source. Kalau Ericsson stop, community fork (sudah terjadi — Erlang open-sourced tahun 1998). |
| José Valim berhenti | Low | Elixir sudah punya core team, Dashbit (perusahaan José) sustainable, dan banyak kontributor. Bahasa sudah mature. |
Setelah baca bab ini, kamu sekarang bisa tanya hal-hal ini ke Claude (atau developer manapun):
| Pertanyaan | Jawaban yang benar | Red flag |
|---|---|---|
| "Kenapa function ini gak di-test?" | "Saya akan bikin test-nya sekarang." | "Gak perlu, ini function kecil." ← TOLAK |
| "Ini jalan di 1 transaction?" | "Ya, pakai Repo.transaction, kalau gagal rollback semua." | "Setiap step independent." ← BAHAYA untuk operasi multi-step |
| "Kalau Fly.io mati, gimana?" | "Docker image standard, bisa deploy di Render/AWS/GCP dalam jam." | "Kita terikat Fly.io." ← SALAH, Phoenix gak vendor-locked |
| "Kenapa gak pakai TypeScript aja?" | "Bisa, tapi untuk case LabaBersih (concurrent, real-time, transactional), Elixir lebih natural. Dan hosting lebih murah." | "Elixir terbaik untuk semua case." ← Gak ada bahasa terbaik universal |
| "Compile error vs runtime error — bedanya?" | "Compile = ketemu sebelum deploy (gratis). Runtime = ketemu saat user klik (mahal)." | Gak bisa jelasin bedanya ← foundational gap |