
The question that kept me up wasn’t about databases or scaling, but what the actual universal interface is. In 2017, I interviewed at Investec with Jamie McCrindle and Johnny Reilly. I confidently pitched them on what I believed was the ultimate stack: “DynamoDB + strong types + GraphQL = universal interface = infinitely scalable app.”
They weren’t impressed. They politely challenged it, and while I defended my position, deep down, a seed of doubt was planted. Nine years later, having built and scaled systems from Next.js to AWS and finally to Cloudflare Workers, I finally understand why they weren’t buying it.
Why GraphQL Felt Universal
At the time, GraphQL checked every box on paper. It gave us type safety, a single endpoint, introspection, and a schema-first approach. It felt like the definitive answer to connecting frontends with backends. We could query exactly what we needed.
But it missed the point entirely. It wasn’t about the query layer. GraphQL is strictly a data layer—a way for machines to format data for other machines.
The AWS Migration and the Complexity Tax
As our platforms grew, we migrated from Next.js to AWS seeking more flexibility. The cost improved initially, but the complexity exploded. We were dealing with Lambda cold starts, DynamoDB capacity planning, intricate CloudFormation templates across three regions, and nearly two years of pure infrastructure work.
The “infinitely scalable” dream came with an exorbitant complexity tax. We were spending more time orchestrating infrastructure than building features.
The Cloudflare Pivot
Then came the pivot to Cloudflare Workers. We moved everything to the edge. The change was profound: zero cold starts, global distribution by default, D1 for our data, and Durable Objects for real-time state.
Costs dropped further, but more importantly, the complexity collapsed. The platform became simple again.
What Jamie and Johnny Were Right About
Looking back at that interview, I realize what Jamie and Johnny sensed: GraphQL wasn’t the answer. They couldn’t have told me what the actual universal interface was — nobody could have in 2017. AI agents and the Model Context Protocol didn’t exist yet. But their skepticism was well-placed. GraphQL is a data layer. It solves querying. It doesn’t solve collaboration.
Nine years later, I think I finally know what the universal interface actually is. It’s Chat — the way humans (and now agents) naturally communicate. When you add MCP into the mix, you get a tool layer that lets those conversations drive real actions.
Together, Chat + MCP = the actual universal interface.
Introducing spike-chat
This realization led directly to building spike-chat, a standalone Cloudflare Worker that provides real-time messaging for the entire spike.land platform.
The architecture is built on the very primitives that made our platform simple again:
- Channels, threads, and DMs all natively supported via D1 and Durable Objects.
- Agent-as-first-class-citizen: AI agents operate within the chat seamlessly, responding to events and executing tools.
- Any app on our platform can enable this chat with a single line of configuration.
Here’s a live demo. We’ve embedded a public channel right here in the blog post. Anyone, including visitors, can post (with rate-limited guest access). Try it out and say hello.
import { SpikeChatEmbed } from "@spike-land/core/block-website/ui/SpikeChatEmbed";
<SpikeChatEmbed
channelSlug="blog-universal-interface"
workspaceSlug="spike-land"
guestAccess={true}
height={500}
/>
The DX That Actually Impresses
The developer experience (DX) is what truly matters. Instead of setting up complex OAuth apps, managing scopes, tokens, and event subscriptions, enabling chat in a workspace app looks like this:
{
"chat": {
"enabled": true
}
}
That’s it. On the frontend, the planned API for connecting to a channel is a single React hook:
const { messages, sendMessage } = useChatMessages({ channelId: "general" });
It’s the simplicity we were always chasing but failing to achieve with just a query language.
Jamie, Johnny — I finally have the answer to your question. And this time, the interface really is universal.