Algorithms to Live By · ch.12 · computational kindness
🤝 Chapter 12 · Computational kindness

Computational kindness:
make it easier to decide

Every one of the last eleven chapters was about your own hard problems — your searches, your queues, your regrets. This one is about the problems you hand to everyone else, usually while believing you're being nice, and how to stop doing that.

Eleven chapters of this book have been advice for you: when to stop looking, when to try something new, how much to tidy, what to forget, when to think less, when to give up gracefully. All of it assumed the hard problem was yours.

This chapter turns the camera around. Most of the hard problems in your life were handed to you by someone else — often by someone being polite. And most of the hard problems in other people's lives, you handed to them the same way. That's the last algorithm: notice when you're doing it, and stop.

1“Wherever you like!”

Someone asks where you want to eat. You say: “Oh, anywhere — you pick.” You feel good about this. You've just been flexible, easygoing, generous. You've given them the whole city.

That is exactly what you've done, and it is not a gift. Consider what they now have to do. They have to search every restaurant in town — that's the space you just opened. They have to guess your preferences, which they cannot observe, from evidence you declined to provide. And if they guess wrong, the mistake is theirs: you get to be disappointed, having risked nothing. You didn't give them freedom. You gave them an unbounded search, an inference problem, and the liability.

The tell is the asymmetry. Saying “anywhere” took you a second and a half. Answering it takes them ten minutes of quiet dread. You didn't reduce the work — you moved it. You outsourced your compute and called it politeness.

There's a name for this move, and it's the name of the chapter: it's computationally unkind.

Interactive · what “anywhere” actually costs Click the chips · watch the search collapse
Options to consider
60
Expected decisions
59
Their time
0:00
Kindness score
0%
No constraints. They're searching the whole city, and they still don't know what you like.
🙃
Notice what happens with zero chips lit: the searcher never finishes. It isn't that the problem is slow — it's that there's no stopping rule, because you never gave them one. Chapter 1 taught you to look at 37% and then leap. But 37% of what? An unbounded set has no 37%. You didn't just make their problem big; you made it ill-posed.

2Kindness is constraint

Here's the kind version of the same sentence: “I could do Thai or the pizza place, and I can't do after 8.”

Count what just happened. Two options and a bound. Their search went from thousands to two. Their inference problem — what does this person actually want? — is gone, because you told them. Their blame risk is gone, because you pre-approved both answers. And the whole thing cost you six extra words.

The instinct that stops people doing this is that constraints feel like a taking. You're narrowing their choices; surely that's the opposite of generous? But look at what you actually removed. You didn't take their freedom — nobody wanted the freedom to send you somewhere you'd hate. You took their work.

Chapter 7 said this in a different accent: a constraint is a regularizer. Fewer parameters, less overfitting, better answers from less thinking. Chapter 8 said it again from the other side — the hardest thing about a hard problem is usually one constraint you can't drop. Here it's the same fact, pointed at another person: every constraint you supply is a dimension they don't have to search.

When you make a request, hand over the constraints — not just the goal.

“Can you look at this?” is a goal. “Can you look at the retry logic in Sync.scala, before Thursday, and I only care about whether it can double-send?” is a goal with its constraints attached. Same request. One of them is a research project; the other is twenty minutes.

🎁
The gift test: if your request would take you longer to answer than to write, you're about to be unkind. Write for a few more seconds. The exchange rate is absurdly in your favour — a second of your specificity buys minutes of their search.

3The tyranny of the bulging inbox

“Just a quick thought — no rush!”

Read that again as a scheduler would. It's a task with no deadline, no priority, and no cost to the sender, and it has just been enqueued in someone else's day. “No rush” sounds like a kindness. It is the removal of the one field that would have let them plan.

Chapter 5 was blunt about this: a queue with no priorities cannot be scheduled. Every scheduling algorithm worth anything — earliest due date, shortest processing time, weighted anything — needs to know what things are worth and when they're due. Strip those out and the receiver has exactly one legal policy left: first-in-first-out, in the order the senders happened to type. Which is to say, your day is sorted by other people's reflexes.

Chapter 10 named the other half. A buffer that accepts everything and never drops isn't generous, it's bufferbloat: latency climbs without bound while throughput stays flat, and the fix — the only fix — is to drop things sooner and more honestly. An inbox is a buffer. “I'll get to everything eventually” is bufferbloat with a nice personality.

And the asymmetry compounds. Seconds to send, minutes to process, multiplied by everyone who has your address. There is no volume of politeness that makes that arithmetic work.

Interactive · two inboxes, same requests Only difference: what the sender bothered to include
Lane A — “no rush!” · FIFO · nothing ever dropped Lane B — deadline + priority stated · EDD · drops the hopeless early
3
Lane A
“no rush!”
Completed
Avg wait
Never done
Lane B
stated bounds
Completed
Avg wait
Never done
Press run. The two lanes get the identical stream of requests.
📬
The two lanes receive the same requests, in the same order, needing the same work. Lane A pays a two-tick surcharge per task to reconstruct what the sender left out, and can't reorder or refuse anything. Lane B knows what's due when, does the urgent ones first, and says no early to the ones it can't make. Lane B finishes more, waits less, and abandons fewer — and it isn't working harder. It was just told more.

4Design for the person doing the work

Go stand at a bus stop with a live countdown. Next 6 arrives in 4 min. Now go stand at one without. Same route, same bus, same driver, same traffic. The bus is not one second faster. The second stop is misery and the first one is fine.

Chapter 6 explains why, and it's not a mood. An unpredictable wait is memoryless: the bus you've been waiting nine minutes for is no more due than the one you've waited one minute for. Your best guess never improves. You cannot leave, cannot get a coffee, cannot commit to anything — because the answer to “how long?” is always “who knows, could be now.” The countdown doesn't speed anything up. It converts an open-ended wait into a bounded one, and a bounded wait is a thing a human can plan around. The information is the kindness. The schedule never changed.

Once you have this lens, the world sorts itself into the kind and the unkind rather quickly:

  • Airline boarding. Optimised beautifully — for the airline. Zones, groups, a fixed departure slot. For the passengers it's a scrum of a hundred people each solving a private optimisation about overhead bins, in a corridor, standing up. The airline's cost went down and everyone else's went up. That's not efficiency, that's a transfer.
  • Forms. “Please attach any relevant documents” is a search problem with an unknown target. “Attach a payslip from the last 3 months (PDF, under 5 MB)” is a fetch. The second form is not more restrictive; it's the only one that can be finished.
  • Calendar invites. “Let me know what works!” asks someone to search a two-dimensional space against a calendar they can't see. Three concrete slots is a multiple-choice question. Notice you didn't lose anything — they can still say “none of these.”
  • Pull requests. “Review when you get a chance” on 40 files is a scavenger hunt. “The real change is in the retry loop; the rest is a rename” is a review. Same diff. One of them respects the reviewer's cache (Chapter 4) and one of them evicts it.
  • Restaurant menus. Six dishes you can decide between beats sixty you can't. A short menu isn't a limitation of the kitchen; it's a service to the table.

The pattern is always the same shape. Kind design gives people a smaller search space. Not fewer good outcomes — a smaller space. Those are different things, and confusing them is why so much well-meant design is exhausting.

🚌
The cheapest kindness available to any system is telling people how long. Progress bars, queue positions, “we'll reply within two days,” the little countdown at the bus stop. None of it makes the work go faster. All of it converts a memoryless dread into a number, and a number is something a person can live around.

5Recursion in the room

“Where do you want to go?” “I don't mind, where do you want to go?” “Honestly, whatever you want.” “No really, you choose.”

You have both been extremely polite and you are both still standing on the pavement. This is Chapter 11's infinite regress, performed live at dinner: I'm modelling your preferences, which include a model of my preferences, which include a model of your model of my preferences, and the recursion has no base case. Neither of you is willing to be the one who terminates it, because terminating it feels like taking, and taking feels rude. So the stack grows until someone gets hungry enough to crash it.

The joke every couple knows is that the “selfish” person in this conversation is the one who ends it. That's exactly backwards. The person who says “I want the pizza place” isn't being pushy — they're providing the base case. They've taken the recursion out of the room and replaced it with a fact. Everyone else is now free to disagree cheaply, which is the only kind of freedom anyone actually wanted.

Stating a preference is not the opposite of deferring. It's the thing that makes deferring possible. You cannot say “sure, that works” to an infinite regress.

♾️
Programmers have a professional intuition for this and mysteriously drop it at the restaurant door. You would never ship a recursive function without a base case and call it “flexible.” Say the preference. Terminate the recursion. You can always be talked out of it — that's a one-line conversation. The regress isn't.

6The best answer is sometimes “good enough, quickly”

Here's the thesis of the entire book, stated plainly, with nothing up its sleeve.

The point of all this was never to compute optimally. Half the problems in these twelve chapters are provably intractable — nobody is solving them exactly, not you, not a data centre, not ever. The other half are perfectly solvable and simply don't deserve it: the optimal restaurant is not worth forty minutes, and the optimal sock-drawer sorting is worth exactly zero minutes.

What you actually got was a way to tell which problem you're in. That's the skill. Is this a one-shot with no going back, or can I revisit (Ch.1 vs Ch.2)? Will I search this often enough to justify sorting it (Ch.3)? Is my data good enough to support the model I'm about to fit (Ch.7)? Am I stuck in a local optimum that only a bad move escapes (Ch.9)? Knowing which problem you're in tells you where thinking pays — and, far more usefully, where it doesn't.

Which brings us to the sentence the whole book has been walking toward:

The cost of computing is part of the problem, not outside it.

This is the move that changes everything. A decision procedure that ignores its own price isn't rigorous — it's incomplete, in the same way a budget that leaves out rent is incomplete. The forty minutes you spent optimising dinner were forty real minutes. They came out of the same account as the meal. An answer that costs more to find than the difference it makes is not the better answer; it just has a nicer proof attached.

So: perfectionism isn't rigour. It's a modelling error — a failure to put your own thinking into the objective function. Rationality that doesn't price itself isn't rational, it's just expensive.

And the corollary is the whole of computational kindness: if the cost of thinking counts when it's yours, it counts when it's theirs.

⚖️
The test is one question, and it takes a second: does the better answer beat the cost of finding it? If the answer is no — and it is no far more often than anyone's ego would like — then the good-enough answer, computed cheaply, is the optimal one. Not a compromise. The actual optimum, once you account for the bill.

7Kindness, in types

You already know this chapter. You've known it for years — you just knew it about function signatures instead of about people.

Consider two APIs for dinner. One takes no arguments and returns a restaurant. The other takes a short list, a deadline, and a fallback. The first one looks lighter. It has fewer moving parts, no ceremony, nothing to fill in. It is also, on inspection, a hard problem in a trenchcoat: everything it doesn't ask for is something the caller must now invent.

Read def dinner(): Restaurant again and feel it in your spine. It promises a result and asks for nothing, which sounds like a favour and is the opposite of one. An unconstrained type is a hard problem you handed to your caller. Every field you left off the signature is a field they have to guess, and every guess they make is a bug you get to be disappointed about later.

Now look at what each field of Ask is doing:

  • options: NonEmptyList[Restaurant] — a bounded search, and the empty case is gone from the type. Nobody has to handle “what if there's nothing?” because you promised there isn't.
  • by: Instant — a deadline. That single field is the difference between Lane A and Lane B up there. It's what makes the request schedulable instead of merely present.
  • ifNoReply: Restaurant — the quietly kindest line in the file. A default means the receiver can decline to decide at zero cost. Silence becomes a legal, correct answer instead of a failure. You have made not-answering-you free.

This is what we've always meant by a good API, and it turns out we were describing manners. Make the constraints part of the request. Make the empty case impossible. Give it a deadline. Give it a default. Types are computational kindness with a compiler attached.

🧾
The generalisation, free of charge: every unstated requirement is a search you delegated. That's true of a function signature, a Jira ticket, a text message, and a request to your spouse — and the person on the other end pays for it in exactly the same currency, whether or not there's a compiler to complain.

8The twelve moves, together

So here's the whole book on one page. Not a table of contents — a cheat sheet. Twelve situations, twelve moves, one line each. The value isn't in memorising them; it's in recognising, mid-dither, which one you're in.

    Interactive · the map of the book Hover a node · click to go there
    🕸️
    The faint threads are the ones that kept surprising us. Explore/exploit and simulated annealing are the same idea at two temperatures. Sorting and caching are the same argument about whether order is worth its price. Scheduling and networking are both queue theory with different feelings attached. Priors and regularization are literally the same maths in two dialects. And relaxation and equilibria are both what you do when the exact answer is off the table. Twelve chapters, rather fewer ideas.

    9Be kind to your future self, too

    One last person to be kind to, and it's the one you treat worst.

    Your future self is a stranger who will receive everything you're currently deferring. They will not have your context — that's the whole point, context is exactly what doesn't survive the trip. And you hand them problems all day with a breeziness you'd never use on a colleague: the calendar entry that says only call, the commit message that says fix stuff, the file named final_v2_REAL.txt, the tab left open as a reminder of something you will not remember.

    Every one of those is “anywhere, you pick!” addressed to yourself. You saved four seconds. They'll spend twenty minutes reconstructing what you already knew and couldn't be bothered to write down. It's the same asymmetry, and you have somehow arranged for both sides of it to be you.

    The fixes are small and slightly embarrassing in their obviousness. The calendar entry with the phone number and the reason in it. The commit message that says why, since the diff already said what. The note at the top of the horrible function admitting that it's horrible and explaining which constraint made it that way. A default, so that Future You can decline to decide.

    None of this is self-care and none of it needs a candle. It's just the last application of the only rule this book has: the cost of thinking is real, so put it in the sum — including when the thinker is you, later, tired, on a Tuesday, with none of this in your head.

    Eleven chapters of algorithms for your own hard problems, and the twelfth one is: leave people less work than you found. Including that one.

    🕰️
    Six seconds is the going rate. “Call Dr. Weiss re: the referral — 555 0182, he asked for the scan date” versus “call”. Both of them fit in the calendar. Only one of them is a message from someone who liked you.

    10Check yourself

    3 questions · instant feedback 0 / 3