Lisp family standards (Common Lisp and Scheme)
Criteria verified as of August 2026. Re-verify on the web before committing to anything (§8).
1. Scope and triggers
Common Lisp is alive, but in specific niches and with a small ecosystem. It is not a
dead technology nor an exercise in nostalgia: there are implementations with monthly releases (SBCL
published 2.6.7 on 28 Jul 2026), there are commercial vendors charging for support and there are systems in
production. What there is not is critical mass: the library surface, the depth of the
tooling and the job market are orders of magnitude smaller than those of any
mainstream language, and that is a project risk, not an aesthetic opinion.
Choose it on criteria, not on taste. Where it genuinely wins: long-lived systems where the
domain is better modelled as its own language (macros, DSLs, compilers, rule and
planning engines, CAD/CAM, symbolic systems), interactive exploration over live state (a REPL
attached to a connected image is a real advantage, not a preference), and teams that already know it.
Where it does not win: CRUD, infrastructure glue, and anything that will rotate between many hands.
Before the first line: are there ≥3 people able to operate and modify it? If not, see §7.
Covers: ANSI Common Lisp and its implementations (free and commercial, with their cost); ASDF and
library distribution (Quicklisp and its integrity model, which is the ecosystem's security fact
— §5); the image, save-lisp-and-die and why that breaks modern CI; the REPL as a
method; the condition and restart system versus exceptions; macros and when not to write
one; CLOS and the MOP. Also Scheme (R7RS, Racket, Guile, Chez) and Emacs Lisp as a separate case.
Not applicable: see clojure-standards (Clojure and ClojureScript are a Lisp and have their own
skill: cede them entirely — deps.edn, project.clj, .clj/.cljs/.cljc/.edn, the JVM, clj-kondo,
the nREPL, immutability by default and its state model. The boundary is not "it is a
Lisp": Clojure has no condition system, no CLOS, no image, no read-eval over mutable
data, and its dependency management is Maven's; nothing is extrapolated from there to here),
haskell-fp-standards and ocaml-fsharp-standards (typed functional programming: the boundary is the
static type system and evaluation, not the paradigm), scala-standards, julia-standards
(another homoiconic language with macros and REPL-driven development, aimed at numerical computing: if the
problem is numerical, it is theirs), elixir-erlang-standards (macros and Elixir's defmacro live
there), python-standards/go-standards/typescript-standards (the real alternative when §7 says
Lisp is not the answer), developer-workstation-standards (configuring Emacs as an editor is
theirs; here only Emacs Lisp as a language), refactoring-tech-debt-standards and
enterprise-architecture-standards (modernisation and portfolio strategy),
legacy-modernization-standards (the umbrella skill for an inherited Lisp system: which "R" is
chosen, whether it is frozen, rewritten or retired, and the archaeology beforehand) and
migration-projects-standards (executing the cutover once decided: rehearsal, window,
data reconciliation, rollback and switching off the source), opensource-licensing-standards (licence analysis; here only which licence each
implementation carries and what it implies, §2), cicd-standards (the pipeline; here the specific problem of
building from an image), appsec-standards and vulnerability-management-standards (methodology
and triage; here the concrete sinks and Quicklisp's integrity model), webassembly-standards
(if the target is Wasm), solidity-standards, mumps-standards and ibm-i-rpg-standards (they are not
comparable: those are legacy platforms with no choice; Common Lisp is still a possible
choice and it has to be defended).
2. Default decisions / Toolchain
Verify the latest version on the web before pinning it in a real project (§8).
| Decision |
Choice |
Verified note (Aug 2026) |
| Default CL implementation |
SBCL |
2.6.7, 28 Jul 2026; roughly monthly cadence. A native compiler, the best performance and the best community support. It is the default unless there is a written reason |
| SBCL's licence |
Public domain + partial BSD/MIT — read raw from COPYING |
"SBCL is derived from CMU CL, which was released into the public domain"; later changes go to the public domain "or under the FreeBSD licence where not", with MIT on LOOP/PCL/CityHash. It is not "MIT" and not "GPL": do not assume |
| CL on the JVM |
ABCL, only if the requirement is Java interop |
Licence verified raw (COPYING): GPL v2 with a Classpath-style exception (13th term) — not MIT and not BSD; it affects how you distribute. Gap: I could not verify its current version (§8) |
| Embedded CL / C++ |
ECL (embeddable, compiles to C) or CLASP (on LLVM, C++ interop) |
ECL: tag 26.5.5 (5 May 2026) on GitLab, not GitHub. CLASP: v3.0.1 (Jun 2026). Both active |
| CCL (Clozure) |
Maintenance only: not for new projects |
Latest release 1.13 (2024); no releases in 2025-2026. It is not dead, but its cadence does not sustain a new choice |
| Commercial implementation |
LispWorks or Allegro CL, only with a requirement that justifies them (cross-platform GUI, mobile delivery, contractual support, closed deliverables) |
LispWorks publishes prices: 8.1 Professional USD 1,500 (32-bit) / USD 3,000 (64-bit), Enterprise USD 4,500, per user, with no runtime royalties. Allegro CL does not publish rates: it is quote-only ("contact … for a customized price quote"). The licence cost is the expensive fact, not the syntax |
| System definition |
ASDF (.asd), no alternative |
Latest stable 3.3.7 (Jan 2024). It lives on gitlab.common-lisp.net; the fare/asdf GitHub repository has been frozen since 2018 and is not the source of truth |
| Library distribution |
Quicklisp as the base + Qlot (qlfile.lock) or ocicl (ocicl.csv) to pin versions |
A versioned lockfile is mandatory. Bare ql:quickload is not reproducible |
| Modern alternative |
ocicl: OCI artifacts, HTTPS, sigstore signatures, per-project local installation |
MIT licence, read raw from LICENSE. It is the option with the best supply-chain story (§5) |
| Rolling dist |
Ultralisp: forbidden in production |
Rolling with no curation: it widens the supply surface with no operational compensation |
| Editor / REPL |
SLIME (swank) or Sly (slynk) on Emacs; alternatives for VS Code/Vim |
The REPL attached to the image is the working method, with the discipline in §3 |
| Tests |
FiveAM or Parachute |
Runnable from the CLI in a clean process, not only from the REPL (§4) |
| Scheme: applications |
Racket |
v9.2 (26 May 2026). It is the Scheme with an ecosystem, documentation and tooling of its own (raco, #lang); if in doubt between Schemes, this one |
| Scheme: embedded / extension |
Guile (GNU, LGPL — verify) |
Latest verified stable series 3.0.11. The forced choice if you extend GNU software |
| Scheme: performance |
Chez Scheme |
10.4.1 (May 2026), in cisco/ChezScheme. It is also Racket CS's backend |
| Scheme standard |
R7RS-small as the portability target |
R7RS-large is NOT closed: it advances through colour dockets (Red 2016, Tangerine 2019) and there is no single ratification event. Its development moved to Codeberg. Do not write "R7RS-large compliant" in any document |
| Emacs Lisp |
A separate case: an editor's extension language, not an application language |
You do not write an application in Elisp. Editor configuration belongs to developer-workstation-standards |
3. Structure and conventions
- One ASDF system per deployable unit, with an explicit
:depends-on and no circular
dependencies between systems. Tests go in a separate system (foo/tests), never hanging off the
main system: otherwise the production binary drags the test framework along.
- Explicit packages (
defpackage), with :export as the contract. No :use of other people's
packages beyond :cl — importing symbols wholesale causes collisions that only appear when
updating a dependency. uiop is referenced with a prefix.
- Macros: the last tool, not the first. Write a macro only when you need to
control evaluation (a new binding, evaluation order, new syntax). If a function,
a higher-order function or an
&rest solves it, it is a function. Every macro: variable capture
avoided with gensym, single evaluation of each argument, and macroexpand-1 in the
test. An exported macro is an API that cannot be changed without recompiling its consumers.
- Conditions and restarts, not exceptions. It is the most underrated technical advantage of the language:
handler-bind runs the handler before unwinding the stack, so a restart-case
can repair and continue. Criterion: libraries signal conditions and offer named restarts;
the application decides with handler-case/invoke-restart. Define your conditions
as subclasses of your own error/warning; never signal simple-error with a string.
- CLOS: generic methods and
defmethod over your own classes; multiple inheritance in moderation.
The MOP is powerful and almost always unnecessary: touching it makes your code depend on implementation
details and hostile to whoever comes next. Written justification for every use.
declaim/declare optimize: by default (safety 1) or higher. (safety 0) disables type
checks and turns an error into memory corruption; it is used in a bounded, measured block,
never globally.
- The REPL's live state is not the program. Everything that works in your image must work
after loading the system from scratch in a new process. Redefining hot is the tool;
the file is the truth.
4. Quality, tests and CI (the image problem)
- The image is the structural problem with CI.
save-lisp-and-die produces an artifact that
contains all the state accumulated in the session — including definitions that are no longer in any
file and, if you are careless, secrets read during the build. Rules: the image is built
in a clean process, from source, with a non-interactive script, in a single reproducible step; and
zero secrets in the build environment (they end up inside the binary).
- Minimum CI gate, in order of cost: (1) the system loads from scratch in a clean image
with no compilation warnings — a
STYLE-WARNING about an undefined function is almost always a typo
that in Lisp does not fail until the call; (2) the suite (FiveAM/Parachute) passes run from the CLI
with a non-zero exit code on failure; (3) the lockfile (qlfile.lock/ocicl.csv) is
committed and has not drifted; (4) the image builds and starts.
- Tests: observable behaviour, with explicit coverage of signalled conditions and
offered restarts — that is the part of the contract that most often breaks silently. Property-based with
cl-quickcheck/check-it where the domain allows it.
- With no type system to cover you: edge validation is manual and mandatory.
Type declarations on public interfaces (SBCL checks them and uses them to optimise).
§6 is deliberately omitted: the observability, timeouts and capacity of a Lisp service
have no language-specific criteria — they are governed by observability-standards and by the
platform's skill. The only specific parts (the image, GC, save-lisp-and-die) are in §4 and §5.
5. Stack security
- The ecosystem's security fact: Quicklisp distributes over plaintext HTTP, and its per-library
integrity is MD5 and SHA-1. Verified by reading the client and the metadata raw: the current
dist is
version: 2026-01-01, and distinfo.txt, releases.txt, systems.txt and the tarballs
are served from http://beta.quicklisp.org/ without TLS; releases.txt lists file-md5 and
content-sha1 per release. The only thing signed with OpenPGP is the bootstrap file
quicklisp.lisp (fingerprint published at quicklisp.org/beta), not the libraries. Consequences:
- Neither MD5 nor SHA-1 resists collisions, and the index containing them arrives over an
unauthenticated channel: there is no usable integrity verification against an attacker on the network.
- There is no per-library author signature: the dist's curation checks that it builds
together, not that it is safe. It is not an audit.
- Criterion: verify the bootstrap's PGP signature; a lockfile is mandatory; install in a
build environment with no write access to production; and, for a serious supply chain,
ocicl (HTTPS + digest-addressed OCI artifacts + sigstore) or vendor and review
the critical dependencies. Additionally: force HTTPS/a trusted proxy at the network level.
read is a sink: Common Lisp's reader evaluates with #. if *read-eval* is on, and it
interns symbols without limit. Never read untrusted input: *read-eval* set to nil,
with-standard-io-syntax, and prefer an explicit parser (JSON/EDN/whatever) to reading someone else's
S-expressions. A read over user data is remote code execution.
eval, compile and intern at runtime over external data: forbidden. Unbounded interning
is additionally an exploitable memory leak.
- The saved image contains everything that was in memory: variables with credentials, build
tokens, history. Treat it as a sensitive artifact; generate it without secrets and scan it.
safety 0 is a security defect, not an optimisation: it removes the checks that
turn a bug into a controlled error instead of memory corruption.
- Swank/slynk is a remote console with arbitrary execution: never listening in production
nor on any interface other than
127.0.0.1, and only over an SSH tunnel.
7. Sustainability, migration and prohibitions
Adoption criteria (decide beforehand, not afterwards):
- Bus factor ≥3 with people who can operate and modify it. If one person sustains the
system, you are building a liability, however good the code is.
- The advantage has to come from the language: macros/DSLs, interactive exploration over live state,
symbolic modelling. If the argument is "it is more elegant", it is not an argument.
- A budget for training and for in-house tooling: some of the tooling you take for granted does not exist and
you are going to write it.
- If (1)-(3) do not hold: another language. It is the right answer most of the time, and
saying it here is cheaper than discovering it in year three.
Exiting an existing CL system — this applies only if there is a real reason (impossible
succession, unsustainable commercial dependency), not fashion: encapsulate and freeze rather than rewrite.
Extract the core of value behind a stable interface (a separate process, HTTP/gRPC), build the
new thing outside against that interface (strangler fig, see refactoring-tech-debt-standards) and do not
automatically translate macros into anything: there is no equivalent target, and the result inherits the
structure without preserving the semantics.
Prohibitions:
- ❌ FORBIDDEN:
ql:quickload without a committed lockfile (qlfile.lock / ocicl.csv): the build is not
reproducible and the dependency floats.
- ❌ FORBIDDEN:
read/eval/compile/intern over untrusted input; *read-eval* enabled
when reading someone else's data.
- ❌ Swank/slynk exposed beyond
localhost; a remote REPL against production as an operating
method ("I connect and fix it hot" is not a procedure: it leaves no trace and no rollback).
- ❌ Building the image from an interactive session, or deploying an image that does not come from a
clean build from source. An image is by definition not reproducible if the process is not.
- ❌ Secrets present in the image build's environment.
- ❌ Global
(safety 0), or (speed 3) without having measured.
- ❌ Writing a macro where a function suffices; exported macros with no
macroexpand-1 test.
- ❌ Using the MOP without written justification.
- ❌ Ultralisp or any other rolling dist in production.
- ❌ Declaring conformance to R7RS-large: it is not closed (§2).
- ❌ Assuming an implementation's licence: SBCL is not MIT, ABCL is not BSD — read the
COPYING raw (§2).
- ❌ Committing a project to LispWorks or Allegro CL without the licence and renewal cost
in writing, and without a plan if the vendor changes terms.
- ❌ Choosing CCL for a new project (§2).
- ❌ Writing an application in Emacs Lisp.
8. Mandatory web verification
- SBCL: latest release at
sbcl.org/news.html (as of Aug 2026, 2.6.7 of 28 Jul 2026) and the status
of your platform in the platform table — not all of them have a recent binary.
- ECL and CLASP: ECL publishes its tags on GitLab (26.5.5, May 2026), CLASP on GitHub (v3.0.1,
Jun 2026). CCL: confirm whether there has been a release after 1.13 (2024) before ruling it out.
- ABCL — declared gap:
abcl.org did not respond during verification and the GitHub
repository publishes neither tags nor releases (it is a bridge to their SVN), so I could not pin its
current version. Do not invent it: look it up at abcl.org/release-notes.shtml or in the project's
SVN. Its licence is verified (GPLv2 + exception).
- ASDF: latest stable (as of Aug 2026, 3.3.7 of Jan 2024) at
asdf.common-lisp.dev and on
gitlab.common-lisp.net. The fare/asdf GitHub mirror is frozen at 2018: it is not a source.
- Quicklisp: the date of the current dist (as of Aug 2026, 2026-01-01 — seven months without an update;
check whether the cadence has resumed, because a stalled dist is a dependency security
risk), the fingerprint of the bootstrap signing key, and whether the transport is still
plaintext HTTP. ocicl: version and the state of its sigstore verification.
- LispWorks: current pricing and editions at
lispworks.com/buy/ (as of Aug 2026, 8.1: Professional
USD 1,500/3,000, Enterprise USD 4,500) — confirm them on the page before budgeting.
Allegro CL: a structural gap — Franz publishes no rates; any figure has to come from
a contractual quote.
- Racket (v9.2, May 2026), Guile (3.0.x series, 3.0.11 verified) and Chez (10.4.1,
May 2026): current version and its licence read raw (Guile is LGPL: verify it, it constrains
distribution).
- R7RS-large: the status of the colour dockets in the Codeberg repository and the WG2 minutes
before assuming anything is "standard".
- CVEs and advisories for the ecosystem's network/TLS libraries (
cl+ssl, HTTP servers), which is where
the real attack surface is, and for the implementation itself.
If the web contradicts this document, the web wins — flag the discrepancy.
1---2name: lisp-standards3description: The Lisp family except Clojure - Common Lisp and Scheme. Use when working with .lisp, .lsp, .cl, .asd, .scm, .ss, .sls, .sld, .rkt or .el files, ASDF defsystem forms, Quicklisp (ql:quickload, quicklisp.lisp, dists, qlfile/qlfile.lock with Qlot, ocicl and ocicl.csv), SBCL, Clozure CL, ECL, ABCL, CLASP, CLISP, CMUCL, LispWorks or Allegro CL images, save-lisp-and-die and dumped Lisp images, SLIME or Sly and swank/slynk REPL sessions, defmacro and macroexpand-1, CLOS defclass/defgeneric/defmethod and the MOP, the condition system (handler-bind, handler-case, restart-case, invoke-restart, signal, cerror), declaim/declare optimize speed safety, fiveam/parachute/rove test systems, Racket raco and4---56# Lisp family standards (Common Lisp and Scheme)78Criteria verified as of **August 2026**. Re-verify on the web before committing to anything (§8).910## 1. Scope and triggers1112**Common Lisp is alive, but in specific niches and with a small ecosystem.** It is not a13dead technology nor an exercise in nostalgia: there are implementations with monthly releases (SBCL14published 2.6.7 on 28 Jul 2026), there are commercial vendors charging for support and there are systems in15production. What there is **not** is critical mass: the library surface, the depth of the16tooling and the job market are orders of magnitude smaller than those of any17mainstream language, and that is a project risk, not an aesthetic opinion.1819**Choose it on criteria, not on taste.** Where it genuinely wins: long-lived systems where the20domain is better modelled as its own language (macros, DSLs, compilers, rule and21planning engines, CAD/CAM, symbolic systems), interactive exploration over live state (a REPL22attached to a connected image is a real advantage, not a preference), and teams that already know it.23Where it does not win: CRUD, infrastructure glue, and anything that will rotate between many hands.24**Before the first line:** are there ≥3 people able to operate and modify it? If not, see §7.2526Covers: ANSI Common Lisp and its implementations (free and commercial, with their cost); ASDF and27library distribution (**Quicklisp and its integrity model, which is the ecosystem's security fact**28— §5); the image, `save-lisp-and-die` and why that breaks modern CI; the REPL as a29method; **the condition and restart system** versus exceptions; macros and when **not** to write30one; CLOS and the MOP. Also Scheme (R7RS, Racket, Guile, Chez) and Emacs Lisp as a separate case.3132**Not applicable**: see `clojure-standards` (**Clojure and ClojureScript are a Lisp and have their own33skill: cede them entirely** — `deps.edn`, `project.clj`, `.clj`/`.cljs`/`.cljc`/`.edn`, the JVM, `clj-kondo`,34the nREPL, immutability by default and its state model. **The boundary is not "it is a35Lisp"**: Clojure has no condition system, no CLOS, no image, no `read-eval` over mutable36data, and its dependency management is Maven's; nothing is extrapolated from there to here),37`haskell-fp-standards` and `ocaml-fsharp-standards` (**typed functional programming**: the boundary is the38static type system and evaluation, not the paradigm), `scala-standards`, `julia-standards`39(**another homoiconic language with macros and REPL-driven development, aimed at numerical computing**: if the40problem is numerical, it is theirs), `elixir-erlang-standards` (macros and Elixir's `defmacro` live41there), `python-standards`/`go-standards`/`typescript-standards` (the real alternative when §7 says42Lisp is not the answer), `developer-workstation-standards` (**configuring Emacs as an editor is43theirs**; here only Emacs Lisp *as a language*), `refactoring-tech-debt-standards` and44`enterprise-architecture-standards` (modernisation and portfolio strategy),45`legacy-modernization-standards` (**the umbrella skill** for an inherited Lisp system: which "R" is46chosen, whether it is frozen, rewritten or retired, and the archaeology beforehand) and47`migration-projects-standards` (**executing the cutover** once decided: rehearsal, window,48data reconciliation, rollback and switching off the source), `opensource-licensing-standards` (licence analysis; here only which licence each49implementation carries and what it implies, §2), `cicd-standards` (the pipeline; here the specific problem of50building from an image), `appsec-standards` and `vulnerability-management-standards` (methodology51and triage; here the concrete sinks and Quicklisp's integrity model), `webassembly-standards`52(if the target is Wasm), `solidity-standards`, `mumps-standards` and `ibm-i-rpg-standards` (**they are not53comparable**: those are legacy platforms with no choice; Common Lisp is still a possible54choice and it has to be defended).5556## 2. Default decisions / Toolchain5758> Verify the latest version on the web before pinning it in a real project (§8).5960| Decision | Choice | Verified note (Aug 2026) |61|---|---|---|62| Default CL implementation | **SBCL** | **2.6.7, 28 Jul 2026**; roughly monthly cadence. A native compiler, the best performance and the best community support. It is the default unless there is a written reason |63| SBCL's licence | **Public domain + partial BSD/MIT** — read raw from `COPYING` | *"SBCL is derived from CMU CL, which was released into the public domain"*; later changes go to the public domain *"or under the FreeBSD licence where not"*, with MIT on LOOP/PCL/CityHash. **It is not "MIT" and not "GPL": do not assume** |64| CL on the JVM | **ABCL**, only if the requirement is Java interop | **Licence verified raw (`COPYING`): GPL v2 with a Classpath-style exception (13th term)** — not MIT and not BSD; it affects how you distribute. **Gap: I could not verify its current version** (§8) |65| Embedded CL / C++ | **ECL** (embeddable, compiles to C) or **CLASP** (on LLVM, C++ interop) | ECL: tag **26.5.5** (5 May 2026) on **GitLab**, not GitHub. CLASP: **v3.0.1** (Jun 2026). Both active |66| CCL (Clozure) | **Maintenance only**: not for new projects | Latest release **1.13 (2024)**; no releases in 2025-2026. It is not dead, but its cadence does not sustain a new choice |67| Commercial implementation | **LispWorks** or **Allegro CL**, only with a requirement that justifies them (cross-platform GUI, mobile delivery, contractual support, closed deliverables) | **LispWorks publishes prices**: 8.1 Professional **USD 1,500 (32-bit) / USD 3,000 (64-bit)**, Enterprise **USD 4,500**, per user, with no runtime royalties. **Allegro CL does not publish rates: it is quote-only** ("contact … for a customized price quote"). The licence cost is the expensive fact, not the syntax |68| System definition | **ASDF** (`.asd`), no alternative | Latest stable **3.3.7 (Jan 2024)**. It lives on `gitlab.common-lisp.net`; **the `fare/asdf` GitHub repository has been frozen since 2018 and is not the source of truth** |69| Library distribution | **Quicklisp** as the base + **Qlot** (`qlfile.lock`) or **ocicl** (`ocicl.csv`) to pin versions | **A versioned lockfile is mandatory.** Bare `ql:quickload` is not reproducible |70| Modern alternative | **ocicl**: OCI artifacts, HTTPS, sigstore signatures, per-project local installation | **MIT** licence, read raw from `LICENSE`. It is the option with the best supply-chain story (§5) |71| Rolling dist | **Ultralisp: forbidden in production** | Rolling with no curation: it widens the supply surface with no operational compensation |72| Editor / REPL | **SLIME (swank)** or **Sly (slynk)** on Emacs; alternatives for VS Code/Vim | The REPL attached to the image is the working method, with the discipline in §3 |73| Tests | **FiveAM** or **Parachute** | Runnable from the CLI in a clean process, not only from the REPL (§4) |74| Scheme: applications | **Racket** | **v9.2 (26 May 2026)**. It is the Scheme with an ecosystem, documentation and tooling of its own (`raco`, `#lang`); if in doubt between Schemes, this one |75| Scheme: embedded / extension | **Guile** (GNU, LGPL — verify) | Latest verified stable series **3.0.11**. The forced choice if you extend GNU software |76| Scheme: performance | **Chez Scheme** | **10.4.1 (May 2026)**, in `cisco/ChezScheme`. It is also Racket CS's backend |77| Scheme standard | **R7RS-small** as the portability target | **R7RS-large is NOT closed**: it advances through *colour dockets* (Red 2016, Tangerine 2019) and **there is no single ratification event**. Its development moved to **Codeberg**. **Do not write "R7RS-large compliant" in any document** |78| Emacs Lisp | **A separate case**: an editor's extension language, not an application language | You do not write an application in Elisp. Editor configuration belongs to `developer-workstation-standards` |7980## 3. Structure and conventions8182- **One ASDF system per deployable unit**, with an explicit `:depends-on` and **no circular83 dependencies between systems**. Tests go in a separate system (`foo/tests`), never hanging off the84 main system: otherwise the production binary drags the test framework along.85- **Explicit packages (`defpackage`), with `:export` as the contract.** No `:use` of other people's86 packages beyond `:cl` — importing symbols wholesale causes collisions that only appear when87 updating a dependency. `uiop` is referenced with a prefix.88- **Macros: the last tool, not the first.** Write a macro **only** when you need to89 control evaluation (a new binding, evaluation order, new syntax). If a function,90 a higher-order function or an `&rest` solves it, **it is a function**. Every macro: variable capture91 avoided with `gensym`, single evaluation of each argument, and `macroexpand-1` in the92 test. An exported macro is an API that cannot be changed without recompiling its consumers.93- **Conditions and restarts, not exceptions.** It is the most underrated technical advantage of the language:94 `handler-bind` runs the handler **before unwinding the stack**, so a `restart-case`95 can repair and continue. Criterion: libraries **signal** conditions and **offer named restarts**;96 **the application decides** with `handler-case`/`invoke-restart`. Define your conditions97 as subclasses of your own `error`/`warning`; **never signal `simple-error` with a string**.98- **CLOS**: generic methods and `defmethod` over your own classes; multiple inheritance in moderation.99 **The MOP is powerful and almost always unnecessary**: touching it makes your code depend on implementation100 details and hostile to whoever comes next. Written justification for every use.101- **`declaim`/`declare optimize`**: by default **`(safety 1)` or higher**. `(safety 0)` disables type102 checks and turns an error into memory corruption; it is used in a bounded, measured block,103 never globally.104- **The REPL's live state is not the program.** Everything that works in your image must work105 after loading the system from scratch in a new process. Redefining hot is the tool;106 **the file is the truth**.107108## 4. Quality, tests and CI (the image problem)109110- **The image is the structural problem with CI.** `save-lisp-and-die` produces an artifact that111 contains all the state accumulated in the session — including definitions that are no longer in any112 file and, if you are careless, **secrets read during the build**. Rules: the image is built113 **in a clean process, from source, with a non-interactive script**, in a single reproducible step; and114 **zero secrets in the build environment** (they end up inside the binary).115- **Minimum CI gate, in order of cost**: (1) the system **loads from scratch** in a clean image116 with no compilation warnings — a `STYLE-WARNING` about an undefined function is almost always a typo117 that in Lisp does not fail until the call; (2) the suite (FiveAM/Parachute) passes **run from the CLI**118 with a non-zero exit code on failure; (3) the lockfile (`qlfile.lock`/`ocicl.csv`) is119 committed and has not drifted; (4) the image builds and starts.120- **Tests**: observable behaviour, with explicit coverage of **signalled conditions and121 offered restarts** — that is the part of the contract that most often breaks silently. Property-based with122 `cl-quickcheck`/`check-it` where the domain allows it.123- **With no type system to cover you**: edge validation is manual and mandatory.124 Type declarations on public interfaces (SBCL checks them and uses them to optimise).125126*§6 is deliberately omitted*: the observability, timeouts and capacity of a Lisp service127have no language-specific criteria — they are governed by `observability-standards` and by the128platform's skill. The only specific parts (the image, GC, `save-lisp-and-die`) are in §4 and §5.129130## 5. Stack security131132- **The ecosystem's security fact: Quicklisp distributes over plaintext HTTP, and its per-library133 integrity is MD5 and SHA-1.** Verified by reading the client and the metadata raw: the current134 dist is `version: 2026-01-01`, and `distinfo.txt`, `releases.txt`, `systems.txt` and the tarballs135 are served from `http://beta.quicklisp.org/` without TLS; `releases.txt` lists `file-md5` and136 `content-sha1` per release. **The only thing signed with OpenPGP is the bootstrap file137 `quicklisp.lisp`** (fingerprint published at quicklisp.org/beta), not the libraries. Consequences:138 - **Neither MD5 nor SHA-1 resists collisions**, and the index containing them arrives over an139 unauthenticated channel: **there is no usable integrity verification against an attacker on the network**.140 - **There is no per-library author signature**: the dist's curation checks that it **builds141 together**, not that it is safe. It is not an audit.142 - **Criterion**: verify the bootstrap's PGP signature; **a lockfile is mandatory**; install in a143 build environment with no write access to production; and, for a serious supply chain,144 **`ocicl`** (HTTPS + digest-addressed OCI artifacts + sigstore) or vendor and review145 the critical dependencies. **Additionally: force HTTPS/a trusted proxy at the network level.**146- **`read` is a sink**: Common Lisp's reader evaluates with `#.` if `*read-eval*` is on, and it147 interns symbols without limit. **Never `read` untrusted input**: `*read-eval*` set to `nil`,148 `with-standard-io-syntax`, and prefer an explicit parser (JSON/EDN/whatever) to reading someone else's149 S-expressions. A `read` over user data is remote code execution.150- **`eval`, `compile` and `intern` at runtime over external data: forbidden.** Unbounded interning151 is additionally an exploitable memory leak.152- **The saved image contains everything that was in memory**: variables with credentials, build153 tokens, history. Treat it as a sensitive artifact; generate it without secrets and scan it.154- **`safety 0` is a security defect**, not an optimisation: it removes the checks that155 turn a bug into a controlled error instead of memory corruption.156- **Swank/slynk is a remote console with arbitrary execution**: **never listening in production**157 nor on any interface other than `127.0.0.1`, and only over an SSH tunnel.158159## 7. Sustainability, migration and prohibitions160161**Adoption criteria (decide *beforehand*, not afterwards):**1621. **Bus factor ≥3** with people who can operate and modify it. If one person sustains the163 system, you are building a liability, however good the code is.1642. **The advantage has to come from the language**: macros/DSLs, interactive exploration over live state,165 symbolic modelling. If the argument is "it is more elegant", it is not an argument.1663. **A budget for training and for in-house tooling**: some of the tooling you take for granted does not exist and167 you are going to write it.1684. If (1)-(3) do not hold: **another language**. It is the right answer most of the time, and169 saying it here is cheaper than discovering it in year three.170171**Exiting an existing CL system** — this applies only if there is a real reason (impossible172succession, unsustainable commercial dependency), not fashion: **encapsulate and freeze** rather than rewrite.173Extract the core of value behind a stable interface (a separate process, HTTP/gRPC), build the174new thing outside against that interface (*strangler fig*, see `refactoring-tech-debt-standards`) and **do not175automatically translate macros into anything**: there is no equivalent target, and the result inherits the176structure without preserving the semantics.177178**Prohibitions:**179- ❌ **FORBIDDEN**: `ql:quickload` without a committed lockfile (`qlfile.lock` / `ocicl.csv`): the build is not180 reproducible and the dependency floats.181- ❌ **FORBIDDEN**: `read`/`eval`/`compile`/`intern` over untrusted input; `*read-eval*` enabled182 when reading someone else's data.183- ❌ Swank/slynk exposed beyond `localhost`; a remote REPL against production as an operating184 method ("I connect and fix it hot" is not a procedure: it leaves no trace and no rollback).185- ❌ Building the image from an interactive session, or deploying an image that does not come from a186 clean build from source. An image is by definition not reproducible if the process is not.187- ❌ Secrets present in the image build's environment.188- ❌ Global `(safety 0)`, or `(speed 3)` without having measured.189- ❌ Writing a macro where a function suffices; exported macros with no `macroexpand-1` test.190- ❌ Using the MOP without written justification.191- ❌ Ultralisp or any other rolling dist in production.192- ❌ Declaring conformance to **R7RS-large**: it is not closed (§2).193- ❌ Assuming an implementation's licence: **SBCL is not MIT, ABCL is not BSD** — read the194 `COPYING` raw (§2).195- ❌ Committing a project to LispWorks or Allegro CL without the licence **and renewal** cost196 in writing, and without a plan if the vendor changes terms.197- ❌ Choosing CCL for a new project (§2).198- ❌ Writing an application in Emacs Lisp.199200## 8. Mandatory web verification2012021. **SBCL**: latest release at `sbcl.org/news.html` (as of Aug 2026, **2.6.7 of 28 Jul 2026**) and the status203 of your platform in the *platform table* — not all of them have a recent binary.2042. **ECL and CLASP**: ECL publishes its tags **on GitLab** (26.5.5, May 2026), CLASP on GitHub (v3.0.1,205 Jun 2026). **CCL**: confirm whether there has been a release after 1.13 (2024) before ruling it out.2063. **ABCL — declared gap**: `abcl.org` did not respond during verification and **the GitHub207 repository publishes neither tags nor releases** (it is a bridge to their SVN), so **I could not pin its208 current version**. Do not invent it: look it up at `abcl.org/release-notes.shtml` or in the project's209 SVN. Its licence **is** verified (GPLv2 + exception).2104. **ASDF**: latest stable (as of Aug 2026, **3.3.7 of Jan 2024**) at `asdf.common-lisp.dev` and on211 `gitlab.common-lisp.net`. **The `fare/asdf` GitHub mirror is frozen at 2018: it is not a source.**2125. **Quicklisp**: the date of the current dist (as of Aug 2026, **2026-01-01** — seven months without an update;213 check whether the cadence has resumed, because **a stalled dist is a dependency security214 risk**), the fingerprint of the bootstrap signing key, and whether the transport is still215 plaintext HTTP. **ocicl**: version and the state of its sigstore verification.2166. **LispWorks**: current pricing and editions at `lispworks.com/buy/` (as of Aug 2026, 8.1: Professional217 USD 1,500/3,000, Enterprise USD 4,500) — **confirm them on the page before budgeting**.218 **Allegro CL: a structural gap — Franz publishes no rates**; any figure has to come from219 a contractual quote.2207. **Racket** (v9.2, May 2026), **Guile** (3.0.x series, 3.0.11 verified) and **Chez** (10.4.1,221 May 2026): current version and its licence read raw (**Guile is LGPL: verify it, it constrains222 distribution**).2238. **R7RS-large**: the status of the *colour dockets* in the **Codeberg** repository and the WG2 minutes224 before assuming anything is "standard".2259. CVEs and advisories for the ecosystem's network/TLS libraries (`cl+ssl`, HTTP servers), which is where226 the real attack surface is, and for the implementation itself.227228If the web contradicts this document, **the web wins** — flag the discrepancy.