Exponential backoff with jitter is an effective retry strategy that helps avoid thundering herds, but most implementations only start applying jitter after the initial request fails. If you’re using a library which is implemented that way, then your workload might still be vulnerable to thundering herd problems. Exponential backoff is a retry strategy where failed requests are retried after…
There’s a subtlety in building AI agents that took me an embarrassingly long time to appreciate: your tool responses are prompts! So many tools and MCP servers treat outputs as mere data pipes: the model calls a function, the function returns a result or an error code, and that’s the end of it. But that mindset overlooks a powerful design opportunity. Tool responses are language , and that means…
When comparing REST and GraphQL, it’s not just about payload size, flexibility, or tooling. The real distinction is in how you design your API. REST is centered around resources, with endpoints like createUserGroup or getUserProfile . GraphQL shifts the focus to capabilities, encouraging more semantically rich mutations like groupJoin , which are disconnected from the underlying datastore and…
A few months ago Stripe announced their upcoming Order Intents API at Stripe Sessions. There’s a lot to like about this API, and if Stripe can maintain their usual quality bar it’ll be a big unlock for a wide variety of different use cases. They didn’t show too much of the API surface during the demo, but the parts we have seen feel a lot like Two Tap’s old universal cart API which I personally…
Networks are unreliable. Even when services are otherwise healthy transient failures like timeouts, dropped connections, and DNS blips can all cause your application to fail for no good reason. Sometimes that’s OK. If you’re building a simple system that only makes a few network requests then it’s reasonable to let these transient failures bubble up and let the caller decide how they’d like to…
I have some thoughts on the Google Cloud’s latest global outage! The incident report contains some interesting nuggets, if you look deep enough. For the most part it’s a pretty typical failure for a complicated distributed system. Google’s service for authorizing inbound API calls was missing handling for an edge case triggered while parsing empty configuration values, and hitting this case causes…
Webhooks are surprisingly hard to process correctly. Almost every software project I’ve worked on has needed to process webhooks from third party vendors, and almost every team I’ve worked with has missed one detail or other in the implementation. In this post I’ll go over some of the details that you should think about when designing an endpoint to process webhook events. Read more on…
Designing great resolver functions in GraphQL is quite unintuitive. Giving consumers the ability to describe their desired response payload means it’s possible for consumers to access fields via surprising paths through the graph, and this is something that can and should influence how you write your resolvers on a field level. Say you’re building a kanban board app like Trello or Asana. A board…
I’ve written about TypeScript enums in the past, and back then I encouraged the use of enums with string values instead of numeric ones due to the improved type safety and debuggability you get. This is still true—you should avoid numeric enums in TypeScript wherever possible—but I no longer think that you should reach for string enums as your first option. There’s an even better way of writing…
I typically describe my career as being the result of a lot of luck. There are so many key moments in my professional journey that just happened to go right for me while also being largely out of my control that it feels disingenuous to attribute the entirety of my achievements to my own skill. I’ve certainly put a lot of effort in to my craft, but identical inputs between two different people can…
I’ve been extremely fortunate over the course of my career, and I’ve been able to learn from some truly incredible people working in software. It truly does “take a village,” and some of the best general career advice I can give to anyone starting out is to find a really good mentor. And so it was that years ago I was peer programming a C# application with a more senior engineer. We needed to…
A common mistake in system design interviews is to underestimate a low but steady write rate. Many candidates quickly dismiss a write rate of 1 request per second as insignificant, but this isn’t always the case—especially in a startup environment where growth is measured week-over-week. Small write volumes can accumulate significantly over time and cause all sorts of downstream problems. During a…
Autofilling form fields has become an increasingly common and practical application of generative AI. With just a bit of context and a well-crafted prompt, AI can produce reliable results for various input fields. One of the most appealing aspects of this use case is its simplicity and quick implementation—developers can often build and deploy this feature without extensive training data,…
One of the powerful design patterns that can be employed within a GraphQL schema is the concept of collection lookups . This pattern allows API consumers to retrieve specific elements from a collection using singular fields, enhancing both usability and performance. To demonstrate this pattern, let’s imagine we’re building a blogging platform (perhaps for sophiabits.com!). The core data type we’ll…
Try as we might to design APIs that last, it is very difficult to completely avoid the need for breaking changes. The Sunset HTTP header offers a standardized way to inform clients about the deprecation of API endpoints in a programmatically interpretable way. In my previous post on this topic I discussed what this header is and why it’s important for managing API lifecycles—but didn’t explain how…