Airframe: a batteries-included building block set for Scala services
Essential Building Blocks for Scala
At a glance
- What is it?
- Airframe is a wide collection of Scala modules covering dependency injection, functional testing with AirSpec, HTTP clients and servers, RPC, serialization, SQL, metrics and logging, published as a set of Maven Central artifacts. It targets Scala 3 by default, and it takes release engineering seriously enough to script its own releases and check binary compatibility with MiMa.
- Who is it for?
- Airframe fits a Scala team that wants infrastructure code to come from one project with one release train, rather than assembling logging, DI, HTTP, RPC and testing from separate libraries, and that will invest in learning the Airframe idioms. It is the wrong choice for a project that wants a minimal dependency set or a single sharp library for one job, since the repository ships around forty modules and adopting Airframe means adopting its conventions.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Scala, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One project, forty infrastructure modules
The project description is a five-word summary: essential building blocks for Scala. The README expands it into the list of things a service usually needs and a team should not want to assemble from scratch: logging, object serialization using JSON or MessagePack, dependency injection, an HTTP server and client with RPC support, and functional testing with AirSpec.
The repository root is where the real scope shows. There are around forty airframe-* modules in the tree, and the names read like a checklist for a backend service.
The web and RPC side is the deepest part. There is airframe-http, and then separate modules for airframe-http-netty, airframe-http-okhttp, airframe-http-finagle, airframe-http-grpc, airframe-http-codegen and airframe-http-recorder, which is a recorder rather than an adapter in the usual sense. There is airframe-rpc plus airframe-rx, airframe-rx-html and airframe-rx-widget for the reactive and view layers, and airframe-control, airframe-surface and airframe-canvas.
Around those sit the utility modules: airframe-core and airframe-core-base, airframe-di and airframe-di-macros, airframe-config, airframe-codec, airframe-json, airframe-msgpack, airframe-parquet, airframe-log, airframe-metrics, airframe-jmx, airframe-jdbc, airframe-sql, airframe-ulid and airframe-launcher. Then the testing side: airspec, airframe-scalatest, airframe-dotty-test, airframe-integration-test with api and js variants, and airframe-benchmark.
The point of listing them is that Airframe is not a framework in the sense of one big runtime. It is a family of libraries that share a release train, a version scheme and a set of conventions. You take the modules you need.
Three pieces the project puts at the centre
Most of the module list is infrastructure you could source elsewhere. Three parts of Airframe are its own designs, and they are the ones the README links separately.
Airframe DI is described as a dependency injection library tailored to Scala, and it has its own documentation page. The word tailored is the whole argument: a DI container written for a language with traits, by-name parameters and type classes has to make choices about how those interact with dependency graphs, and a container designed around classes and constructors makes different choices. Airframe DI also has a macros module, airframe-di-macros, which suggests compile-time work rather than runtime reflection, a common approach in Scala.
AirSpec is the functional testing library, again with its own page and its own repository directory, airspec/. It is positioned as a testing framework, not as a set of assertions, and the topics list includes testing-framework.
Airframe RPC is the third, described as a framework for using Scala both for frontend and backend programming. The README includes an RPC overview diagram for it. The idea of a shared Scala codebase on both sides of the wire is what makes the module set interesting: if the client and the server are the same language, an RPC layer can generate both ends from one interface definition, and the surrounding modules for HTTP, code generation and reactive streams are the parts that make that usable.
Taken together, these three form a coherent position. Airframe is a set of Scala-native replacements for the usual platform-neutral Java libraries, aimed at teams who want their infrastructure idiomatic to the language they already use.
Scala 3 by default, with 2.12 and 2.13 still served
Airframe uses Scala 3 as the default Scala version, and the README explains how to work with the older ones: to use Scala 2.x versions, run `++ 2.12` or `++ 2.13` in the sbt console.
That is the whole compatibility story in the README, and it is worth reading carefully before you start. The default is the newest language version, and the older ones are available by switching the sbt Scala version at the console. The Maven Central badge in the README points at the airframe_2.12 artifact, which is the cross-built suffix for the 2.12 line, so artifacts exist for the older versions as well.
For a team on Scala 2.13, this means the library is usable but you are on the compatibility path rather than the default one. That distinction matters when you hit a problem, because the reproduction will differ between the two, and it matters when the upstream project optimises for the newer language.
The build is sbt, and the repository root shows the toolchain: build.sbt, a project/ directory for the build definition, a sbt/ directory, and sbt-airframe/ for a plugin of the same name. There is also a package.json with a single dependency, jsdom, and a pnpm-lock.yaml, which is a detail worth understanding rather than skipping. JavaScript is not Airframe's target, but the airframe-integration-test-js module and the jsdom dependency mean some tests run in a JavaScript environment, presumably to check the behaviour of code that has to work on both sides of an RPC boundary.
The formatting and linting setup is also conventional Scala practice: .scalafmt.conf, .scalafix.conf, and .scala-steward.conf for automated dependency updates, plus .codecov.yml and a .git-blame-ignore-revs file, which is the mechanism for marking reformatting commits so that git blame stays useful.
Releases are cut by a script, not by a web button
The release process is documented in unusual detail, and it explains why the version numbers look the way they do.
Every pull request is labelled automatically by release-drafter using the rules defined in .github/release-drafter.yml, which means the changelog is assembled from the pull request titles rather than written by hand. To publish a new version, the README says to create a new release tag as follows:
$ git switch main
$ git pull
$ ruby ./scripts/release.rbThat script updates the release notes file, pushes a new git tag to GitHub, and creates a new GitHub release note. After that, the artifacts are published to Sonatype, which the README calls Maven Central, and it says this usually takes 10 to 30 minutes.
The warning underneath is the most useful line in the section: do not create a new tag from the GitHub release pages, because it will not trigger the GitHub Actions for the release. That is a specific failure mode, and it is the kind of thing that costs an afternoon the first time it happens.
The version scheme is visible in the release list. Recent tags are v2026.2.2 on 2026-07-02, v2026.2.3 on 2026-10-14 and v2026.2.4 on 2026-10-17, so versions are calendar-based with a sequence number, and the repository moves quickly within a release: two tags three days apart in October 2026. The last push was on 2026-10-20.
For a user, that cadence is a double-edged fact. You get frequent releases, and the release notes are the document to read before upgrading, since a version number alone tells you very little here.
MiMa stands between your upgrade and a broken build
The binary compatibility section is the most developer-focused part of the README, and it exists because of the scale of the module set.
The README states that when changing some interfaces, binary compatibility should be checked so as not to break applications using older versions of Airframe. The mechanism is specific. In build.sbt you set AIRFRAME_BINARY_COMPAT_VERSION to the previous version of Airframe as a comparison target, and then you run `sbt mimaReportBinaryIssues` to check the binary compatibility.
MiMa is the standard tool for this in the Scala ecosystem, and using it here means the check is mechanical rather than a reviewer's memory. A change that alters a method signature in airframe-http or a trait in airframe-di will show up as a reported issue rather than as a broken build in someone else's application months later.
This matters more here than in a small library, and the reason is visible in the module count. Airframe is used as a set: an application may depend on airframe-core, airframe-http, airframe-rx and airspec at once, and the versions of those modules are tied together by the release train. The compatibility check is what keeps a shared release train from being a coordinated breaking change.
The setup also has a cost. AIRFRAME_BINARY_COMPAT_VERSION has to be bumped deliberately, and the report has to be read when it flags something. That is a maintenance tax, and it is the price of the release-train model, but it is a tax paid in the project rather than in every application that depends on it.
Where Airframe fits and where it does not
The honest reason to adopt Airframe is scope. A team that needs logging, DI, an HTTP client and server, RPC, JSON and MessagePack serialization, metrics, JMX, SQL access, configuration and a functional testing framework has two realistic options: assemble about a dozen libraries and settle the version conflicts yourself, or take one set of modules that already agree with each other.
The difference in approach against assembling libraries yourself is mostly about coherence. Because the modules share a release train, a bug fix in the HTTP layer and a change in the RPC layer arrive together. Because they share conventions, the DI container, the logger and the test framework use the same idioms. And because the project checks binary compatibility between releases, the upgrade is a version bump rather than an audit.
Where it does not fit is equally clear. If you need one thing, a logging library or a JSON codec, taking Airframe for it is a large dependency for a small need. If your organisation has already standardised on a DI container or an HTTP stack, Airframe competes with decisions already made. And if you are on Scala 2.12 and need the newest fixes, you are on the compatibility path rather than the default one.
The remaining question is the one the documentation answers best. The project publishes module documentation at wvlet.org/airframe/docs, a module overview diagram, release notes, and separate pages for Airframe DI, AirSpec and Airframe RPC, with examples under examples/ in the repository for RPC, the reactive demo and shared source. The site wvlet.org/airframe is the entry point, and reading the module list and the release notes before you add a dependency is the fastest way to decide whether the set fits your problem.
Editorial conclusion
Airframe fits a Scala team that wants infrastructure code to come from one project with one release train, rather than assembling logging, DI, HTTP, RPC and testing from separate libraries, and that will invest in learning the Airframe idioms. It is the wrong choice for a project that wants a minimal dependency set or a single sharp library for one job, since the repository ships around forty modules and adopting Airframe means adopting its conventions. Before committing, check the module list in the repository root against the pieces you actually need, confirm the Scala version you target is served by the artifacts you want, and read the release notes at wvlet.org/airframe/docs/release-notes.html, since versions are cut by a script and published to Maven Central with a ten to thirty minute delay.
Frequently asked questions
What is Airframe for Scala?
It is a collection of essential building blocks for Scala applications, including logging, object serialization using JSON or MessagePack, dependency injection, an HTTP server and client with RPC support, and functional testing with AirSpec. The project is published as a family of modules under org.wvlet.airframe.
Which Scala versions does Airframe support?
The README states that Airframe uses Scala 3 as the default Scala version, and that to use Scala 2.x versions you run ++ 2.12 or ++ 2.13 in the sbt console. The Maven Central badge points at the airframe_2.12 artifact.
How is a new Airframe version published?
Pull requests are labelled automatically by release-drafter, then the release is cut by running ruby ./scripts/release.rb from the main branch, which updates the release notes, pushes a tag and creates the GitHub release. The artifacts reach Sonatype, which is Maven Central, usually 10 to 30 minutes later, and tagging from the GitHub release pages will not trigger the release workflow.
How does Airframe protect binary compatibility?
The README says to set AIRFRAME_BINARY_COMPAT_VERSION in build.sbt to the previous version of Airframe as the comparison target, then run sbt mimaReportBinaryIssues to check binary compatibility, so that applications using older versions are not broken.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/wvlet-airframe)Was this useful?