How to Set Up a Common Lisp IDE in 2026
From Zero to a Green Test Suite
I still remember my first real attempt at setting up Common Lisp. It was a painful dance with SBCL, Quicklisp, manual ASDF configuration, and endless Emacs tweaking, and it ate an entire weekend before a single defun ran. It worked, eventually — but it felt like assembling IKEA furniture with the instructions missing, and I came out the other side wary of recommending the language to anyone who wasn't already stubborn enough to fight for it.
That is not what setting up Common Lisp feels like anymore.
Somewhere along the way the ecosystem quietly finished growing up. There's now a package manager that behaves the way uv or cargo behave — project-local by default, with a lockfile you never have to remember to generate. There's an IDE you can download and double-click, with no Emacs and no keybindings to memorize, that gets a total beginner from nothing to a running expression in about five minutes. And testing, once an afterthought bolted on at the end, now has a clear, mature default that most of the ecosystem has quietly converged on.
This is the complete, start-to-finish guide to all of it. It assumes nothing is already installed on your machine and nothing has been read before it — every command, in order, for every path, so that by the end you have a working Lisp implementation, a package manager, a scaffolded project, an editor wired up for interactive development, a test suite that's gone green (and, once, deliberately red, so you know what that looks like too), and a CI pipeline that keeps it all that way on every push.
Grab a terminal. Work through the parts in order — each one depends on the last — and let's see just how far this has come.
Part 1 — Install a Common Lisp Implementation
Before the first command, one decision shapes everything downstream: do you want Roswell in your toolchain or not? Both are complete, legitimate paths — this isn't "Roswell and then a lesser fallback." Roswell buys you effortless multi-implementation switching and the shortest possible Emacs setup later. Skipping it buys you one fewer moving part and pairs especially well with ocicl (Part 2.4) and mine (Part 4.1), neither of which needs it at all. Every part of this guide flags which instructions are for which path, but here's the no-Roswell thread end to end, if you want to jump straight to it:
The complete no-Roswell path: 1.2 (install SBCL directly) → 2.4 (ocicl, no Roswell required) → 3.3 (ocicl new scaffolding) → 4.1, 4.2.1, 4.3, or 4.4 for your editor (all four work without Roswell) → Part 5 (testing, unaffected either way) → 6.2 (ocicl-based CI). Follow just those sections top to bottom and you'll never install Roswell once.
Now, the implementation itself. Every Lisp setup starts with the same small identity crisis: Common Lisp is a language standard, not a single program, so "installing Lisp" actually means picking one of several competing implementations of that standard first. Think of it like choosing your coffee — espresso, latte, or a capsule machine. Each has its merits, and the right one depends on what you're actually trying to do. The three worth knowing about:
| Implementation | Character | Best for | Version as of mid-2026 |
|---|---|---|---|
| SBCL (Steel Bank Common Lisp) | Espresso — powerful, reliable, the default everyone reaches for | General-purpose programming, production use, "just start here" | 2.6.4 |
| CCL (Clozure CL) | Latte — smooth, extremely fast compile times | Rapid interactive prototyping, tight edit/compile/test loops | rolling release |
| ECL (Embeddable Common Lisp) | Capsule machine — compact, embeddable | Embedding Lisp inside a larger C/C++ application, constrained environments | 26.3.27 |
If you're unsure, pick SBCL — the espresso. It's the safest default: fast, actively maintained, and what almost every tutorial, library, and tool in this guide assumes you have. You can always come back and pull an espresso shot of CCL or ECL later once you know what you're optimizing for.
1.1 The recommended path: Roswell
Roswell (by Sano Masatoshi & colleagues) is the tool that quietly made all of this bearable in the first place. It's a Lisp implementation installer, version manager, and script launcher, all in one — it lets you install multiple implementations and multiple versions of each, and switch between them per-project, without the manual compiling and PATH-wrangling that used to eat that first weekend.
Installing it first will make everything downstream in this guide (Emacs integration in particular) noticeably shorter.
macOS:
brew install roswell
LIBRARY_PATH=$(brew --prefix)/lib CPATH=$(brew --prefix)/include ros install sbclUbuntu / Debian:
curl -L https://github.com/roswell/roswell/releases/latest/download/roswell.deb -o roswell.deb
sudo dpkg -i roswell.deb
ros install sbclIf you'd rather build from source instead of using the .deb:
sudo apt-get -y install git build-essential automake libcurl4-openssl-dev
git clone -b release https://github.com/roswell/roswell.git
cd roswell
sh bootstrap
./configure
make
sudo make install
ros setupWindows: Roswell no longer supports Windows directly. Use the Windows Subsystem for Linux instead:
wsl --installThen open the resulting Ubuntu shell and follow the Ubuntu instructions above.
Check it worked:
ros run -- --versionYou should see something like SBCL 2.6.4. Start a REPL and try it:
ros run* (+ 1 2)
3
* (quit)Tip: wrap it with rlwrap (brew install rlwrap or sudo apt install rlwrap) for proper line-editing (arrow keys, history) in the raw REPL: rlwrap ros run.
Installing other implementations and switching between them:
ros install ccl-bin # install CCL
ros install ecl # install ECL
ros use ccl-bin # make CCL the active default
ros run -- --version # confirm which one is active now
ros list installed # see everything you've installed
ros use sbcl # switch back1.2 The manual path (no Roswell)
If you'd rather not add another tool to the mix, install an implementation straight from your OS package manager or its own installer. This is a perfectly good choice — Roswell is genuinely optional, not a prerequisite for anything except a couple of specific shortcuts this guide will flag as they come up.
SBCL:
# macOS
brew install sbcl
# Ubuntu / Debian
sudo apt install sbcl
# Windows — no WSL required: download the native Windows installer directly
# from https://www.sbcl.org/platform-table.html and run it
# verify, any platform
sbcl --versionCCL, if you specifically want the fast-compile latte instead:
# macOS
brew install clozure-cl
# Ubuntu / Debian
sudo apt install clozure-cl
# verify
ccl --versionECL, if you're embedding Lisp in a C/C++ application:
# macOS
brew install ecl
# Ubuntu / Debian
sudo apt install ecl
# verify
ecl --versionWindows without WSL, more generally:
every implementation above publishes native Windows binaries —
- SBCL's are on sbcl.org,
- ECL ships Windows releases on GitHub.
It works, but in practice it's the path with the roughest edges (fewer tested combinations, more path/encoding quirks), so if you hit friction, falling back to wsl --install and the Ubuntu instructions in 1.1 remains the path of least resistance even if you don't want Roswell once you're inside the WSL shell — you can install SBCL there with plain apt install sbcl too.
Checkpoint: whichever you picked, open a REPL and confirm it responds:
sbcl # or ccl, or ecl* (+ 1 2)
3
* (quit)1.3 One thing worth knowing before you move on
If you installed via Roswell in 1.1, you already have Quicklisp — ros setup bootstraps it silently as part of installing your first implementation. Confirm it:
ros run --eval '(ql:quickload :alexandria)' --eval '(quit)'If alexandria loads without complaint, Quicklisp is live and Part 2.1 below is just a formality for you — a quick pointer to where things live, not a from-scratch install. If you went the manual, no-Roswell route in 1.2 instead, Quicklisp genuinely isn't installed yet, and Part 2.1 is where you'll do that install for real.
Part 2 — Choose a Package Manager
Here's the question I get asked most often by people coming from other languages: "does Lisp have anything like uv yet?" For most of this ecosystem's life the honest answer was no — you got one shared, global pile of packages and hoped for the best. In 2026, for the first time, the answer is a genuine yes, and this section is where we find out why.
A Common Lisp library or project is distributed as one or more ASDF systems (ASDF = Another System Definition Facility, Lisp's build system).
A package manager fetches those systems and their dependencies for you. There are four worth knowing, and they solve overlapping but distinct problems:
- Quicklisp (by Zach Beane) — one shared, global dist of packages, updated a few times a year. The original, still the most-documented, still the safe default for following along with an existing tutorial.
- Ultralisp (by Alexander Artemenko)— not a replacement for Quicklisp, a companion to it: mirrors package sources from GitHub in near-real-time, so if you need a fix that hasn't made it into a Quicklisp release yet, you can pull from Ultralisp instead.
- Qlot (by Eitaro Fukamachi) — layers project-local dependency pinning on top of Quicklisp, similar in spirit to a Gemfile or
package-lock.json. - ocicl (by Anthony Green)— a from-scratch replacement for the whole model: dependencies are project-local by default, distributed as signed OCI artifacts (the same artifact format Docker images use) over plain HTTPS, with an automatic lockfile. If you've used
uv,cargo, or modernnpmand wondered why Lisp didn't have something like that — this is it.
We'll set up all four, since which one you reach for depends on the project.
2.1 Quicklisp — the classic, global installer
If you came from the Roswell path (1.1): you already did this. Roswell's ros setup bootstraps Quicklisp for you the moment you install your first implementation, so there's no separate install step to run — skip straight to the "using it" bullet below.
If you came from the manual, no-Roswell path (1.2): here's the from-scratch bootstrap, step by step:
- Download the installer and verify its signature:
curl -O https://beta.quicklisp.org/quicklisp.lisp
curl -O https://beta.quicklisp.org/quicklisp.lisp.asc
gpg --verify quicklisp.lisp.asc quicklisp.lisp- Load it and install:
sbcl --load quicklisp.lispAt the resulting SBCL prompt:
(quicklisp-quickstart:install)
(ql:add-to-init-file)
(quit)This creates ~/quicklisp/ and adds Quicklisp to your SBCL init file so it loads automatically every time.
3. Verify:
sbcl --eval '(ql:quickload :alexandria)' --quitIf alexandria loads without error, you're set.
Using it, either way:
your own in-development projects are expected to live in ~/quicklisp/local-projects/ — clone or symlink projects there and ql:quickload will find them by system name.
Update installed packages any time with (ql:update-all-dists).
A Roswell-specific shortcut worth knowing: since Roswell's Quicklisp integration is already wired in, you can install any Quicklisp-available library straight from the command line, without opening a REPL at all:
ros install rove # installs the "rove" testing library via
# Quicklisp, plus any bundled ros scriptsThis is genuinely just ql:quickload under the hood, exposed as a shell command — handy for one-off installs, but for real projects (as opposed to individual libraries) you still want one of the project-local managers below.
2.2 Ultralisp — rolling releases alongside Quicklisp
- With Quicklisp already installed, add Ultralisp as an extra dist:
(ql-dist:install-dist "http://dist.ultralisp.org/"
:prompt nil)- From then on,
(ql:quickload "some-system")will pull from Ultralisp automatically whenever it has a newer version than the Quicklisp dist does.
2.3 Qlot — project-local dependencies on top of Quicklisp
- Install it. There are three ways in — pick based on which path you took in Part1:
# If you have Roswell (1.1):
ros install qlot
# If you don't want Roswell at all — Qlot's own standalone installer,
# whose only real dependencies are sbcl and OpenSSL:
curl -L https://qlot.tech/installer | sh
# A third option that works either way, if you already have Quicklisp
# running (Part 2.1) and don't need the separate `qlot` shell command —
# load it as a library and drive it from Lisp directly:
sbcl --eval '(ql:quickload :qlot)'The first two give you a qlot command on your PATH; the third gives you the qlot:install, qlot:add, and qlot:update functions to call from a REPL instead — functionally equivalent, just without the shell wrapper. 2. Inside your project directory:
qlot initThis creates a qlfile. 3. Declare a dependency by editing qlfile:
ql alexandriaYou can also point at Ultralisp for a specific package if you need the rolling version:
ultralisp some-fast-moving-package- Install and lock:
qlot installThis generates qlfile.lock (commit both files) and a project-local .qlot/ directory that doesn't touch your global Quicklisp state.
5. Always run Lisp through Qlot so it sees your locked, project-local dependency set:
qlot exec sbcl2.4 ocicl — the 2026 default for new projects
This is the one that finally answers the uv question. Where Qlot layers project-local pinning on top of Quicklisp's central-dist model, ocicl throws that model out entirely: dependencies are distributed as signed OCI artifacts (yes, the same artifact format Docker images use) over plain HTTPS, they're project-local from the very first command with no opt-in step, and the lockfile writes itself. It's a genuinely different design, not just a nicer wrapper around the old one.
- Install the
ociclbinary:
# macOS
brew install ocicl
# Ubuntu / Debian
curl -LO https://github.com/ocicl/ocicl/releases/latest/download/ocicl.deb
sudo dpkg -i ocicl.deb
# any platform, from source (requires SBCL)
git clone https://github.com/ocicl/ocicl
cd ocicl && make && sudo make install- Tell ASDF to trust the current directory — add this once to
~/.sbclrc:
(asdf:initialize-source-registry
(list :source-registry
(list :directory (uiop:getcwd))
:inherit-configuration))- Inside any project directory, add dependencies directly:
ocicl install alexandria dexadorThis resolves the dependency graph and writes ocicl.csv — your lockfile — right there in the project. Nothing global was touched, and no separate "generate the lockfile" step is needed; it's automatic.
4. Load your project the normal way:
sbcl --eval '(asdf:load-system :my-project)'The ocicl-runtime is a small shim embedded in your Lisp image that finds and loads systems from ocicl.csv automatically.
Checkpoint: run cat ocicl.csv. You should see your dependencies pinned to exact, content-addressed versions. Clear your local cache and re-run ocicl install; you should get byte-identical pins back — that reproducibility is the entire point of the design.
Which one should you actually use?
Quicklisp if you're following a tutorial that assumes it. ocicl for anything you're starting today. Qlot if a team you're joining has already standardized on it. Ultralisp alongside Quicklisp, whenever you need a package fix that hasn't shipped in a dist release yet.
Part 3 — Scaffold a Project and Understand ASDF
It's tempting to skip straight to the editor at this point — that's usually the fun part — but the file underneath everything else is worth five minutes of your patience first, because every tool in the rest of this guide is really just a nicer way of editing and running it. Before touching an editor, let's understand the file that ties a Lisp project together: the .asd system definition.
3.1 Scaffolding by hand, and what each part means
Let's build one real project and carry it through the rest of this guide — every editor, test, and CI example from here on works against this exact my-project.
- Create the project directory and, using ocicl from Part 2.4 (the 2026 default), fetch the two libraries this project will actually use —
alexandria, a general-purpose utility library, andfiveam, our test framework from Part 5:
mkdir my-project && cd my-project
ocicl install alexandria fiveamThat's the step that was missing if you've only ever copy-pasted a .asd file from a tutorial: the packages named in :depends-on below actually have to be fetched first, or ASDF has nothing to find when it goes looking for them. If you're on Quicklisp instead of ocicl, the equivalent is simply making sure (ql:quickload '(:alexandria :fiveam)) succeeds once before you go further — Quicklisp fetches on first load rather than needing a separate install command. 2. Lay out the rest of the structure:
my-project/
├── my-project.asd
├── ocicl.csv (already written by step 1)
├── src/
│ └── main.lisp
└── tests/
└── main.lispmy-project.asd:
lisp
(defsystem "my-project"
:version "0.1.0"
:author "Your Name"
:license "MIT"
:depends-on ("alexandria")
:components ((:module "src"
:components ((:file "main"))))
:description "A demo project"
:in-order-to ((test-op (test-op "my-project/tests"))))
(defsystem "my-project/tests"
:depends-on ("my-project" "fiveam")
:components ((:module "tests"
:components ((:file "main"))))
:description "Test system for my-project"
:perform (test-op (op c) (symbol-call :fiveam :run! :my-project-suite)))A few things worth understanding line by line:
:depends-onlists other systems this one needs — whenever you introduce a new library, add it here first, or ASDF won't find it.:componentslists every file and folder belonging to the system, filenames given without the.lispextension. Every time you add a file, update this list too, or it won't be loaded.- Tests are conventionally their own separate system (
my-project/tests) that depends on the main system plus a test framework — this keeps test-only dependencies out of your production dependency graph.
src/main.lisp:
(defpackage :my-project
(:use :cl)
(:export #:my-absolute))
(in-package :my-project)
(defun my-absolute (x)
(if (< x 0) (* -1 x) x))defpackage declares a namespace. (:use :cl) pulls in the standard library. :export lists which symbols are public — visible to code outside this file. If you only need a couple of specific functions from a dependency rather than everything, prefer:
(defpackage :my-project
(:use #:cl)
(:import-from #:alexandria #:flatten)
(:export #:my-absolute))Checkpoint — load it and prove it works before moving on:
sbcl --eval '(asdf:load-system :my-project)' \
--eval '(print (my-project:my-absolute -3))' \
--quitYou should see 3 printed. This is the exact project every editor and testing example in the rest of this guide will keep building on.
3.2 Scaffolding automatically with cl-project
Writing that structure by hand every time gets old. cl-project (also by Fukamachi) generates it for you:
# with Roswell:
ros install fukamachi/cl-project
# without Roswell — load it as a library instead, no shell command needed:
sbcl --eval '(ql:quickload :cl-project)'(cl-project:make-project #P"~/common-lisp/my-project/"
:depends-on '(:alexandria))This produces the exact .asd/src/tests layout above, pre-wired for the Rove test framework by default (see Part 5 for why you'll likely want to switch its test dependency to FiveAM). Either install method gives you the same cl-project:make-project function — the Roswell version just also drops a cl-project shell script in ~/.roswell/bin as a convenience.
3.3 Scaffolding automatically with ocicl new
If you're using ocicl from Part 2.4, its own scaffolding command is the fastest path and comes with CI already wired up:
ocicl new cli my-project
cd my-projectThis generates the .asd/source layout and a .github/workflows/ directory with CI, release builds (RPM/DEB/tarball/Windows installer), SBOM generation, and GPG-signed package repositories already configured — see Part 6.2 for what that pipeline does.
Part 4 — Choose and Set Up Your Editor
This is the part of the ecosystem that changed the most, and the fastest. For most of Lisp's recent history, "set up an IDE" quietly meant "set up Emacs," full stop — a genuinely powerful combination, but one with a learning curve steep enough that plenty of curious newcomers bounced off it before ever seeing a REPL. That is no longer the only door in. There are four genuinely good paths in 2026, ranging from "download and double-click" to "full GUI-builder IDE." Pick the one that matches who you are today; you can always come back and try another later, since your project's files don't change based on which editor touched them.
4.1 Path A — mine: zero-config, for trying Lisp today
If you'd told me a few years ago that the biggest accessibility jump in Lisp tooling would be "someone finally built a normal-looking app," I'd have believed you but wouldn't have expected it to actually happen. Released April 2026 by the Coalton team, mine is exactly that: a complete, self-contained application. No Emacs, no plugin ecosystem, no keybinding cheat sheet to print out and tape to your monitor — Ctrl+C/Ctrl+V really do copy and paste. It ships a REPL with hot-reload, a full interactive debugger with restarts, inline diagnostics (from hard errors to soft optimization hints), jump-to-definition, autocomplete, and guided, built-in lessons for structural (paren-aware) editing — the skill that normally takes weeks to pick up from Emacs, taught in about five minutes here.
Step by step:
- Go to
https://coalton-lang.github.io/mine/and download:- Windows/macOS → mine-app, a fully self-contained installer, zero dependencies.
- Linux/terminal-first users → mine-core — needs a terminal with a Unicode font and Kitty keyboard-protocol support (kitty, WezTerm, or Ghostty all qualify).
- Run the installer (or
mine --setuponce, for mine-core). - Launch it and press F5 → Project → New Project to scaffold a basic template.
- Open the generated
.lispfile and write:
(defun greet (name)
(format nil "Hello, ~A!" name))- Use the "beam to REPL" key shown in the status bar to send the function to the running image, then type
(greet "World")directly into the REPL panel. - Open the built-in structural-editing tutorial from the help menu.
It's alpha software and noticeably less feature-dense than SLY for power users, but for going from nothing to a running Lisp expression in minutes, nothing else in the ecosystem is faster. It also has first-class support for Coalton, the statically-typed functional language that compiles to Common Lisp, built in.
4.2 Path B — Emacs: the power-user path, two ways to wire it up
Emacs plus a Lisp-interaction mode is still where the ecosystem's deepest tooling lives. There are two competing modes:
- SLIME (Superior Lisp Interaction Mode for Emacs) — the original, actively maintained (version 2.32, released December 2025), deliberately prioritizes stability. Widest base of tutorials and existing answers.
- SLY — a fork of SLIME with a materially better debugger (easier stack-frame navigation), a REPL that can render images and tables inline, "sticker" annotations that show a value's history as code runs, flex-style completion out of the box, and generally snappier performance on large projects. It intentionally left SLIME's Emacs-23 compatibility behind to allow cleaner, more modern Elisp.
4.2.1 Quick setup (recommended), either flavor:
;; ~/.emacs.d/init.el
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(unless (package-installed-p 'sly)
(package-refresh-contents)
(package-install 'sly))
(setq inferior-lisp-program "sbcl") ; or "ros -Q run" if using RoswellSwap 'sly for 'slime throughout if you'd rather use SLIME instead — the shape of the config is identical.
Restart Emacs, run M-x sly (or M-x slime), and wait for a CL-USER> prompt to appear. Open a .lisp file in the same session, place your cursor after a top-level form, and press C-x C-e to send it to the running Lisp process.
Note for no-Roswell readers: 4.2.1 above is already your path — it needs nothing beyond MELPA and a plain sbcl on your PATH. What follows in 4.2.2 is a deeper, more explicit config; it has both a Roswell variant and a Quicklisp-only variant, so it's usable either way.
4.2.2 The deep-config way (no package-install shortcut, full control over heap size and indentation — useful if MELPA is unreachable or you want to see every wire):
- Install Emacs globally and start it once in the background:
sudo apt install emacs
emacs &- Install SLIME's server-side counterpart, Swank — pick based on your Part 1 path:
# With Roswell:
ros install slime
ros install swank
# Without Roswell — load Swank via Quicklisp instead, no separate install step:
sbcl --eval '(ql:quickload :swank)' --quit- Inside Emacs, run
M-x install-package RET slime RETas well, so the Elisp side is present too. - Open
~/.emacs.d/init.el(C-x C-f, type the path,RET) and write:
(require 'package)
(setq package-enable-at-startup nil)
(setq package-archives '())
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(package-refresh-contents)
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
;; connect Emacs to the Lisp implementation:
;; ── if you're on the Roswell path, use its helper so `ros use` switches follow through ──
(load (expand-file-name "~/.roswell/helper.el"))
(setq inferior-lisp-program "ros -Q run")
;; ── if you're on the no-Roswell path, skip the line above and use this instead ──
;; (setq inferior-lisp-program "sbcl")
(setq slime-contribs '(slime-fancy))
(add-to-list 'slime-contribs 'slime-cl-indent) ; correct indentation for `loop` etc.
(setq-default indent-tabs-mode nil) ; don't use tabs
;; give SBCL as much heap as your machine actually has
(defun linux-system-ram-size ()
(string-to-number (shell-command-to-string
"free --mega | awk 'FNR == 2 {print $2}'")))
(setq slime-lisp-implementations
`(("sbcl" ("sbcl" "--dynamic-space-size"
,(number-to-string (linux-system-ram-size))))
("ecl" ("ecl"))
("ccl" ("ccl"))))- Save (
C-x C-s) and restart Emacs (C-x C-c, thenemacs &again). - Create a test file:
C-x C-f, typetest.lisp,RET. Inside it, pressM-x, typeslime,RET. - A new Emacs buffer should open showing:
; SLIME 2.32
CL-USER>Position your cursor at the end of any expression in test.lisp and press C-x C-c to evaluate it in the connected Lisp.
(Key notation reminder if you're new to Emacs: C- means hold Ctrl, M- means hold Alt/Meta, RET is Return. C-x C-f means: hold Ctrl, press x, release both, hold Ctrl, press f, release both.)
Verdict: SLY for anything you're starting today; SLIME if you're joining a team or codebase that already standardized on it; the manual path if you want zero magic or MELPA is blocked on your network.
4.3 Path C — VSCode: Alive vs. the new OLIVE
4.3.1 Alive — the mature, low-dependency option:
- Install VSCode, then install the Alive extension from the Marketplace (
rheller.alive). - Alive's LSP server needs a few Lisp libraries. With Quicklisp installed (Part 2.1), open an SBCL REPL once and run:
(ql:quickload "usocket")
(ql:quickload "flexi-streams")
(ql:quickload "bordeaux-threads")
(ql:quickload "cl-json")- Open a
.lispfile in VSCode. Open the Command Palette (Ctrl+Shift+P) and run Alive: Start REPL and Attach. - A REPL panel appears at the bottom. Place your cursor on a top-level form and run Alive: Send To REPL to evaluate it.
- Alive also supports remote development out of the box — it works the same way over Remote-SSH, Remote-WSL, and GitHub Codespaces if you have the corresponding VSCode extensions installed.
4.3.2 OLIVE — the newer, Swank-based alternative (published June 2026): Alive's REPL starts a new thread for every evaluation, which the Alive maintainers themselves note can get in the way of some interactive workflows. OLIVE instead drives a Swank server — the same protocol SLIME and SLY use in Emacs — which is a more battle-tested transport for Lisp's interactive style.
- Install the OLIVE extension from the Marketplace.
- Start a Swank server from your Lisp process:
(ql:quickload :swank)
(swank:create-server :port 4005 :dont-close t)- In VSCode, run OLIVE: Connect to Swank, giving it port
4005. - Evaluate forms, get completions, and jump to definitions the same way you would in Alive.
Verdict: Alive is the mature default. Reach for OLIVE specifically if Alive's threading model has caused you problems.
4.4 Path D — LispWorks: free, GUI-native, no Emacs required
If what you actually want is a self-contained IDE with a visual GUI builder, LispWorks is free and real.
- Go to
lispworks.com/downloadsand download LispWorks Personal Edition (free, roughly 42–65MB depending on platform). - Run the installer for your OS.
- Launch LispWorks — you get an integrated Listener (REPL) window and an Editor window immediately, no separate attach step.
- Write a function in the Editor and evaluate it via the Lisp menu's "Evaluate Form."
- For GUI work, open Tools → CAPI → Interface Builder and drag controls onto a window — this is the part Emacs/VSCode setups don't give you for free.
Know the Personal Edition's limits going in: a heap-size ceiling (you're warned as you approach it), a five-hour session cap (warned at 4 hours, force-quits at 5, possibly without saving or cleaning up temp files), and save-image, deliver, and load-all-patches are all disabled — so you can't ship a standalone executable and your init files aren't loaded.
If those limits bite, step up to the still-free LispWorks Hobbyist Edition: same installer flow, but no heap or session-time limit, save-image and init files become available. deliver remains reserved for the paid Professional/Enterprise tiers, and Hobbyist is licensed for non-commercial, non-business, non-academic individual use only.
Part 5 — Testing: FiveAM First, Then the Rest of the Field
Testing frameworks are the one corner of this ecosystem that's always been a little chaotic — Lisp has never lacked for options, it's lacked for consensus. Older tutorials would point you at whichever framework was trendy that year and leave you to discover the alternatives the hard way, usually by inheriting a codebase that used a different one. The good news for 2026: the community's default has genuinely settled. It's FiveAM — mature, community-maintained, and the framework most existing test suites you'll encounter are written in. Here's a full hands-on walkthrough, followed by the rest of the field for the days FiveAM isn't the right fit.
5.1 The field, briefly
| Framework | Author / origin | Style | Where it shines |
|---|---|---|---|
| FiveAM | Edward Marco Baringer, community-maintained | test/is macros, fixtures, suites | The de facto default in 2026 |
| Rove | Eitaro Fukamachi | Successor to Prove; ok/ng-style assertions, colorful terminal output, grouped testing blocks | Projects already using it, or if you like RSpec-flavored assertions |
| Parachute | Shinmera (Yukari Hafner) | Extensible; uniquely has compatibility shims to read FiveAM, Prove, and Lisp-Unit test suites | Migrating or combining a mixed-framework test suite without rewriting it |
| lisp-unit2 | AccelerationNet | JUnit/SUnit-style, strong built-in float/rational comparisons | Numeric-heavy code, teams coming from xUnit-style testing elsewhere |
| 1am | lmj | Deliberately minimal, single file, no framework dependency at all | Tiny libraries |
5.2 Step by step: FiveAM, from zero to a passing (and a deliberately failing) test
- Add it to your project. With ocicl (Part 2.4):
ocicl install fiveamWith Quicklisp: (ql:quickload :fiveam). With Qlot: add ql fiveam to your qlfile and run qlot install. 2. Use the my-project/tests system from Part 3.1 — it already depends on fiveam and calls fiveam:run! on :my-project-suite during test-op. 3. Write tests/main.lisp:
(defpackage :my-project/tests
(:use :cl :fiveam :my-project))
(in-package :my-project/tests)
(def-suite :my-project-suite)
(in-suite :my-project-suite)
(test my-absolute-negative
(is (= 3 (my-absolute -3))))
(test my-absolute-positive
(is (= 3.1 (my-absolute 3.1))))
(test my-absolute-zero
(is (zerop (my-absolute 0))))
;; deliberately wrong, so you can see a failure report once
(test my-absolute-deliberately-broken
(is (= 99 (my-absolute 5))))- Run the suite from a plain shell:
sbcl --non-interactive \
--eval '(asdf:load-system :my-project/tests)' \
--eval '(fiveam:run! (quote my-project/tests::my-project-suite))'You should see three green passes and one red failure with a printed diff. Delete the deliberately-broken test once you've seen the report. 5. Or run (asdf:test-system :my-project) directly — this is what the :perform (test-op ...) clause in your .asd file wires up for you. 6. Run tests interactively from SLY/SLIME instead of round-tripping through a shell: with the project loaded in your REPL, just call
(fiveam:run! :my-project-suite)Checkpoint: break a test on purpose, re-run (fiveam:run! :my-project-suite), and confirm FiveAM names the exact failing check plus what it expected vs. what it got.
5.3 The same suite in Rove, for comparison
(defpackage :my-project/tests/main
(:use :cl :my-project :rove))
(in-package :my-project/tests/main)
(deftest my-absolute
(testing "negative input should give positive output"
(ok (= (my-absolute -3) 3))
(ng (eq (my-absolute -3) -3)))
(testing "zero should always give zero"
(ok (zerop (my-absolute 0)))))ok asserts truthiness, ng asserts falseness; group related checks under testing. Rove also supports error/signal checking and macro-expansion checks:
(ok (signals (/ 1 0) 'division-by-zero))
(ok (expands '(my-macro 1 2) '(+ 1 2)))And setup/teardown hooks around a whole test package:
(setup (ensure-directories-exist *tmp-directory*))
(teardown (uiop:delete-directory-tree *tmp-directory* :validate t :if-does-not-exist :ignore))Run it with (rove:run :my-project/tests), or control the reporter style: (rove:run :my-project/tests :style :spec) (other styles: :dot, :none).
5.4 Parachute, for mixed test suites
If you inherit a codebase with FiveAM tests in one library and Rove tests in a dependency, Parachute is the one tool that can run both without a rewrite:
ocicl install parachute(define-test my-absolute
(of-type number (my-absolute -3))
(is = 3 (my-absolute -3)))Run everything — including FiveAM/Rove suites pulled in transitively — with (parachute:test 'my-absolute).
5.5 Emacs integration: Slite
If you're on the Emacs path from 4.2, install Slite for a dashboard-style test runner — green/red badges per test, jump-to-definition on a failure, one-key rerun. It's an ASDF system plus an Emacs package; install both, then run M-x slite from a buffer in your project to open the dashboard.
Part 6 — CI/CD: Run Your Tests on Every Push
There was a time when getting Common Lisp running in a CI pipeline meant piping a shell script off GitHub to bootstrap every implementation by hand, then hand-rolling a forty-line Travis matrix just to get to the point where your actual tests could run. That time is over. Here's the same job, three different ways, from shortest to most legacy.
6.1 GitHub Actions with Roswell + Qlot
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: 40ants/setup-lisp@v4
- run: qlot install
- run: |
qlot exec sbcl --non-interactive \
--eval '(asdf:load-system :my-project/tests)' \
--eval '(unless (fiveam:run! :my-project-suite) (uiop:quit 1))'40ants/setup-lisp installs Roswell and all its dependencies (doing the right thing per OS: Ubuntu, macOS, and Windows are all supported), upgrades ASDF, installs Qlot, adds ~/.roswell/bin and .qlot/bin to PATH, and caches the installed toolchain plus ~/.cache/common-lisp/ between runs — so repeated builds are fast.
6.2 GitHub Actions with ocicl (the shortest path in 2026)
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install ocicl
run: |
curl -LO https://github.com/ocicl/ocicl/releases/latest/download/ocicl.deb
sudo dpkg -i ocicl.deb
- run: ocicl install
- run: |
sbcl --non-interactive \
--eval '(asdf:load-system :my-project/tests)' \
--eval '(unless (fiveam:run! :my-project-suite) (uiop:quit 1))'No Roswell layer, no separate lockfile-generation step — ocicl install reads the manifest already checked into your repo and reproduces exactly the dependency set you tested locally. If you scaffolded with ocicl new back in Part 3.3, this workflow — plus release builds, SBOM generation, and signed package repos — was already generated for you; you likely don't need to hand-write this file at all.
6.3 What about mine?
mine (Part 4.1) is a local development environment, not a CI runner, and isn't meant to run headless in a pipeline. That's fine: code written in mine is an ordinary ASDF system underneath, so 6.1/6.2 apply unchanged regardless of which editor produced the code.
6.4 Legacy option: Travis CI
Some older or migrating projects still run on Travis rather than GitHub Actions. It still works, just with more boilerplate. A minimal setup:
language: common-lisp
sudo: required
install:
- curl -L https://raw.githubusercontent.com/roswell/roswell/release/scripts/install-for-ci.sh | sh
script:
- ros -s fiveam -e '(or (fiveam:run! :my-project-suite) (uiop:quit -1))'Testing across multiple implementations at once with a matrix:
language: common-lisp
sudo: false
env:
global:
- PATH=~/.roswell/bin:$PATH
- ROSWELL_INSTALL_DIR=$HOME/.roswell
matrix:
- LISP=sbcl-bin
- LISP=ccl-bin
install:
- curl -L https://raw.githubusercontent.com/roswell/roswell/release/scripts/install-for-ci.sh | sh
- ros install fiveam
script:
- ros -s fiveam -e '(or (fiveam:run! :my-project-suite) (uiop:quit -1))'Sending coverage to Coveralls:
env:
matrix:
- LISP=sbcl COVERALLS=true
install:
- ros install fukamachi/cl-coveralls
script:
- ros -s fiveam
-s cl-coveralls
-e '(or (coveralls:with-coveralls (:exclude (list "t"))
(fiveam:run! :my-project-suite))
(uiop:quit -1))'Given the choice, prefer 6.1 or 6.2 for a new project — Travis is included here only so a migration from an older setup has a direct reference.
Part 7 — Decision Matrix
If you've read this far and your head is spinning slightly from how many good options now exist, that's fair — it's a nicer problem to have than the one this ecosystem used to hand you. Here's the whole guide compressed into one table, so you can find your row and go build something.
| You are... | Engine | Package manager | Editor | Test framework | CI |
|---|---|---|---|---|---|
| Brand new to Lisp, trying it today | SBCL | Quicklisp | mine | FiveAM, once you have a real function to test | — |
| Starting a fresh serious project | SBCL | ocicl | SLY or Alive | FiveAM | ocicl + GitHub Actions |
| Joining a Quicklisp/SLIME codebase | SBCL | Quicklisp/Qlot (match the team) | SLIME | whatever the repo already uses | 40ants/setup-lisp |
| Inheriting a mixed FiveAM+Rove test suite | SBCL | ocicl or Qlot | SLY | Parachute (reads both) | either GitHub Actions path |
| Want a native GUI builder, no Emacs | SBCL | ocicl or Quicklisp | LispWorks | FiveAM | ocicl |
| VSCode loyalist hitting Alive's threading limits | SBCL | ocicl | OLIVE | FiveAM | ocicl |
| Embedding Lisp in a C/C++ app | ECL | ocicl | any | FiveAM | ocicl |
| Fast iterative prototyping loop | CCL | Quicklisp or ocicl | SLY | FiveAM | ocicl |
Closing
That's the whole path, start to finish: an implementation, a package manager that finally behaves the way modern tooling elsewhere does, a project scaffold you understand rather than just copy-paste, an editor that fits how you actually like to work, a test suite that goes green and shows you exactly what red looks like, and a CI pipeline that keeps it that way on every push.
I opened this guide talking about a lost weekend. If you've followed it from Part 1, you probably just did the same thing in an afternoon — and you came out the other side with a codebase you understand, not just one that happens to run. That's the real difference five years made.
Pick your row from the table above, work the steps in order, and let's Lisp together.