Why I Actually Use Gleam (And You Might Too)

BEAM Architecture

Gleam exists because Erlang syntax looks like it was designed to torture developers. Louis Pilfold, Gleam's creator, got tired of writing {ok, Value} = some_function() for every damn function call. Gleam 1.0 dropped in March 2024 - I've been using it since version 0.8 and it's finally stable enough that I won't get fired for suggesting it.

What Gleam Actually Fixes

Three weeks ago my Node.js API crashed in production because someone passed null to a function expecting a string. No type checking, no warning, just a silent failure that took down order processing for 2 hours. Gleam won't let you deploy code with that kind of stupidity.

When Gleam's compiler yells at you, it actually tells you what's wrong. Last week I had a type mismatch error - instead of getting cryptic bullshit like "error TS2322" it showed me exactly what types were expected vs what I provided. Fixed in 30 seconds instead of searching through 47 Stack Overflow answers.

The BEAM Runtime Advantage

BEAM was built for telecom companies that needed 99.9% uptime or people died. WhatsApp scaled to 900M users with 50 engineers because BEAM doesn't fall over when you get traffic spikes. Meanwhile I've seen Java APIs crash from 100 concurrent connections because someone fucked up the thread pool configuration.

Here's why BEAM doesn't suck:

  • Actor model processes that use 2KB of memory each (not Java threads that start at 8MB and grow)
  • Supervisor trees restart crashed processes without killing your whole app (learned this during a production incident where only one feature broke instead of everything)
  • Hot code deployment that I've actually used in production without downtime (still feels like black magic)
  • Built-in clustering - servers just know about each other without Consul or etcd bullshit

My last Node.js project started dropping connections at 1,000 concurrent users. Same logic in Elixir handled 50,000 users on the same hardware. BEAM is legitimately different.

Why Types Don't Suck Here

Rust's borrow checker makes me want to throw my laptop out the window. Haskell's type system needs a PhD to understand. Gleam just prevents the dumb bugs without making me hate my life.

When you screw up, Gleam shows you exactly where and how to fix it. No Googling "how to fix lifetime annotation in async function" for 3 hours.

Compile to Whatever You Want

Your Gleam code compiles to either:

  • Erlang: All the BEAM runtime benefits, OTP supervision trees, the works
  • JavaScript: Runs anywhere JS runs - browsers, Node, Deno, edge functions

I've used the same validation logic in both my API (compiled to Erlang) and frontend (compiled to JS). No rewriting, no "frontend validation is different from backend validation" bullshit.

Library Ecosystem Reality

Gleam's package ecosystem is tiny. But here's the thing - it can use any Erlang or Elixir library through FFI. Phoenix web framework? Works. Postgrex database driver? Works. That Redis library your entire architecture depends on? Probably works.

I've wrapped Erlang crypto functions, used Elixir HTTP clients, and called into weird telecom libraries from the 1990s. If it runs on BEAM, Gleam can use it. Check the awesome-gleam list but don't expect to find everything you need yet.

The real question isn't whether Gleam is worth learning - it's whether you're tired of debugging the same stupid runtime errors over and over. If you're ready to let the compiler catch your bugs before they hit production, let's get this thing installed.

Questions I Get Asked (And My Honest Answers)

Q

Why does my Gleam installation randomly break every time I update macOS?

A

Homebrew fucks up Erlang paths when macOS updates system libraries. I've had to reinstall Erlang 3 times this year because macOS Sonoma 14.3.1 decided my OTP 26.2 installation was "incompatible" with new system tools. The exact error is dyld: Library not loaded: /opt/homebrew/lib/libcrypto.3.dylib. Run brew reinstall erlang gleam and pray to the BEAM gods.

Q

Can I use this at work or will my team think I'm an idiot?

A

Depends on your team. If they're already using Elixir or Erlang, they'll probably be interested. If they're Java shops that think anything functional is "academic bullshit," you're gonna have a bad time. I got Gleam approved at my current job by showing them WhatsApp's scaling numbers with Erlang.

Q

Is Louis Pilfold the only person who actually understands this language?

A

Pretty much, yeah. The core team is like 3 people and Louis writes most of the compiler. But he's really responsive on Git

Hub issues and Discord

  • I had a bug report fixed in 2 days last month, which is faster than the TypeScript team has ever responded to anything I've filed.
Q

Will my Gleam code break when version 1.1 comes out?

A

Probably not. Since hitting 1.0 in March 2024, breaking changes require a major version bump. The language is now stable at 1.12+ and I haven't had any major breaks in production. Still pin your versions though

  • dependencies can update.
Q

What's the deal with Erlang version conflicts?

A

Installation hell, basically. If you have multiple Erlang installations from different sources (Homebrew, kerl, system packages), Gleam gets confused about which one to use. Last month I had OTP 24, 25, AND 26 installed simultaneously and Gleam kept picking the broken system OTP 24 from 2021. Error message was just {"init terminating in do_boot",noproc}. Took 4 hours to figure out which erl binary was in PATH first. Delete everything and reinstall from one source.

Q

Does pattern matching actually make code better or is it just fancy?

A

It's legitimately better once you stop trying to write if/else chains. Took me about 2 weeks to stop thinking imperatively. Now when I go back to JavaScript I miss having the compiler force me to handle all cases. No more unexpected token runtime errors.

Q

Why does the BEAM eat so much memory on startup?

A

That's just how it works. BEAM preallocates memory pools for garbage collection and process scheduling. My Gleam apps use 50MB at startup before handling any requests. It's not a memory leak

  • it's designed for long-running processes, not Lambda functions.
Q

Can I deploy Gleam to Vercel/Netlify/wherever I deploy my React apps?

A

Only if you compile to JavaScript. The Erlang target needs a proper server that stays running. I deploy BEAM apps to Fly.io or Railway because they actually support long-running processes. Serverless platforms work fine for the JavaScript compilation target though.

Installing Gleam (Prepare for the Usual Bullshit)

Package Manager

Installation works 80% of the time. The other 20% will make you question why you didn't stick with Python. Here's what actually happens and why computers hate us.

macOS Installation

Homebrew (works until it doesn't):

brew install gleam

This works great until macOS updates and breaks everything. I've reinstalled Gleam 5 times this year because Sonoma decided to randomly declare my Erlang build "incompatible" with system libraries. Apple broke something in 14.3.1 that fucked up BEAM apps for weeks.

MacPorts if you hate yourself:

sudo port install gleam

Takes 45 minutes to compile everything from source. But hey, at least it doesn't randomly break when Apple releases point updates. Used this during the macOS Ventura fiasco when Homebrew was fucked for 3 weeks.

Linux Installation

Every distro except Ubuntu: Use Homebrew because native package managers are a nightmare:

## Install Homebrew first (yes, on Linux, shut up)
/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"
brew install gleam

Arch Linux (because of course you use Arch):

yay -S gleam

The AUR maintainer randomly vanished for 2 months in 2024. Gleam 0.32 was stuck for 6 weeks because the PKGBUILD hadn't been touched since 2023. I ended up maintaining my own fork just to get security updates.

Alpine/Docker (actually works):

apk add gleam

Fedora (COPR roulette):

sudo dnf copr enable frostyx/gleam
sudo dnf install gleam

COPR repos are maintained by volunteers who have real jobs. The current repo hasn't been updated since Gleam 1.8 and the maintainer's last commit was 6 months ago.

Ubuntu: Just don't. The official repos have Gleam 0.18 from 2021. Use Homebrew or compile from source like a masochist.

Windows Installation

Scoop (least terrible option):

scoop install gleam

If you don't have Scoop, install it first. It's the only Windows package manager that doesn't fuck up your PATH every other Tuesday.

Manual installation: Download from GitHub releases and prepare for PATH hell. You'll add the binary, restart your terminal 3 times, reboot Windows twice, and it'll still claim gleam doesn't exist. I spent 4 hours last month debugging this exact issue for a coworker.

Verification

gleam --version
erl -version  # Verify Erlang is working

Expected output:

gleam 1.12.0
Erlang (SMP,ASYNC_THREADS) (BEAM) emulator version 26.2

Common issues that will ruin your day:

  • gleam: command not found - PATH is fucked, shell cache is stale, or the binary landed in /usr/local/bin instead of /opt/homebrew/bin
  • Erlang version 25.3 expected, found 24.1 - You've got Homebrew Erlang, system Erlang, AND kerl Erlang all fighting for control
  • Command exists in PATH but shell can't find it - Classic Windows. Restart PowerShell, restart Explorer, sacrifice a rubber duck, reboot twice

Editor Setup

VS Code Logo

VS Code:

  1. Install the Gleam extension
  2. Enable format-on-save in settings
  3. The language server connects automatically. If it doesn't, restart VS Code.

Other editors:

Language server support provides decent error highlighting and autocompletion.

Let's Actually Build Something

Create a project:

gleam new my_first_project
cd my_first_project

This spits out a basic project structure. It's clean, minimal, and doesn't come with 47 configuration files like some frameworks I could mention:

my_first_project/
├── gleam.toml          # Project config (not 500 lines of XML)
├── manifest.toml       # Lock file (like package-lock.json but sane)
├── README.md           # Obligatory README
├── src/
│   └── my_first_project.gleam  # Your code goes here
└── test/
    └── my_first_project_test.gleam  # Tests (you'll write them, right?)

Run it:

gleam run

If this fails with cryptic Erlang errors, you're in the 10% where installation has issues.

What You Actually Got

gleam.toml - Clean configuration:

name = \"my_first_project\"
version = \"1.0.0\"

[dependencies]
gleam_stdlib = \">= 0.38.0 and < 2.0.0\"  # Standard library, always needed

Simple and clear compared to Maven XML or complex package.json files.

No complex configuration options. No plugins. No build phases. Just dependencies and metadata.

src/my_first_project.gleam - Your actual program:

import gleam/io

pub fn main() {
  io.println(\"Hello from my_first_project!\")
}

Simple. No classes, no public static void main, no framework initialization. Just a function that prints output.

Run gleam run and if everything worked, you'll see your message. If it didn't work, you're in the 10% of installations with issues.

If gleam run fails with cryptic BEAM errors, your Erlang installation is completely fucked. Run erl -version first - if that crashes, you're in dependency hell. Last month I had 3 different Erlang versions from Homebrew, system packages, AND kerl, and Gleam kept picking OTP 23 from 2020 for some reason. Took me 6 hours to trace down which symlink was pointing where. Deleted everything and started over with just Homebrew.

Gleam vs Everything Else (My Brutally Honest Take)

Language

My Brutally Honest Take

JavaScript

JavaScript is garbage for anything serious. Sure, it runs everywhere, but "everything is an object except when it isn't" gets old fast. I spent 3 years fixing runtime type errors that Gleam catches at compile time. NPM dependency hell is a special kind of suffering.

Go

Go is boring as hell but at least it works. You can read Go code 6 months later and understand what the fuck it does. But error handling is verbose garbage and the lack of generics (before 1.18) made everything painful. Still better than most alternatives.

Rust

Rust is amazing if you enjoy fighting the compiler more than solving actual problems. The borrow checker is theoretically brilliant but practically exhausting. I spent more time fixing lifetime annotations than writing business logic. Use it for systems programming where performance matters. Skip it for web APIs.

Python

Python is fine for scripts and data science. Anything bigger becomes a maintenance nightmare without types. I've debugged production Django apps where nobody knows what data structure any function expects. GIL kills any hope of real concurrency.

Elixir

Elixir is what I used before Gleam. BEAM is fantastic, but dynamic typing means you find stupid bugs at runtime. Pattern matching is great until you typo a field name and it fails silently. Gleam fixes this.

Building Something That Actually Works (Mostly)

Terminal CLI

Forget "Hello World" - let's build something that doesn't make you want to delete your code. We'll make a CLI tool for reading environment variables because I'm tired of debugging shell scripts that break when $HOME is empty.

Adding Dependencies (The Non-Nightmare Version)

gleam add argv envoy

Unlike npm, this doesn't download 500MB of dependencies to print "Hello World". Your `gleam.toml` gets two lines:

[dependencies]
gleam_stdlib = ">= 0.38.0 and < 2.0.0"
argv = ">= 1.0.0 and < 2.0.0"      # Command line args
envoy = ">= 1.0.0 and < 2.0.0"     # Environment variables

Version ranges actually work here. I haven't had a dependency break my build in 6 months, which is a personal record coming from JavaScript land.

The Code (That Won't Make You Cry)

Replace the hello world template with something actually useful:

import argv
import envoy
import gleam/io
import gleam/result
import gleam/list

pub fn main() {
  case argv.load().arguments {
    ["get", name] -> get_environment_variable(name)
    ["list"] -> list_common_variables()
    _ -> show_usage()  // For invalid commands
  }
}

fn get_environment_variable(name: String) -> Nil {
  let value = envoy.get(name) 
    |> result.unwrap("Environment variable not set")
  
  io.println(format_variable(name, value))
}

fn list_common_variables() -> Nil {
  let common_vars = ["PATH", "HOME", "USER", "SHELL"]
  
  common_vars
  |> list.each(fn(var) { get_environment_variable(var) })
}

fn show_usage() -> Nil {
  io.println("Usage:")
  io.println("  gleam run -- get <VARIABLE_NAME>")
  io.println("  gleam run -- list")
  io.println("")
  io.println("Examples:")
  io.println("  gleam run -- get PATH")
  io.println("  gleam run -- list")
}

fn format_variable(name: String, value: String) -> String {
  name <> "=" <> value
}

What's actually happening here:

Testing This Thing

Get a specific variable:

gleam run -- get PATH

List common variables:

gleam run -- list

Trigger the help text (because you forgot the syntax already):

gleam run

If it works, great. If it crashes with badmatch errors, welcome to Erlang's completely useless error messages. {badmatch,{error,enoent}} means "file not found" but you'll spend 30 minutes on Stack Overflow figuring that out. BEAM error messages were designed by sadists.

What Makes This Code Not Suck

Pattern Matching (The Good Kind)

Look at this beauty:

case argv.load().arguments {
  ["get", name] -> get_environment_variable(name)  // Exact match
  ["list"] -> list_common_variables()              // Simple match  
  _ -> show_usage()                                 // Everything else
}

No giant switch statements full of null checks. The compiler forces you to handle all cases, which annoyed me for the first week until I realized it prevents the stupid bugs that crash in production.

Pipe Operator (Finally, Readable Code)

Instead of this nested nightmare:

result.unwrap(envoy.get(name), "Not set")

You get this left-to-right flow:

envoy.get(name) |> result.unwrap("Not set")

Your brain reads it like: "Get the environment variable, then unwrap it with a default."

No Exceptions (Finally)

Gleam doesn't have exceptions. Environment variable doesn't exist? You get a Result type:

// envoy.get() returns Result(String, Nil) - it tells you it might fail
let value = envoy.get(name) |> result.unwrap("Environment variable not set")

No more try/catch blocks wrapping every function call. No more Cannot read property 'foo' of undefined crashing user checkout flows at 2am on Black Friday. The type system forces you to handle failures explicitly, which feels annoying for about 2 weeks until you realize it prevents 90% of the production bugs that used to ruin my weekends.

What's Next (The Stuff You'll Actually Need)

Tests (You Know, Those Things You Should Write)

Put this in test/my_first_project_test.gleam:

import gleeunit
import gleeunit/should
import my_first_project

pub fn main() {
  gleeunit.main()
}

pub fn format_variable_test() {
  my_first_project.format_variable("TEST", "value")
  |> should.equal("TEST=value")
}

Run them:

gleam test

The test runner actually works and gives you useful output. Shocking, I know.

Building for Production (The Moment of Truth)

gleam build

This compiles to BEAM bytecode. No giant binaries, no weird runtime dependencies, just efficient code that runs on the battle-tested Erlang VM.

Honestly? After spending 3 years debugging Node.js memory leaks and random Java OutOfMemoryErrors at 3am, Gleam feels like cheating. BEAM apps just fucking stay up. The type system catches the dumb bugs before they hit prod, and when something does break, it fails fast instead of limping along corrupting user data for 6 hours before anyone notices.

I haven't been woken up by production alerts in 8 months. That's a personal record since I started writing server code.

You're now armed with enough Gleam to be dangerous. But if you want to go deeper, you'll need to know where to find help when things inevitably get weird.

Where to Go From Here

Related Tools & Recommendations

compare
Recommended

Python vs JavaScript vs Go vs Rust - Production Reality Check

What Actually Happens When You Ship Code With These Languages

rust
/compare/python-javascript-go-rust/production-reality-check
100%
compare
Recommended

MetaMask vs Coinbase Wallet vs Trust Wallet vs Ledger Live - Which Won't Screw You Over?

I've Lost Money With 3 of These 4 Wallets - Here's What I Learned

MetaMask
/compare/metamask/coinbase-wallet/trust-wallet/ledger-live/security-architecture-comparison
65%
tool
Recommended

Rust - Systems Programming for People Who Got Tired of Debugging Segfaults at 3AM

Memory safety without garbage collection, but prepare for the compiler to reject your shit until you learn to think like a computer

Rust
/tool/rust/overview
65%
review
Recommended

I Got Sick of Editor Wars Without Data, So I Tested the Shit Out of Zed vs VS Code vs Cursor

30 Days of Actually Using These Things - Here's What Actually Matters

Zed
/review/zed-vs-vscode-vs-cursor/performance-benchmark-review
49%
news
Recommended

VS Code 1.103 Finally Fixes the MCP Server Restart Hell

Microsoft just solved one of the most annoying problems in AI-powered development - manually restarting MCP servers every damn time

Technology News Aggregation
/news/2025-08-26/vscode-mcp-auto-start
49%
tool
Recommended

Alpaca Trading API Production Deployment Guide

competes with Alpaca Trading API

Alpaca Trading API
/tool/alpaca-trading-api/production-deployment
47%
integration
Recommended

Get Alpaca Market Data Without the Connection Constantly Dying on You

WebSocket Streaming That Actually Works: Stop Polling APIs Like It's 2005

Alpaca Trading API
/integration/alpaca-trading-api-python/realtime-streaming-integration
47%
tool
Recommended

Alpaca-py - Python Stock Trading That Doesn't Suck

competes with Alpaca-py SDK

Alpaca-py SDK
/tool/alpaca-py/overview
47%
tool
Recommended

Helm - Because Managing 47 YAML Files Will Drive You Insane

Package manager for Kubernetes that saves you from copy-pasting deployment configs like a savage. Helm charts beat maintaining separate YAML files for every dam

Helm
/tool/helm/overview
47%
tool
Recommended

Fix Helm When It Inevitably Breaks - Debug Guide

The commands, tools, and nuclear options for when your Helm deployment is fucked and you need to debug template errors at 3am.

Helm
/tool/helm/troubleshooting-guide
47%
tool
Recommended

Node.js ESM Migration - Stop Writing 2018 Code Like It's Still Cool

How to migrate from CommonJS to ESM without your production apps shitting the bed

Node.js
/tool/node.js/modern-javascript-migration
47%
integration
Recommended

Stop Your APIs From Breaking Every Time You Touch The Database

Prisma + tRPC + TypeScript: No More "It Works In Dev" Surprises

Prisma
/integration/prisma-trpc-typescript/full-stack-architecture
44%
tool
Recommended

TypeScript - JavaScript That Catches Your Bugs

Microsoft's type system that catches bugs before they hit production

TypeScript
/tool/typescript/overview
44%
tool
Recommended

JavaScript to TypeScript Migration - Practical Troubleshooting Guide

This guide covers the shit that actually breaks during migration

TypeScript
/tool/typescript/migration-troubleshooting-guide
44%
news
Popular choice

Morgan Stanley Open Sources Calm: Because Drawing Architecture Diagrams 47 Times Gets Old

Wall Street Bank Finally Releases Tool That Actually Solves Real Developer Problems

GitHub Copilot
/news/2025-08-22/meta-ai-hiring-freeze
44%
tool
Recommended

GitHub Actions Security Hardening - Prevent Supply Chain Attacks

integrates with GitHub Actions

GitHub Actions
/tool/github-actions/security-hardening
42%
alternatives
Recommended

Tired of GitHub Actions Eating Your Budget? Here's Where Teams Are Actually Going

integrates with GitHub Actions

GitHub Actions
/alternatives/github-actions/migration-ready-alternatives
42%
tool
Recommended

GitHub Actions - CI/CD That Actually Lives Inside GitHub

integrates with GitHub Actions

GitHub Actions
/tool/github-actions/overview
42%
tool
Popular choice

Python 3.13 - You Can Finally Disable the GIL (But Probably Shouldn't)

After 20 years of asking, we got GIL removal. Your code will run slower unless you're doing very specific parallel math.

Python 3.13
/tool/python-3.13/overview
42%
news
Popular choice

Anthropic Raises $13B at $183B Valuation: AI Bubble Peak or Actual Revenue?

Another AI funding round that makes no sense - $183 billion for a chatbot company that burns through investor money faster than AWS bills in a misconfigured k8s

/news/2025-09-02/anthropic-funding-surge
39%

Recommendations combine user behavior, content similarity, research intelligence, and SEO optimization