The Teapot That Punishes You
A masochist’s teapot has the spout and handle on the same side:

You will burn yourself because it makes errors inevitable.
This is the software equivalent of masochist’s teapot:
function formatName(user) {
// What if user is undefined?
// What if firstName is null?
// What if lastName is empty string?
return user.firstName + ' ' + user.lastName;
}
It invites mistakes. And just like with the teapot, the fault lies not with the user, but with the design.
So I propose a new type of design thinking: error-aware design.
Error-Aware ≠ Error-Handling
Most engineers think about errors like this:
- Write code
- Add error handling
- Document edge cases
Error-aware design flips this:
- Design out preventable errors
- Make remaining errors obvious
- Handle what’s left gracefully
The Cost of Bad Design
That innocent-looking API is costing you:
- Debugging time: “Why did this succeed but fail later?”
- Integration headaches: “But the docs said…”
- Production fires: “We didn’t know that could happen”

Core Principle: The Error That Never Was
The best error handling is eliminating entire classes of errors:
- Make invalid states unrepresentable. Like a teapot that physically can’t burn you.
- Fail fast and loud. Like a teapot that screams when overfilled.
- Guide users to success. Like a teapot with an obvious fill line.
// This is Typescript instead of JavaScript
interface User {
firstName: string;
lastName: string;
}
function formatName(user: User): string {
return `${user.firstName} ${user.lastName}`;
}
Up Next: The Toolkit
The next post arms you with concrete patterns of error-aware design. In good design, the right way should be the only way.
Challenge: Look at your last production incident. Could it have been designed away at the API level?
- The Philosophy of Error-Aware Design