SQLAlchemy & Documentation


I have used ORMs for decades now (Hibernate, ActiveRecord, GORM1, Entity Framework, and many others too minor to note), but I am not an expert in SQLAlchemy. I have used it for small projects, yet recently came back to working with it as an ORM.2

Not to take away from issues with other frameworks, but my recent return to SQLAlchemy highlights one of the most glaring issues: how technical matters impact the experience of new and/or returning developers.

Two Distinct APIs?

Did you know that code that uses SQLAlchemy can look vastly different depending on what you use it for?

That’s probably good to know at the outset.

SQLAlchemy is presented as two distinct APIs, one building on top of the other. These APIs are known as Core and ORM.

– from the SQLAlchemy Unified Tutorial3

Given this very big distinction, I’ll say that the documentation for “Core” is far less thorny, and makes more sense to the novice user: it’s composed of mappings to tables and a thin DSL on top of all the SQL primitives we know and love. The documentation for “Core” is terse, practical, and to the point.

I wish I understood that sooner.

Unfortunately, like the majority of developers, I did not start there. I first arrived at SQLAlchemy with a domain model in mind and the belief that an ORM is what I’m shopping for.

And that’s where the documentation reads more like a car manual. Imagine you’ve driven many cars, but you need to understand how this specific engine works before you drive this one.

The Deep End Comes Quick

The docs for SQLAlchemy ORM are often extensive but not immediately useful. For example, look at ORM Mapped Class Overview. It comes across as someone coming on way too hard, way too early in a relationship:

In modern SQLAlchemy, the difference between these styles is mostly superficial; when a particular SQLAlchemy configurational style is used to express the intent to map a class, the internal process of mapping the class proceeds in mostly the same way for each, where the end result is always a user-defined class that has a Mapper configured against a selectable unit, typically represented by a Table object, and the class itself has been instrumented to include behaviors linked to relational operations both at the level of the class as well as on instances of that class.

As the process is basically the same in all cases, classes mapped from different styles are always fully interoperable with each other. The protocol MappedClassProtocol can be used to indicate a mapped class when using type checkers such as mypy.

This is the first thing you read after a one-sentence overview.

Let’s assume the meanings of “declarative” and “imperative” are evident to the reader (which, for many developers, may not be obvious at all). The documentation presents facts, maybe even very useful facts, but facts that do not belong in any overview unless one is already steeped in SQLAlchemy.

And what does the term “modern” mean in “modern SQLAlchemy” anyway? Is this documenting how it works, or how it works in comparison to how it used to work? It can come across as confusing.

“I just walked in. What is going on?”

And that’s my (short) gripe. I’ve worked with ORMs for a long time and have some experience with SQLAlchemy. But reading the documentation with fresh eyes, I longed to be somewhere else. Anywhere else.

You’ve got a busy morning ahead of you…

SQLAlchemy ORM Overview

You’ve got a busy morning ahead of you…

The first thing you see when you click ‘SQLAlchemy ORM’ is the page above (it also tells you to go to the ‘first tutorial’ which I presume is the “Unified Tutorial” but it doesn’t say). How is this helpful to someone brand new to the framework?

Comparing Documentation

Often I would start with ActiveRecord’s documentation, which is well laid out, helpful, and of similar age to SQLAlchemy. But let’s consider GORM instead.

Envision you are a developer familiar with other ORMs, and interested in using an ORM for your new Go project. This is what you’d see.

This overview is an actual overview.

GORM's Overview

This overview is an actual overview.

This is a reminder of what good documentation can look like:

  • A bulleted list, easily scannable for terminology that may already be familiar to the reader.
  • Lack of expository text telling you what to do in what order.
  • Tutorials have their own section with short, descriptive names.
  • No history lesson. It suggests what the framework does in its entirety.

The Real Problem

Okay, I lied. It’s not really about SQLAlchemy.

If people can’t adequately orient themselves around the documentation for a popular framework, many inevitably land on StackOverflow or HackerNews, both sites being veritable wastelands of lousy advice and useless hot takes.

Nobody goes back to an old HN thread to make corrections, or remove up-votes when advice becomes outdated. Yet new developers continue to accept outdated answers on these sites or manipulate outdated idioms to their new codebases when they should visit the docs instead.

But if those docs are hard to navigate, it’s a real problem.

  1. I wrote this with Groovy in mind, but I soon remembered the Go framework which goes by the same acronym. I worked with both of these, years apart. ↩︎

  2. All this discussion aside, as of 2024, it’s still the best option if you are looking for a well-supported ORM in the Python space. ↩︎

  3. Any time you see ‘unified’ in a title for anything, you already know you’re working through a lot of history. ↩︎