Contrarian claim to start: async/await did not make concurrent programming easy. It made concurrent programming look easy, which is a different achievement and a more dangerous one.
That distinction is at the heart of a paper I’ve been chewing on. “A Design Space Exploration of Async/Await” by Gavin Gray, Shriram Krishnamurthi, and Will Crichton went up on arXiv on August 21, 2026, appeared on the Cognitive Engineering Lab site on September 8, and is headed to OOPSLA 2026. It picked up a modest 9 points on Hacker News, which tells you something about how quietly important ideas travel. The core contribution is an articulated design space with nine dimensions describing how tasks behave and execute.
Nine dimensions. For two keywords. Sit with that for a second.
Why a non-technical reader should care
If you work with AI agents, you spend your day surrounded by waiting. An agent asks a model for a response and waits. It calls a search tool and waits. It writes to a database and waits. Waiting is not a side effect of agent work, it is most of agent work.
Async/await is the language feature that describes waiting. The pitch, and the paper names this directly, is straight-line asynchrony: code that pauses and resumes should read top to bottom like ordinary code, even though the machine underneath is doing something far messier. You write “await this,” the program goes off and does other useful things, then picks up where it left off.
That readability is genuinely valuable. It’s also why the feature gets mistaken for a solved problem. The words look the same in every language. The behavior does not.
The nine dimensions problem
What the authors did is map out where the real choices live. Nine separate dimensions govern how a task behaves and how it executes. Different languages resolve those dimensions differently, and the paper identifies key differences that affect two things in particular: the task lifecycle and cancellation.
Those two words deserve translation.
Task lifecycle
When does the waiting actually begin? In some designs, describing a task starts it immediately. In others, nothing happens until someone waits on it. Same syntax, different moment of ignition. If you’ve ever wondered why a piece of code behaved differently after a straightforward-looking port between languages, this is a common culprit.
Cancellation
This one matters enormously for agents. Cancellation is what happens when you change your mind mid-wait. The user closes the tab. A timeout fires. A cheaper answer arrives first and the expensive one is no longer needed.
Stopping something that has already started is a hard problem, and languages disagree about the answers. Who is allowed to cancel? Does the cancelled task get a chance to clean up? What happens to work it already started elsewhere? None of that is visible in the word “await.” All of it is visible in your incident reports.
What this means for people building with agents
The paper is a programming languages paper, not an agents paper. I’m the one drawing the line. But the line is short.
- Cancellation is a product decision disguised as a technical one. Whether your agent can be cleanly interrupted mid-task shapes how the interface feels. A stop button that doesn’t really stop anything is a broken promise.
- Framework choice carries design choices you didn’t make. When you pick a stack, you inherit its position on those nine dimensions. You may never read the documentation for them, but your system’s behavior under load is downstream of them.
- “It worked in the other language” is not a bug report. Ported concurrency logic can be structurally correct and behaviorally different, because the paper’s whole point is that the surface syntax hides variation underneath.
- Ask about waiting behavior in code review. Not “is this async,” but “when does it start, and what happens if we stop it early.” Two questions, and they cover a lot of ground.
The honest read
I don’t think this research is an argument against async/await. Straight-line asynchrony is a real ergonomic gain, and the paradigm’s stated aim of simplifying concurrent programming is a good aim. Making hard machinery readable is worthwhile work.
What the paper does is drag the hidden choices into daylight and give them names. Naming things is underrated. Once you know there are nine dimensions and that cancellation and task lifecycle are where languages diverge, you can ask better questions instead of assuming the keyword handled it for you.
Async/await isn’t a feature you adopt. It’s a set of decisions you inherit. Knowing which ones you got is the whole job.
🕒 Published: