GeistHaus
log in · sign up

Jacob Brazeal

Part of wordpress.com

stories
Asyncio in Promise Terms
Uncategorizedasyncioconcurrencycoroutinefuturejavascriptpython
Let’s learn how asyncio works in Python by comparing it to Promises in Javascript. To be clear, this is for my own benefit:  I learned about Promises long before I knew any theory about coroutines, like any self-respecting frontend engineer in the 2010’s.  The lightbulb moment for me was this code snippet: const sleep = … Continue reading Asyncio in Promise Terms →
Show full content

Let’s learn how asyncio works in Python by comparing it to Promises in Javascript.

To be clear, this is for my own benefit:  I learned about Promises long before I knew any theory about coroutines, like any self-respecting frontend engineer in the 2010’s.  The lightbulb moment for me was this code snippet:

const sleep = ms => new Promise((res, rej)=>setTimeout(res, ms))

A promise triggers its handler — possibly including I/O or timeouts or library code — and the handler is passed “hooks” it can call to mark the promise as “ok” (resolved) or “failed” (rejected.) So, in the example above, the handler sets the timer, which calls the “succeeded” hook when the timeout finishes. Any callback hell can be papered over into a Promise, with a decidedly nicer async/await API.

When we write:

await sleep(1000)

… execution in the current control flow suspends until the promise is resolved or rejected with a return value or exception, and then we resume execution.  Javascript has always been event-loop driven, so queueing up bits of control flow is very intuitive.

Anyway, I am not here to explain Promises. I’m not claiming that they are easy to understand, just that they happen to be something that I understand, and that most frontend engineers understand, by necessity.

No, I’m here to write what I am sure is the 10,000th article explaining Python’s asyncio in terms of Promises. I am writing it, as I said, for my own benefit. Anyway:

All explanations of asyncio reference three primitives: coroutines, tasks, and futures, running on an event loop. I think this is eminently unreasonable – surely God intended us to learn only one abstraction! — but, nevertheless, you and I can no longer avoid learning what they mean. The bad news is that all three concepts are kind of like promises in slightly different overlapping ways.

The good news is 

  1. each of these ideas is somewhat simpler than a Promise and 
  2. Coroutines, tasks, and futures collectively compose into something that is broadly the same as a Promise.

My goal is to explain that mapping. 

The Part That Lines Up Nicely

At a high level, asyncio’s scheduling is handled by an event loop. Much like in a JavaScript runtime, the event loop has a queue of jobs to do. Each job runs on the current thread until it completes or yields control back to the scheduler because it hit I/O, await, etc. We are doing single-threaded cooperative multitasking like it’s 1963! Frontend engineers are familiar with the pain caused when one long-running task causes the UX to hang up, and we have learned tricks like  

await new Promise(resolve => setTimeout(resolve, 0));

to give the event loop a chance to run other tasks that might have queued up. Well, that’s precisely the kind of event loop we’re dealing with in Python. Now, because Python is a fancy language that also supports multithreading, it’s possible to have multiple different event loops running in parallel, but that is an abomination that should not be tolerated, in the opinion of this former frontend engineer. Let’s just pretend that there is one event loop. 🙂

Now then, something we haven’t made explicit is “what is an independent unit of control flow?” In JavaScript, each <script> block or UX event handler runs independently. Let’s call such units of control flow a Job, for lack of a better term. Each Job will run until completion or failure. If there is an exception, it will bubble up to the root of that Job, but will not, for example, terminate the entire JavaScript runtime. Once a Job starts running, it will hog the current thread until it’s done or yields control flow back to the event loop. If that happens, the Job suspends until the event loop resumes it again. Control flow for a given job executes in a serial. Control flow for separate jobs can interleave.

A Promise creates a new Job, with independent scheduling and error boundaries. Demonstration:

Look at that! Our top-level control flow has no idea that the Promise raises an exception; we continue on to log “bar”. However, we can await the Promise if we like:

Now the promise’s execution is part of our serial control flow, the error bubbles up, and execution aborts before we can log “bar”. [Note that if a Promise does not throw an error, then the initial handler does run immediately as part of the parent control flow.]

The corresponding concept in asyncio is a task. The corresponding demonstration looks something like:

Which outputs:

This has the same semantics as our first example: the task is its own “job” with its own control flow/walled-off exceptions. And, as you might expect, if we alter it just slightly to…

… then the error in foo does propagate to our top-level control flow, and we never print “bar”.

In short, the idea of “create a Promise object” is a lot like “create a task.” Both push an independent “job” onto the event loop. Both return a handle to the job that you can pass around and/or await.

Python Shows You How the Sausage Is Made

A Promise object has a fairly limited API. We can attach additional .then() and .catch() handlers, but we can’t really inspect or manipulate the internals. As the saying goes:

How do you synchronously determine the current state of a native Promise? You don’t. And honestly, maybe that’s a good thing, because if Javascript DID expose the internal state, the types and semantics would be kind of complex, right?

[Python has entered the chat] Hey, can I show you something?

The truth is, there’s a lot of machinery needed to create an abstraction like a Promise or an asyncio Task, and if you were to expose that machinery, it would look kind of like coroutines and futures. So, let’s take a look!

First, we should point out that there are two separate bits of magic going on in async/await in JavaScript. 

The first bit of magic is: A promise lets us asynchronously trigger handlers when it resolves. This was standardized in JS in 2015.

The second bit of magic is: When you are in an async function and call await promise, control flow in the calling function is suspended. The runtime waits for the promise to resolve and then schedules execution to resume. This was standardized in 2017.

What do we call this second bit of magic? The official name for functions that can cooperatively multitask with the event loop is a coroutine. Async functions are coroutines. 

Well, Python has coroutines, too! There are some minor differences in calling  convention::

async def foo():

    return 123

foo() # this doesn’t actually execute foo; it creates a coroutine object

await foo() # this suspends the current function, executes foo, and resumes  

In Javascript, if you call an async function without await, the result is more like asyncio.create_task(): an independent chain of control flow is triggered.

However, you can see this is purely a difference of syntax semantics. async functions are, in both cases, coroutines.

Now that we’ve touched on coroutines, let’s look at Python Futures.

One way to think about a Promise is as a pub/sub service: 

  • Create a handle: handle = new Promise()
  • subscribe to the handle: await handle / handle.then().catch() …
  • publish event: (reserved for internal promise handler)

The Promise API makes it easy to subscribe, but doesn’t expose a mechanism for publishing events. However, we can hack this together without too much trouble! Behold:

What have we got here? Well, we have created a new kind of Promise-like object that adds one API and removes another. 

  • What we’ve added: external callers can manually resolve our Future object via fut.signalSuccess(). We achieve this by intercepting the “hook” while constructing the promise inside Future. 
  • What we’ve removed: you can’t actually pass a handler to Future(). Unlike a Promise, this function doesn’t schedule any code for execution. It’s just a way to listen for  future accept/reject events.

Interestingly, just as you can implement a Future from a Promise, you can implement a Promise from a Future:

So, Promises and Futures are duals: just two ways to look at the same underlying abstraction. 

Python decided to give us Futures instead of Promises. (No big deal, we can just implement promises from futures!) A Future doesn’t execute code, but it does let coroutines subscribe to the resolve/reject state of the future. Tasks implement the API of futures so we can await and inspect the entire chain of asynchronous work kicked off by the task.

To recap:

  • A Javascript Promise is very similar to a Python Task: Each is an independent “job” scheduled on the event loop. Execution of coroutines within a job is serial, but coroutines from different jobs can interleave on the event loop. Errors bubble up to the top of a Task/Promise execution but then stop (unless some other coroutine is await’ing the failed task/promise).
  • A coroutine is the standard term for a function designed to yield/resume to the event loop. In JS we call it an async function. A Task/Promise will often consist of a tree of coroutines.
  • A Future is one way to implement the pub/sub signalling we know and love from JS Promises. Tasks are a kind of Future, since it’s interesting to signal them and await them.

On a final note, I’ll say that asyncio also implements many interesting features that the JS runtime doesn’t, like task cancellation and special kinds of locks. However, that is a topic for another day.

eephusthoughts
http://jacobbrazeal.wordpress.com/?p=982
Extensions
Looking back on Stripe’s payment API migration
Uncategorizedbitcoincryptocryptocurrencyfinancetechnology
This 2020 post walks through the first and second iteration of Stripe’s payment API. The original API was designed for credit card charges and was delightfully simple. You could move money with a single curl command! However, as Stripe added support for payment methods like Bitcoin, the flow became more complex and often required customers … Continue reading Looking back on Stripe’s payment API migration →
Show full content

This 2020 post walks through the first and second iteration of Stripe’s payment API. The original API was designed for credit card charges and was delightfully simple. You could move money with a single curl command! However, as Stripe added support for payment methods like Bitcoin, the flow became more complex and often required customers to perform a delicate choreography of webhooks and API calls. Stripe set out to improve the situation with its v2 PaymentIntent API, a years-long but triumphantly successful migration.

The Stripe blog post itself is more concerned with history than technical details. Actually, I don’t think it explains the workings of the APIs (or their differences) very clearly, so I’ll get to that later. The migration story itself goes like this:

  • v1 API was designed for credit/debit cards. Cards turn out to be the easiest payment flow, because charges (1) settle immediately and (2) do not require explicit action by the cardholder.
    • In fact, US debit/credit cards were the only flow with both those properties, out of the many domestic and global payout rails Stripe eventually added. 
  • The API began to evolve when Stripe added support for Bitcoin and ACH. Bitcoin is the opposite of credit cards: funds settle asynchronously and require secondary interaction from the bitcoin holder. This, too, is an unusual combination!
    • Most payout rails aren’t like credit cards or bitcoin.
    • So, the abstractions Stripe developed were highly path-dependent and based on “outlier” examples. 

Stripe spent months designing a new flow from scratch and then years rolling it out. The new flow was much more ergonomic for most payment flows. The only shortcoming was that it was a little more complex for the good old “just let me charge a credit card” flow, so they found a way to simplify that part too, and all was well™. It’s a great story!

What was less clear to me, from the post, was the real difference between the two flows, so I spent some time trying to work it out in more detail. 

The Bad Old Days

Let’s say you’re operating an online store, accepting payment via Stripe. You want to do the following:

  1. Get the user’s payout information.
  2. Charge their account.
  3. Wait for the charge to settle in Stripe’s bank account.
  4. Give your customers what they paid for. 

Let’s look at how steps 1, 2 and 3 work for credit cards vs. bitcoin.

CardBitcoinGet payout informationUsers upload their credit card info to Stripe using the stripe.js integration. Stripe sends back an encrypted Token. That way our store’s server never needs to see the sensitive credit card information.Users provide their public bitcoin address. Charge accountOur server asks Stripe to pull funds from the card details represented by Token. This action is represented by a Charge object.Stripe cannot directly initiate a transfer from the user’s bitcoin address. 
Instead, stripe.js asks the customer to initiate a transfer to a bitcoin address held by Stripe. The action/flow of requesting an action from the customer was represented by the BitcoinReceiver object (later known as Source.) 
Once the funds arrive in the escrow account, Stripe notifies the storefront account via a webhook/polling/etc and the storefront can finally initiate the Charge that will move funds virtually from the escrow account to the store’s Stripe balance.
If the store doesn’t initiate the Charge in time, then the transaction will be reversed, and funds will return to the customer’s account. Funds settleCard Charge events settle immediately. From the store’s perspective, a single synchronous request completes the transaction and confirms that it succeeded.The final Charge settles immediately, since it’s a virtual transfer.

The card flow is elegant and synchronous. The bitcoin flow is horrifying. If anything goes wrong (for instance, the store’s backend goes down and misses the webhook for BitcoinReceiver success) then the transaction will fail, even though (from the customer’s perspective) they successfully initiated a transfer from their bitcoin account. The store must track separate, asynchronous state machines for BitcoinReceiver and for Charge. The store needs to ensure that both the BitcoinReceiver and Charge flows are unique for each purchase to avoid double charges.  

I feel the need to repeat: the bitcoin flow is horrifying. It seems insane to model a payment as an asynchronous, multi-step process where part of the orchestration is farmed out to the store’s own server. However, I think we can piece together how Stripe got here:

  1. We (Stripe) have a nice Charge flow that allowed a store to initiate a payment from its server.
  2. The Charge() function requires a “root password” like a credit card number or ACH routing number. Bitcoin users don’t like sharing their private keys, so that basically blocks us from running Charge().
  3. Okay, how do we get around this? Well, if we could get customers to send funds in advance to a bitcoin account that we control, then we do have the “admin perms” to that account, so now Charge() will work.
    1. To quote the blog post: “As Bitcoin didn’t fit into our abstractions, we had to introduce a new BitcoinReceiver API to facilitate the client-side action we needed the customer to take in the online payment flow”.
  4. Of course, Charge() needs to be initiated by the store, so we’ll have to let them know when we have control of the funds (via webhook, etc) so they can finally call the function.
    1. Oops! Looks like sometimes stores don’t call the final Charge(). Well, we’d better return the customers’ bitcoin after a while, then!

I suspect that people were willing to go along with this because Bitcoin was already widely seen as very complex and distributed and so it just made sense that Stripe’s Bitcoin integration would likewise feel complex and distributed. Plus, Bitcoin was sort of a weird edge case, so it didn’t make sense to mess too much with Charge().

Of course, as Stripe began to support payout methods like Alipay and Wechat, required customer integration shifted from a weird edge case to a core flow. BitcoinReceiver was already well-established, so Stripe just abstracted it a bit further to a Source object that could collect funds from different kinds of customer accounts. Going forward, if the customer wouldn’t give you the root password to their account, you would just have them transfer funds to Stripe in advance, and then call Charge() once those funds settled.

Still, it’s a completely insane flow, or in the formal terms of Stripe’s blog post, “a conversion nightmare.”

The New, Glorious v2 API

So, why do storefronts actually need to initiate that intermediate Charge() action at all? Couldn’t Stripe itself auto-trigger Charge() as soon as the Source account is funded? The blog post doesn’t address this question directly. 

In fact, we don’t really learn about any of the technical tradeoffs Stripe considered while designing v2. Instead, Stripe lists meta-lessons from their brainstorming process like “Close laptops” and “Use colors and shapes.” I love it.

However, I think the core reason why Stripe couldn’t auto-trigger Charge() is because they didn’t trust customers to decide how much money to send. The BitcoinReceiver / Source flows were fundamentally triggered by untrusted frontend code. Only the server-side Charge() was in the control of the storefront. 

So, we need a new abstraction that allows stores to declare the charges upfront. Sort of a combination of Charge/Source that is created on the store’s server at the beginning of the payments flow. We need a unified state machine that waits for any customer-initiated transfers to settle, checks that they sent enough money, then auto-triggers the corresponding charge.

We need PaymentIntent.

As Stripe explains, this object provides a single asynchronous state machine that covers all aspects of a payment lifecycle. It can block on customer action, but then proceed automatically to initiating the charge if everything is in order. Stores still need to listen for webhooks when the final payment has succeeded, but not for intermediate states.

It’s kind of brilliant, honestly. The new flow is not only better for the new payment methods introduced since bitcoin — it’s a better flow for bitcoin, too! 

The only problem is that it is slightly less ergonomic for credit card transactions, which were previously synchronous. To get around this, Stripe has added some semantic sugar to the PaymentIntent API that basically lets you make it synchronous by erroring out if any asynchronous actions are required. This is the cherry on top for a very, very impressive API migration.

eephusthoughts
http://jacobbrazeal.wordpress.com/?p=972
Extensions
Review: Machines of Loving Grace
Uncategorizedaiartificial-intelligencechatgpttechnologywriting
This famous 2024 essay by Dario Amodei makes the case that “powerful AI” could bring wondrous benefits to society. Powerful AI is defined as a technology that can interact with the digital world in all the ways that a human can but is universally more competent and intelligent than any human, such as (for example) … Continue reading Review: Machines of Loving Grace →
Show full content

This famous 2024 essay by Dario Amodei makes the case that “powerful AI” could bring wondrous benefits to society. Powerful AI is defined as a technology that can interact with the digital world in all the ways that a human can but is universally more competent and intelligent than any human, such as (for example) a Nobel Prize winning scientist. What’s more, powerful A.I. will also be faster than humans, producing output at a 10-100x rate per unit of time, and we will be able to operate millions such A.I.s on the infrastructure that performed the original training. This essay is the source of the famous phrase “country of geniuses in a datacenter”. 

The main appeal of powerful A.I. is that it could dramatically accelerate scientific progress. Amodei models scientific progress as a series of incredibly important discoveries that currently happen at the rate of ~1x a year. In the realm of biology, that’s stuff like CRISPR gene editing, mRNA vaccines, new microscopy techniques, etc. If we imagine that we had another 75 such discoveries waiting for us in the 21st century, Amodei proposes that our A.I. geniuses could help us find them all within just 5-10 years. It is possible that A.I. could make discoveries at an even faster rate, although overall rate of progress is limited by non-genius factors like running physical experiments and managing regulatory issues. Still, with massive parallelization of research, we should get a lot of important discoveries in a very short period of time. 

The most obvious application of these A.I. discoveries would be to cure disease (of the mind and body.) Amodei notes that we have already made lots of progress in eradicating and slowing down various kinds of diseases, and it’s plausible that we would have solved almost all of those problems by the end of the 21st century anyway — what if A.I. did it even faster? It is even possible, Amodei says, that we could dramatically extend the human lifespan.  

This part of the essay is the most compelling, which is probably why it comes first. Dario’s prediction that we might have powerful A.I. as soon as 2026 seems basically correct, give or take a year or two; the skeptics were wrong. A.I. is smarter and faster than professionals at many types of knowledge work already, and seems on track to continue that progress for some time. And the application to “massively parallelizing scientific research, with an eye towards curing disease” seems quite plausible and good. 

If you were looking for the best argument for expediting superhuman AI, this is it. This is the argument, laid out by Dario himself. Internalize it.

The essay begins to drag on a bit as Dario dwells at length on more speculative benefits to the economy, climate change, inequality and democracy. In each case, one can just as easily see how a malicious or mishandled AI might instead make these problems worse, and Dario calls for some radical action like a coalition of democracies that tries to coerce authoritarian countries into new modes of government by withholding AI. 

This part of essay might also be the most important. Disregard the motivated reasoning that hopes that A.I. might cure all ills. Instead, look at the complicated ways that A.I. is going to interact with the world order, and consider Dario’s guesses as to how the world might shift to accommodate it. I think most A.I. related changes have been of this variety so far: not step change shifts, but complicated twists in the fabric of society that were perhaps predictable with hindsight. For example, academic assignments and remote interviews are basically meaningless now because A.I. can one-shot anything. I think we obviously will not see universally good or bad outcomes for democracy, the economy, inequality, and climate change. (Although on net, I think it will tend to make most of these worse.) Overall though, I think all of these areas will get much more complicated and introduce a raft of new second-order effects and policies to compensate.

Finally, Dario gets to the bad news, what we’ve all been worried about this whole time. If AI is better than humans at everything, what happens to our sense of meaning, and what happens to our livelihoods? 

On the loss of meaning: Dario is not worried about it. As he says, most of us aren’t the best at anything we do, and that doesn’t bother us. Meaning mostly comes from relationships anyway. I think there is a little sleight of hand here – Dario gives examples related to how he enjoys his part-time hobbies, but also implies that A.I. will take everyone’s jobs, and I think people get a sense of meaning from being useful that will not be so easy to replace. 

On the loss of jobs: Dario twists the knife. At first, A.I. might only be better at 90% of work, so people will figure out how to split the final 10% to get more leverage. But in the long run, he thinks A.I. will truly be better at 100% of knowledge work, to the point where humans simply have nothing to contribute. And while the famous comparative advantage essay points out that as long as there’s a shortage of A.I. to go around, we can still produce value doing the less important stuff, Dario also thinks this is a temporary state. Eventually, he says, there will be so much A.I. and it will be so powerful that there will simply be no role for humans in the economy. And in that world, we’ll have to shift to an entirely different economic model, perhaps universal basic income, perhaps something else (he does not project much enthusiasm for any of the options.) 

To be honest, I still find the idea that “A.I. will replace 100% of the economy” to be kind of unrealistic. Consider this thought experiment: suppose A.I. runs its own economy but the returns are not equally distributed (as is certain to be the case) so that most people are poor (as seems likely to me.) In this case, we won’t be able to buy what the A.I. is selling anyway, so we’ll return to some form of barter and build our own economy on the side. In the worst case I suppose we could end up in a Matrix- or Terminator- like society where we are hiding in caves from a society of malicious robots. Dario does not address this: he says he wants to avoid bringing too much science fiction into the discussion.

Every day I feel that the science fiction is already here.

eephusthoughts
http://jacobbrazeal.wordpress.com/?p=969
Extensions
Find what you’re missing at zombo.com
Uncategorizedbooksfamilyfictionlifewriting
We were approached on a sunny day by a charming stranger in a brilliant raincoat. “I would like you to make a radio advertisement,” said he, flashing white veneers and an impressive wad of cash. “A really, terrifically good advertisement.”  “What are we selling?” “Doesn’t matter. Something you might buy over the internet. Put in … Continue reading Find what you’re missing at zombo.com →
Show full content

We were approached on a sunny day by a charming stranger in a brilliant raincoat.

“I would like you to make a radio advertisement,” said he, flashing white veneers and an impressive wad of cash. “A really, terrifically good advertisement.” 

“What are we selling?”

“Doesn’t matter. Something you might buy over the internet. Put in whatever product or terms you like. If it’s a good advertisement, we can sell it. You have my word.”

We paused to consider it. We needed the money, and we did have the stranger’s word, offered with a gravity that was deeply reassuring. “O.K.,” we said. 

“Fine, fine,” said the stranger. “One last thing – the business is zombo.com.”

We began the next day in our dingy little recording studio.

Take 15

SMITH. I lost my job, I totaled my car, and my entire family was dying of cancer. That’s when I knew I had to try something different.

GREEN: Something different?

SMITH: Yes. A friend showed me zombo.com

GREEN: What is zombo.com?

SMITH: zombo.com helped me turn my life around by integrating zombo.com techniques into my daily life. It brings the everyday wisdom of the ancient Mayans into your home, paired with a deep understanding of holistic body work.

GREEN: That sounds amazing.

SMITH. Yes. Totally turned my life around. zombo.com. The peaceful place.

Cut

Take 27

SMITH: [chanting] zombo. Zombo. zombocom. Zombo. zombo. zombocom.

GREEN: Don’t knock it til you try it. Bake or boil it, stew or fry it.

SMITH: [in background, softly] zombo. Zombo. zombocom.

GREEN: Add our powder to your cooking. If you can’t see it, you’re not looking!

SMITH: zombo, zombo, zombocom

GREEN: Get that taste on your tongue, Keep on chewing til you’re done.

[TOGETHER]: Zombo, zombo, zombocom!

Cut

Take 50

GREEN: [whispers]: Did you get an invite?

SMITH: [urgently]: No. I’ve asked everywhere, everyone. You?

GREEN: Not yet. [scoffs] What do you expect? The whole world wants to join zombo.com. But I think I know how to get in.

SMITH: [astonished] How?

GREEN: Obama can get us in.

SMITH: [skeptically] You think Obama has those kind of connections?.

GREEN: It’s our best chance.

SMITH: I’ll write to him right away..

GREEN: We must join zombo.com, no matter the cost. 

Cut

Take 102

SMITH: [deep voiceover] Board games. Water polo. Excavators. The world’s largest turtle..

GREEN: There’s truly something for everyone at zombo.com.

SMITH: Space flight. The first robin in springtime. Dinosaurs. The approval of your parents. Paint.

GREEN: (surprised) Paint?

SMITH: Every color, base, and sheen.

[TOGETHER]  Find what you’re missing at zombo.com.

Cut

Take 565

SMITH: Close your eyes.

GREEN: [peacefully] Okay.

SMITH: Clear your mind.

GREEN: Okay.

SMITH: I mean, totally empty it. Forget everything you have ever known.

GREEN: Okay.

SMITH: Weep.

GREEN: [sobs for several minutes] 

SMITH: Join zombo.com.

GREEN: Okay.

—–

We never saw the stranger again, but we’re still looking for the perfect ad. Maybe you can write it. Send it to us here.

..

eephusthoughts
http://jacobbrazeal.wordpress.com/?p=956
Extensions
Cancer
Uncategorizedcancerhealthlifeself-care
2025 rolls in with the shadow of a pending biopsy. I forget about it for a little while. They make you wait so very long to confirm anything. I doubt that anything could be seriously wrong. What are the chances of that, at my age? The chances are low, but you only have to lose … Continue reading Cancer →
Show full content

2025 rolls in with the shadow of a pending biopsy. I forget about it for a little while. They make you wait so very long to confirm anything. I doubt that anything could be seriously wrong. What are the chances of that, at my age?

The chances are low, but you only have to lose once.

It’s melanoma, and it goes deeper than the biopsy. They say I have a 20% chance of stage 3 cancer and they need to operate right away. Perhaps I should not have taken all those twelve-hour hikes in the sun a few years back. Crumbs. Better text my family.

The surgeon tells me that my right ear is already five millimeters shorter than my left ear, and if they do the surgery this way, I’ll lose another five millimeters, but if they do it this other way, there will just be a soft spot. Heck, I don’t want my right ear to be down a full centimeter. Guess we should just go for the soft spot.

And suddenly I’m in the operating room, and they are playing Dreams by Fleetwood Mac for me, and just like that the lights go out.

***

I wake up, a little fuzzy, high on fentanyl. Amusingly, as I boot back up, I recite prime numbers and source code, mostly javascript and HTML. Apparently my soul is a webpage. Sarah puts Sandy on my lap.

Annoying, I must wait another week to see if I’m dying. They have to test the lymph nodes they cut out of my neck. It is horrible to wait like this. I spend my days reading PostgreSQL source code to distract myself. Embarrassing, after all, for one’s soul to be implemented in HTML.

I’m okay.

eephusthoughts
http://jacobbrazeal.wordpress.com/?p=946
Extensions
Advice for System Design Interviews
Uncategorizedcareerinterviewsjob-searchsystem-design
I would like to provide some advice for System Design interviews that is at odds with the guides provided by sites like HelloInterview and interviewing.io. I will start with the shortcomings of their method, and then I will tell you what I think you should do instead. These sites teach a prescriptive formula for system … Continue reading Advice for System Design Interviews →
Show full content

I would like to provide some advice for System Design interviews that is at odds with the guides provided by sites like HelloInterview and interviewing.io. I will start with the shortcomings of their method, and then I will tell you what I think you should do instead.

These sites teach a prescriptive formula for system design interviews. You just have to do functionalrequirementsnonfunctionalrequirementshighleveldesign! It’s so easy that you don’t even need to know anything about systems. (I mean no offense to the author of that blog post, whom I recall as an excellent career coach back in his Triplebyte days.) Hey! Here are some pithy remarks and segues to use throughout your interview.

It all seems very smooth and convincing.

However, first of all, it’s very obvious when someone is following one of these guides, and it’s especially obvious when they’re leaning on the formula as a crutch. The candidate is primarily concerned with checking off each box in their plan — wasting time and distracting from the actual task at hand.

Second of all, I think people often treat these guides more literally than they are intended. What the training videos meant as situational examples, people interpret as part of the inherent structure of system design interviews that needs to be repeated in every interview. For example, the other day a candidate began as follows:

  • Functional requirements (a few bullet points, not necessarily the most important ones)
  • Non-functional requirements (again somewhat random)
  • “And now, I’d like to address the CAP theorem.” Here the candidate announced that they would prioritize “availability over consistency,” although it wasn’t clear how we could evaluate such a decision, or what it applied to. But this was the point in the interview where Evan King discussed availability vs consistency in his Youtube system design tutorial.

Indeed, the candidate repeated so many remarks from Evan King that I felt as if I were participating in a fan fiction of sorts.

And look, I like the guy! I think he’s a great communicator, and his videos are very educational. But it’s still obvious when people are desperately parroting the form of his interviews. Perhaps he is a victim of his own success.

Okay, so what do I recommend that you focus on in system design interviews? Frankly, just two things:

  1. What am I being asked?
  2. What is interesting about this problem?

For some reason, candidates often procrastinate a long time before getting to the part of the problem I want to discuss. (And which I highlighted at the beginning of the interview.)  Candidates will spend fifteen minutes painfully enumerating APIs and database schemas before addressing the core problem, if they even get that far. They treat the seven stages of the HelloInterview prep guide as a rote prayer that must be recited at all costs.

Look, all I want is for you to talk about the interesting part of the problem. While you enumerate POST bodies and draw boxes that say “API gateway” — I’m just waiting for you to get to the point. What is the essential issue that captures why the system is not a basic CRUD app? You can always talk about the boring parts of the problem later, but odds are I won’t let you get there, because we are having such fun discussing the important part. 

To sum up: Don’t get stuck in an elaborate canned interview formula. Just confirm what the problem is and start exploring the most interesting part.

eephusthoughts
http://jacobbrazeal.wordpress.com/?p=940
Extensions
Judgment Day (Top of the First)
Uncategorizedaaron-judgebaseballmlbshohei-ohtanisports
GRAMP:  Hello folks. Welcome to an historic Opening Day, 2026. We are all excited to see the all-new Brooklyn Judges host the reigning champion L.A. Dodgers. I’m Alan Gramp and I will be your play-by-play announcer today. I’m joined by M.L.B.’s commissioner, Rob Manfred, and general counsel, Lara Wisch, as well as analyst Tom Tango. … Continue reading Judgment Day (Top of the First) →
Show full content

GRAMP:  Hello folks. Welcome to an historic Opening Day, 2026. We are all excited to see the all-new Brooklyn Judges host the reigning champion L.A. Dodgers. I’m Alan Gramp and I will be your play-by-play announcer today. I’m joined by M.L.B.’s commissioner, Rob Manfred, and general counsel, Lara Wisch, as well as analyst Tom Tango.

MANFRED, WISCH, TANGO: Hello!

GRAMP: We’ll also hear from somatogenetic kineticist John Hammock, owner of the Brooklyn Judges.

HAMMOCK: Great to be here!

GRAMP: We have a lot to talk about today, but let’s start with, um, with opening line-ups. For the L.A. Dodgers, batting first, the starting pitcher and reigning MVP, Shohei Ohtani. Batting second, the right fielder, Kyle Tucker. Batting third, the first baseman, Freddie Freeman. Batting cleanup, the catcher, Will Smith. Batting fifth, at shortstop, Mookie Betts. Batting sixth, in right field, Teoscar Hernandez. Batting seventh, playing third, Max Muncy. Batting eighth, center fielder Andy Pages. Batting ninth, the second baseman, Tommy Edman. 

TANGO: Tucker is a nice addition to last year’s second-best lineup by runs scored. 

GRAMP: Indeed. And now the lineup for the Brooklyn Judges. Starting pitcher, Lucas Giolito. Batting first, playing right field, Aaron Judge. Batting second, playing first base, Aaron Judge. Batting third, the catcher, Aaron Judge. Batting cleanup … I mean, look, it’s all Aaron Judge, alright. Hammock, can you explain how this works again?

HAMMOCK: I created nine genetically identical copies of Aaron Judge and started a new professional baseball team. 

GRAMP: Right. I don’t really understand it but I guess I accept it. Commissioner Manfred, can you provide some perspective here?

MANFRED: There’s a really strong market for Aaron Judge in New York City and we thought this seemed good for the sport.

GRAMP: Oh, that’s not really what I — what I meant was, some people have expressed, like, ethical concerns with this whole thing. Obviously that’s not how I feel, but for the fans, can you explain how you think about this?

MANFRED: Lara?

WISCH: We lost track of which Judge was the original, and they all were strongly in favor of playing in MLB, so this was really the best option.

GRAMP: That really makes sense, I think? Well, opening ceremonies have concluded, and Giolito’s on the rubber vs. Shohei Ohtani. He sets. The first pitch, a fastball — off the glove of Aaron Judge, all the way to the backstop, ball 1.

HAMMOCK: Frankly I expect we’ll see a lot of that today. We’ve asked Giolito to throw mostly fastballs.

GRAMP: Yes, I’ve been curious about that. The one-oh pitch – swing and a ground ball, left side — past a diving Aaron Judge into left. Leadoff single for Ohtani. So yeah tell us more about how you’re thinking about defense.

HAMMOCK: I just told the boys not to worry too much about ground balls. Judges aren’t really built for those, although it wouldn’t surprise me if they start fielding them eventually, they are very competitive. I think we should be able to get enough fly balls to get out of the inning eventually. 

GRAMP: That brings up Tucker. First pitch fastball – off the glove of Aaron Judge, down goes Ohtani to second. Big turn but he’s stopping there. Man, I’m kind of curious – Giolito’s OK with this? Might be rough for his ERA.

HAMMOCK: I told him forget about ERA, he can win 30 games here. We’re going to win every game this season. 

GRAMP: About that. The 1-0 pitch to Tucker is fouled back. Do you — maybe this one is for Tom, how do you think this team is going to do?

TANGO: Well, the Dodgers will probably be the toughest competition they’re going to have. I’ve been working on a model but there are a lot of factors it’s hard to estimate so far —

GRAMP: The 1-1 pitch comes in at 80 MPH, Judge makes the catch this time, first ball he’s caught all season. Looks like both he and Giolito are relieved. 1-2. The infield is playing everyone way back, practically on the outfield grass, it’s almost like we have seven outfielders. Well, ha, I suppose in a way we do. Giolito’s ready. Here it comes, a high fastball — there’s a fly ball, center field. Several Judges are converging — OK, someone’s got it, there we go, one out.

HAMMOCK: That’s honestly a relief. I — in transparency, we have had some long innings in Spring Training. 

GRAMP: Spring Training? No one saw your team in Spring Training. There’s a high fastball to Freddie Freeman, ball one. 

HAMMOCK: Well, I can’t say too much about that, but we held training games in our own facility.

GRAMP: … Wait, don’t tell me there are more Aaron Judges.

HAMMOCK: Well, as I said, I can’t speak to that. But we did hold training games.

GRAMP: All 7,000 people are on their feet in this sellout crowd here in Coney Island at MCU Park, former home of the Mets’ A-ball team. The 1-0 to Freeman is a curveball, SWING and a miss, and off the glove of Aaron Judge, down goes Ohtani to third. Giolito is visibly frustrated out on the mound.

HAMMOCK: No he’s not. I promise you, he’s not. He understands the deal, believe me.

GRAMP: Whatever you say. He sure looks upset to me, though.

TANGO: This is actually a shockingly high number of passed balls. Last year Agustín Ramírez led all of baseball with 19 passed balls. We might pass that up in a single game.

GRAMP: Out goes Judge to the mound for a quick discussion.  

TANGO: I’m running some quick numbers on this, and if there is a 50% passed-ball rate, an average offense scores about 11 runs per nine innings against average pitching. The Judges project to score about 12 runs per 9, but this could be a real issue for them.

GRAMP: I agree, this seems problematic. Freeman takes a strike. 

TANGO: At that rate, it might make sense for the pitching staff to start throwing soft-toss. Allowing seven or eight runs a game should be fine. In fact, and I’m sure I’m not the first person to suggest this, why are we not also pitching a Judge?

HAMMOCK: League regulations currently prohibit pitching a position player until we’re winning by 10 runs, but I absolutely plan to do this.

GRAMP: The 1-2 pitch is lofted to right field, Judge’s natural position. He looks so good out there. Drifts a little to his right, makes the catch, Ohtani will score easily, that’s one-nothing Dodgers, but two down.

TANGO: I need to hear more of the back story of this team. Just to confirm, you have the original Aaron Judge on this team, right? Which one is that? And how did you get him to agree to this?

HAMMOCK: I really can’t share internal details of our process for both legal and technical reasons, but it’s probably not giving away too much to say that I consider all of them the original Aaron Judge. In fact, that was really important to us, in order to ensure that individual offensive performances are all credited to the original Judge.

MANFRED: That’s — that’s still under discussion with the MLBPA, John.

HAMMOCK: Of course.

GRAMP: Quickly 2-0 on Will Smith, another ball rolls into the backstop. That last pitch clocked at just 71 MPH. I know we make this about Judge’s catching abilities, but I really feel like Giolito is a little wild right now. I’m kind of surprised L.A. is swinging at all. While we have you here, Rob, how do the Judges factor into expansion plans? I think we were all a little surprised to hear that a team was being added to the league so quickly. And how did you work things out with the Yankees?

MANFRED: I think we all recognized the unique opportunity we have here. Aaron Judge is not getting any younger. As to the Yankees – I think Lara can speak to this —

LARA: We exercised what is best described as  “eminent domain” to release Judge from his contract with the Yankees and, in exchange, to transfer Juan Soto and Francisco Lindor to them, while of course dissolving the New York Mets for competitive balance purposes. 

GRAMP: Yes, I meant to ask about that too. It’s all very, um, a lot to take in — swing and a FLY BALL, DEEP to right, oh, wow. Giolito really sort of served that one up. 2-0 Dodgers on a monster Will Smith homer.  Hahaha that was really something. Here’s Betts, coming off a bit of a down season, but still one of the absolute best players in the league. Um, there’s a — I’m not sure Giolito even really wound up. Was that underhand? Anyway, Betts pops it up, and it’s Giolito who calls off the entire infield to retire Betts and end the inning. Funny, sort of seemed like he didn’t trust the guys.

HAMMOCK: I can assure you that he has absolute faith in every Judge on that field. 

GRAMP: Two-nothing Dodgers, and we head to the bottom of the first!

<to be continued>

eephusthoughts
http://jacobbrazeal.wordpress.com/?p=931
Extensions
Why Are You Telling Me This?
Uncategorized
I. I have always depended rather heavily on conversational scripts. For me, this is not a specific list of things to say in a conversation, but rather a thematic approach to the interaction. The theme has varied over time. For instance: The issue with all of these approaches is that they are often unwelcome, a … Continue reading Why Are You Telling Me This? →
Show full content

I.

I have always depended rather heavily on conversational scripts. For me, this is not a specific list of things to say in a conversation, but rather a thematic approach to the interaction. The theme has varied over time. For instance:

  • “Ad-lib off the last thing that was said; say something interesting that seems somewhat related.” This is how I talked in college. Eventually I got the feedback that this made me a terrible conversationalist, and so I began exploring other themes…
  • “Express maximum sympathy and validation for whatever emotional content is included in the message.” This is how I approached difficult conversations at work and in my family, especially when the other party was discussing negative feelings about a person or situation, or perhaps a personal insecurity. 
  • “Reflect back whatever is being said in an active-listening style.” In recent years, this has been a default “script” I’ve used in many conversations. It has the advantage of focusing the conversation more squarely on what the other person is saying.

The issue with all of these approaches is that they are often unwelcome, a misread of the situation. It is jarring to receive advice when one wants sympathy, or to be vapidly affirmed when one needs help. But even if we treat people the way they want in a conversation, we might still miss the real point of what they are trying to tell us.

II.

The script I like to use now is, therefore, ‘What does this person want out of this interaction?’ and relatedly ‘what are they trying to tell me?’ 

What do you want out of this interaction? Are you looking for a casual chat? A shoulder to cry on? (very rarely!) advice? … I may already have an idea, from the historical precedent of my relationship with you — but do I really know what you want? The need for information vs. reassurance vs. advice etc. is so vast. And I won’t always know what you want, and I might need to ask; should ask, even, if the conversation is important. 

What are you trying to tell me? This is the more dynamic, and interesting, question to consider throughout the conversation. A mistake I have made many times is responding cheerfully to the last thing that was said. For instance, suppose you said to me:

“Poetry is really important to me and has shaped my worldview immensely. … The most significant poem for me is Frost’s A Road Not Taken.

I might have focused on the fact that you mentioned that you like poems, and said:

“Wow that’s cool, what are your top 10 favorite poems?”

This is an okay response, socially speaking, because at least I am keeping the focus on you, and nominally on the topic you brought up. But it completely misses the point of what you were trying to say. The point of mentioning Frost’s poem was not to express a broad enthusiasm for the poetic canon. The point was: you have some really meaningful beliefs, and this specific poem is deeply linked to them. You are, essentially, offering me the chance to dig into your core personal beliefs.

And so a much better response would have been:

“That’s really cool. What is so meaningful to you in that poem?” 

(As an aside: it might make sense to ask about your other favorite poems a bit later in the conversation; it’s certainly a related topic. But it is not the best way of digging into what you just said.)

In general, this approach has been a big help for me in conversations. When someone says a lot of words to me, it’s very easy for me to focus on a ‘subplot’, some detail or opinion that seems significant, and respond to that. But when I first ask myself, why are you telling me this, I often realize that the detail is actually a supporting example, or perhaps a slight tangent to the main point. But the point is often obvious if I think about it for a second or two, and I can attest that responding directly to it makes conversations flow much more smoothly, and seems to subtly relax people in a way I hadn’t noticed before. 

III.

Perhaps I should expand on how you might determine the point of what someone is saying. Here are a few things you might try tracking while they are speaking:

  • What is the original topic? (a person, place, thing, idea…)
  • What is the dominant emotion? (happy, angry, worried…) 
  • And what is that emotion “about”?
    • Was that emotion triggered by a specific event, or is it more of an abstract reason, supported by a number of examples?
  • How does each statement relate to your understanding of the main point so far? Am I currently hearing an example, a tangent, an explanation, etc.?
  • Why would someone say these things? Perhaps they are coping with a difficult situation? Or they want you to do something? Or maybe they’re trying to get at another, more difficult subject? Don’t take your interpretation here too seriously, but you should definitely try to form one.

You should try to confirm your understanding of the main point if it seems complex. If you instead push the conversation in a tangent, people will often roll with it, but you’ve missed the chance to really engage with them. If you ever get the sense that the two of you are talking past each other about different things, you might both be missing the point.

I think all of this analysis is easier with written text. There’s not a lot of time to process everything someone says in real time, and it’s easy (for my mind at least) to focus too much on what’s being said now, vs. how it connects to an underlying theme. Maybe this is why some of the smartest people I know tend to pause for a beat or two before they speak.

eephusthoughts
http://jacobbrazeal.wordpress.com/?p=919
Extensions
A thematic improvement to my bullet chess
Uncategorizedchessfictiongamessportswriting
I play bullet chess on lichess these days. My true skill level generally doesn’t change; ratings go up and down, mostly as a function of how focused and warmed up I am. Realistically, I would need to solve puzzles or review my games or get coaching (etc. etc.) to actually level up, and I know … Continue reading A thematic improvement to my bullet chess →
Show full content

I play bullet chess on lichess these days. My true skill level generally doesn’t change; ratings go up and down, mostly as a function of how focused and warmed up I am. Realistically, I would need to solve puzzles or review my games or get coaching (etc. etc.) to actually level up, and I know that. Still, here and there, I’ll make a little discovery that improves my game overall.

Well, I noticed a pattern today, made an adjustment, and just reached my all-time high bullet rating!

The pattern was: my opponents often castle on the opposite side from me. Since I almost always castle king-side, that means they’re frequently castling queen-side. That’s kind of weird, right — king side is by far the most popular — so why are my opponents doing this? I realized two things:

  1. I often prepare an early attack on the king-side, even before my opponent has castled. It makes sense for them to dodge it.
  2. I often castle very early – a habit I picked up from crazyhouse chess.

When my opponent castles queen-side, my burgeoning attack is thwarted, my pieces are out of place, and my opponent can start a really strong counter-attack. And with their king out of the way, they can bring a lot of pressure. My impression, over the years, is that castling queen-side is usually wrong because your opponent can often pick up a tempo or two in the counter attack. But in my case, I’ve already wasted so many tempi attacking the king on the wrong side that I can’t counter very well.

Anyway, my adjustment was: just wait to castle until my opponent has castled. This naturally leads me to delay any heavy attacks on a particular side of the board, as well. Suddenly, my opponents are no longer castling on the other side of the board from me, and my rating is at an all-time high. Neat! Best of all, this is a change I can make without any practice, as it’s just a thematic/strategic shift. An instant improvement. Probably this is not a universally correct heuristic, but it’s helping me avoid this particular losing theme.

It seems to me that AI might be able to analyze one’s games and discover broad thematic advice like this automatically. Surely someone has built this already.

eephusthoughts
http://jacobbrazeal.wordpress.com/?p=908
Extensions
Baseball, Pitcher Wins, and Life
Uncategorizedbaseballmlbpitching
I. Suppose that I insult you, and you get angry at me. Does that make you an angry person? Well, no, you say: one angry reaction doesn’t mean you’re an angry person. (Plus, you didn’t start it!) But what if you always react this way when people insult you? Well, how often does that happen? If someone … Continue reading Baseball, Pitcher Wins, and Life →
Show full content

I.

Suppose that I insult you, and you get angry at me. Does that make you an angry person?

Well, no, you say: one angry reaction doesn’t mean you’re an angry person. (Plus, you didn’t start it!) But what if you always react this way when people insult you? Well, how often does that happen? If someone insults you every day, and so you get angry at them every day, perhaps that makes you an angry person. But if you are only insulted, say, once a year, it’s a little harder to call you a (habitually) angry person. Is it necessary — is it even possible? — to separate who you are from what you do?

Perhaps we can agree that this exercise is a difficult metaphysical problem, made harder because of everyones’ subjective values around labelling people and around anger itself.

II.

Let us check in with the baseball statisticians. Their goal is simple — to separate a player’s true talent from their results. A batter hits a line drive at one hundred miles an hour – right at the third baseman. Was it a good at-bat? A pitcher gives up walk after walk, but is saved by a miraculous triple play. Did they pitch well?

In the long term, we trust that results mostly correlate with a player’s true talent level, but really those correlations can be obscured by countless variables. The quality of your opponent, the dimensions of the ballpark, an umpire’s bad eye.

The noisiest statistic is a pitcher’s wins. In the past, wins were revered, part of the “triple crown” of pitching statistics, along with strikeouts and ERA. You expected the best pitchers to win twenty games a year, and perhaps win three hundred in their career. 

Yet a pitcher might throw a very good game and get charged with the loss because his team didn’t score any runs.  And a good pitcher backed by a habitually weak offense will have an unfairly weak win-loss record. Famously, in 1974, John Matlack was the most valuable pitcher in baseball (according to modern sabermetrics) but due to a weak win-loss record, didn’t receive a single vote for the Cy Young. [0]

[0] https://www.baseball-reference.com/players/m/matlajo01.shtml

That is why, in recent times, a pitcher wins have become an unfashionable statistic. A reference to a benighted age when we didn’t really understand how to value pitchers. Who even looks at the win-loss record nowadays?

III.

But a pitcher’s win-loss record actually tells you a lot. Specifically, it helps you understand if the team actually tends to win when the pitcher is on the mound! What’s the alternative? Well, you could use modern sabermetrics to measure how good the starting pitcher REALLY is, and how good the offense is, (considering the size of the ballpark, and how fast the wind is blowing out), etc., etc., building a complex model that guesses who will win. 

…Or, you can just eyeball the pitcher’s win-loss record, and get a pretty good idea of how things tend to go for the team when that pitcher is on the mound. It’s not fair to the pitcher, especially if we “blame” the losses on the pitcher, but the win-loss record is still quite useful.

IV.

Suppose that I insult you, and you get angry at me every time. Does that make you an angry person?

Here are, I think, what baseball stats have to say about it:

1. We can’t judge you just by your reaction. Sure, you’re getting angry, but that must be viewed in light of your environment (which sounds particularly frustrating.) You might well be more patient than most people, even though you’re getting angry so often. We just don’t have enough information to make a complete value judgement about your behavior.

2. But we can judge the overall results — you’re getting angry a lot. When you’re the pitcher for your team, there’s a lot of emotional L’s going down. And that is a problem. Maybe there are no easy solutions, but we shouldn’t pretend that just because you’re patient (or not), the overall situation is OK. Maybe you shouldn’t be friends with me anymore since I keep insulting you.

In short — even if a habitual problem is not entirely your fault, it’s still a problem, and you should think outside the box to try to change it. 

❤

eephusthoughts
http://jacobbrazeal.wordpress.com/?p=889
Extensions