In 2020, a lot of software teams made the same decision under duress: kill the meetings, go async. The logic was clean. Meetings interrupt deep work, async messages can be answered when convenient, and tools like Slack and Notion make it easy to document decisions in writing. Remote work forced the experiment, and many teams declared it a success almost immediately.
One of those teams was at a mid-sized SaaS company building developer tooling. They had about 40 engineers across three time zones, and they went from a meeting-heavy culture to an almost aggressively async one. Standups moved to a Slack bot. Design decisions moved to Notion threads. Code reviews were handled through GitHub comments with no expectation of real-time response. The calendar cleared out. Engineers reported feeling more in control of their time.
For about six months, productivity metrics looked good. Velocity (the rate at which the team completed planned work per sprint) was up. On-call incidents were down. Leadership pointed to it as proof that fewer meetings meant more output.
Then the wheels came off a major feature release.
The Setup
The team was building a significant infrastructure change: migrating their authentication service from a legacy session-based system to a token-based approach using JWTs (JSON Web Tokens, the standard way of encoding identity information in modern APIs). It was the kind of project where multiple teams needed to stay coordinated because the authentication layer touched almost everything.
In the async model, coordination happened through a Notion doc that was supposed to be the source of truth. Engineers would update it when they made decisions, and others would comment asynchronously. It felt very orderly.
What actually happened was that the doc forked. Not in a version-control sense, but cognitively. The backend team made a decision about token expiry windows late on a Tuesday. They posted it in the Notion doc. The frontend team, who were eight hours ahead in a different time zone, had already started implementing against different assumptions that morning, based on an earlier comment thread in Slack that nobody had thought to reconcile with the Notion doc.
Neither team was careless. Both teams were doing exactly what the async system asked of them: posting updates, reading threads, making progress without waiting on each other. The problem was that async communication has a fundamental property that’s easy to forget: it doesn’t preserve shared state. Every participant is operating from their own local snapshot of reality, and those snapshots drift.
What Happened
The divergence wasn’t caught until integration testing, two weeks before the scheduled launch. By then, the frontend team had built UI flows that assumed tokens would refresh on a 15-minute cycle. The backend had shipped an implementation that refreshed on a 60-minute cycle with a 5-minute grace period. These aren’t compatible. The user experience would have been session drops that appeared random but were actually deterministic, the worst kind of bug because users blame themselves before they blame the software.
Fixing it required a week of coordinated rework, a postmortem that surfaced four other smaller misalignments in the same project, and a rushed two-day sprint to reconcile the documentation. The launch slipped by three weeks.
The cost wasn’t zero. It was just deferred and then multiplied.
Here’s the thing that makes this instructive rather than just a cautionary tale: the team’s async tooling worked exactly as designed. Slack delivered the messages. Notion stored the decisions. GitHub tracked the reviews. Nothing failed technically. What failed was the assumption that removing synchronous coordination also removes the need for shared context.
Why It Matters
Async communication is genuinely better than meetings for a lot of things. Status updates, decisions that don’t require back-and-forth, documentation, code review on non-blocking changes. For those use cases, it respects everyone’s time and creates a written record that meetings rarely produce.
But there’s a category of coordination that async handles badly, and it’s the category that tends to matter most: decisions with high dependency density. When the output of your work becomes the input to someone else’s work, and the constraints are evolving, and people are in different time zones, async communication doesn’t eliminate the coordination cost. It just makes that cost accrue invisibly until you hit an integration point.
This is related to why notification overload creates its own kind of cognitive tax. The medium that feels lower-friction in the moment often generates more total friction across the system.
The authentication migration failure is actually a good illustration of what distributed systems engineers call eventual consistency, which is the property where all nodes in a system will eventually agree on the same state, but might disagree in the interim. Distributed systems handle this with explicit reconciliation mechanisms: vector clocks, conflict-resolution rules, designated sources of truth with enforced write patterns. Teams running async-first communication rarely build those mechanisms. They assume the Notion doc is the source of truth without enforcing that it actually is.
Basecamp, the company that arguably did more to evangelize async communication than anyone, has thought seriously about the tradeoffs of that model. Their approach works in part because they’ve built explicit norms around what warrants a Hey notification versus what can wait, and they’re a company whose product literally is a communication tool, so they’ve spent years refining those norms. Most teams adopting async borrow the philosophy without building the infrastructure.
What We Can Learn
The engineering team in this story eventually did something sensible: they built a decision log with explicit ownership. Any decision that crossed team boundaries had to be logged with a named owner, a deadline for feedback, and an explicit “decision finalized” marker. When a decision was finalized, the owner was responsible for proactively notifying downstream teams, not just posting in Notion and assuming someone would see it.
They also added a weekly 30-minute sync specifically for surfacing cross-team dependencies, nothing else. Not a status update, not a demo, not a general check-in. Just: what are you building this week that touches something I’m building, and are our assumptions aligned?
The insight is that async communication requires more discipline than synchronous communication, not less. A meeting is self-synchronizing almost by definition: you’re in the same room (or video call), you react to each other in real time, and shared misunderstandings surface quickly because someone will usually say something that exposes them. Async threads don’t do that. They require you to proactively surface the misunderstandings that would have been caught automatically in a real-time conversation.
This doesn’t mean async is wrong. It means async is a tool with a specific profile of strengths and failure modes, and those failure modes cluster around high-dependency work with evolving constraints. If you adopt async-first without accounting for that profile, you’re not reducing coordination overhead. You’re deferring it, and deferred coordination costs tend to compound the same way deferred technical debt does: quietly, then suddenly.
The team that shipped JWT authentication three weeks late didn’t need more meetings. They needed better consistency mechanisms, clearer ownership, and a way to ensure that decisions propagated to everyone who needed them before anyone built something dependent on a stale assumption.
That’s an engineering problem. And like most engineering problems, it has a boring, disciplined solution that works better than the elegant theory that preceded it.