Programming is about mental stack management
The performance of large language models is, in part, constrained by the maximum size "context window" they support. In the early days, if you had a long-running chat with ChatGPT, you'd eventually exceed its context window and it would "forget" details from earlier in the conversation. Additionally, the quality of an LLM's responses will decrease if you fill that context window with anything but the most relevant information. If you've ever had to repeat or rephrase yourself in a series of replies to clarify what you want from ChatGPT, it will eventually be so anchored by the irrelevant girth of the preceding conversation that its "cognitive ability" will fall off a cliff and you'll never get the answer you're looking for. (This is why if you can't get the answer you want in one, you're better off editing the original message as opposed to replying.)
Fun fact: humans are basically the same. Harder problems demand higher mental capacity. When you can't hold everything in your head at once, you can't consider every what-if and, in turn, won't be able to preempt would-be issues. Multi-faceted tasks also require clear focus to doggedly pursue the problem that needs to be solved, as distractions will deplete one's cognitive ability.
These two traits—high mental capacity and clear focus—are essential to solving all but the most trivial computer programming tasks. The ability to maintain a large "mental stack" and the discipline to move seamlessly up and down that stack without succumbing to distraction are hallmarks of many great programmers.
Why is this? Because accomplishing even pedestrian tasks with computers can take us on lengthy, circuitous journeys rife with roadblocks and nonsensical solutions. I found myself down an unusually deep rabbit hole this week—so much so that I actually took the time to explain to Becky what yak shaving is, as well as the concept of pushing dependent concepts onto a stack, only to pop them off later.
The conversation was a good reminder that, despite being fundamental to everyday programming, almost nobody talks about this phenomenon—neither the value of mastering it as a skill or its potential impact on one's stress level and mood. (Perhaps that's because, once you've had this insight, there's just not much reason to discuss it in depth, apart from referencing it with shorthand like "yak shaving".) So, I figured it might be worth taking the time to write about this "mental stack management", using this week's frustrations as an example with which to illustrate what I'm talking about. If you're not a programmer and you're curious what it's like in practice, you might find this illuminating. If you are a programmer, perhaps you'll find some commiseration.
Here goes.
Why it takes me a fucking week to get anything done
The story starts when I set out to implement a simple HTTP route in POSSE Party that would return a JSON object mapping each post it syndicates from this blog to the permalinks of all the social media posts it creates. Seems easy, right?
Here's how it actually went. Each list item below represents a problem I pushed onto my mental stack in the course of working on this:
- Implement the feature in 5 minutes and 10 lines of code
- Consider that, as my first public-facing route that hits the database, I should probably cache the response
- Decide to reuse the caching API I made for Better with Becky
- Extract the caching functionality into a new gem
- Realize an innocuous Minitest update broke the m runner again, preventing me from running individual tests quickly
- Switch from Minitest to the TLDR test runner I built with Aaron last year
- Watch TLDR immediately blow up, because Ruby 3.4's Prism parser breaks its line-based CLI runner (e.g.
tldr my_test.rb:14
) - Learn why it is much harder to determine method locations under Prism than it was with parse.y
- Fix TLDR, only to find that super_diff is generating warnings that will definitely impact TLDR users
- Clone super_diff's codebase, get its tests running, and reproduce the issue in a test case
- Attempt to fix the warning, but notice it'd require switching to an older way to forward arguments (from
...
to*args, **kwargs, &block
), which didn't smell right - Search the exact text of the warning and find that it wasn't indicative of a real problem, but rather a bug in Ruby itself
- Install Ruby 3.4.2 in order to re-run my tests and see whether it cleared the warnings super_diff was generating
At this point, I was thirteen levels deep in this stack and it was straining my mental capacity. I ran into Becky in the hallway and was unnecessarily short with her, because my mind was struggling to keep all of this context in my working memory. It's now day three of this shit. When I woke up this morning, I knew I had to fix an issue in TLDR, but it took me a few minutes to even remember what the fuck I needed it for.
And now, hours later, here I am working in reverse as I pop each problem off the stack: closing tabs, resolving GitHub issues, publishing a TLDR release. (If you're keeping score, that puts me at level 5 of the above stack, about to pop up to level 3 and finally get to work on this caching library.) I needed a break, so I went for my daily jog to clear my head.
During my run, a thought occurred to me. You know, I don't even want POSSE Party to offer this feature.
Well, fuck.