Playacting genius: the performative logic of reasoning from first principles
The first confusion is that a lot of the time what they call “reasoning from first principles” is more accurately “being a fast learning autodidact”. Going from zero to expertise in six months is not in any real way discovering a field by reasoning from first principles. They’ve just speedrun a field from a logical starting point. This is a major accomplishment, one that is enormously useful in the right context, as long as you are aware that this is what you did.
Anybody can reason from first principles or basic truths (if you are a regressive Aristotelian intent on ignoring all progress in human thought over the past two millennia). It’s generally useless because for any given field you just end up running into a series of “turkey” problems on a compressed timeline without the benefit of history.
Unsolicited advice
Was at a training once where the facilitator said “Unsolicited advice is criticism. Always.”
Half the room audibly gasped / objected.
The other half shouted a chorus of yes/thank you/ amen.
She offered no quarter to the “just being helpful” brigade. It was glorious.
The book of CP-System
If you want to learn about the hardware powering titles such as Street Fighter II, Ghouls’n Ghosts, or Final Fight, then “The Book of CP-System” is for you. Inside you will find the “Capcom System” (a.k.a CPS-1) explained in excruciating details, along more than one hundred explanatory drawings. The software is also covered with the description of the historical way of doing things and as well as a modern toolchain (CCPS).
Edge case poisoning
Now you can argue that the original model is wrong. After all, it doesn’t handle any of the edge cases! There are two “unfortunates” with this argument. First, it’s not necessarily true that the client will encounter any of these edge cases. Consider this model as part of a recipe API, where multiple groups are calling it for their own purposes. Alice needs recipes with inedibles, Barry needs recipes with optionals, and everybody else needs neither. Alice is “punishing” everybody else with her edge case. They have a more complicated API because of her. In turn, she’s “punished” by Barry’s edge case. While she needs an API that handles inedibles, she doesn’t need one that handles optionals, so her model is overcomplicated too.
Second, while the original model isn’t totally correct, it’s philosophically correct. It represents the most common case in a much more understandable way. Someone looking at the code base for the first time will more easily be able to understand what’s going on. If they then see an edge case, they can mentally model it as “the happy path except…”, a perturbation of the base model. If you instead present them with the final model, they’ll struggle much more to see the core idea in the forest of edge case handling.
I call this edge case poisoning. It’s almost impossible to avoid because anything dealing with the real world is going to have tons and tons of edge cases. You think handling time zones is hard? Time zones are only notorious because they are a real-world domain that we all have to deal with. A software engineer in Turkey who works in shipping probably doesn’t have to deal with the intricacies of confectionery recipes. But they will have to deal with time zones. Doesn’t matter if you’re dealing with time zones or recipes or logistics or art collectors, you will have edge cases, meaning you will be forced to take on the essential complexity.
Mystery knowledge
With two examples we should DRY things up. The abstract concept here is knowledge or skills that
You are unlikely to discover on your own, neither through practice and reflection nor by observing others apply it.
Once somebody tells you about it, you can easily learn and apply it.
Once you can use it, it immediately gives you significant benefits, possibly to the point of raising your expertise level.
This might be a studied topic, but if it is I don’t know even what field of knowledge it belongs to, much less what it’s called. In the meantime I call it mystery knowledge. Thinking less “whodunit” mystery and more “Greco-Roman mystery cults” mystery.
Theorycrafting
Another type of writing I’ve been thinking a lot about: “Theorycrafting”. This is informative content caveated with “not based on experience”. You just thought really hard about something, came up with ideas, and wrote them down. One example would be Decision Table Patterns. A lot of the techniques there aren’t things I’ve used in practice. I was just writing down techniques I thought could help in these cases. Pure theory.
I’ve always been a bit torn about theorycrafting. There’s an obvious problem that I don’t actually know if it works or not. Lots of things sound good in theory but fail in practice. Not only do I not know if it works, I have no information on whether it works. It’s just an idea I had. Is that worth sharing with people?
I guess it’s okay if I make it very, very clear it’s theorycrafting. Then people can read it and go “oh, this is purely an idea, he’s not advising it, just proposing it.” In return, I get a chance to talk about ideas, because ideas are interesting. There’s a lot more ideas I want to share than I can reasonably flesh out and test in practice. At least the idea gets to be out there. If other people find it interesting, they can explore it themselves.
Reject simplicity, embrace complexity
Simplicity is good. We should write simple code. But complexity is unavoidable. We do a disservice to ourselves by pretending that any software can be simple if we just try hard enough. Instead, we should study the factors that lead to complex software. That way we can learn how to recognize, predict, and manage complexity in our systems. And then we can seek simplicity within that context. It won’t give us simple software, but it will help us write simpler software. Nuance is better than mantras.
The myth of self-documenting code
Our discipline has developed a large body of theory on how to write better code, but not write better comments.
Embrace the grind
I often have people newer to the tech industry ask me for secrets to success. There aren’t many, really, but this secret — being willing to do something so terrifically tedious that it appears to be magic — works in tech too.
We’re an industry obsessed with automation, with streamlining, with efficiency. One of the foundational texts of our engineering culture, Larry Wall’s virtues of the programmer, includes laziness:
Laziness: The quality that makes you go to great effort to reduce overall energy expenditure. It makes you write labor-saving programs that other people will find useful and document what you wrote so you don’t have to answer so many questions about it.
I don’t disagree: being able to offload repetitive tasks to a program is one of the best things about knowing how to code. However, sometimes problems can’t be solved by automation. If you’re willing to embrace the grind you’ll look like a magician.
For example, I once joined a team maintaining a system that was drowning in bugs. There were something like two thousand open bug reports. Nothing was tagged, categorized, or prioritized. The team couldn’t agree on which issues to tackle. They were stuck essentially pulling bugs at random, but it was never clear if that issue was important.. New bug reports couldn’t be triaged effectively because finding duplicates was nearly impossible. So the open ticket count continued to climb. The team had been stalled for months. I was tasked with solving the problem: get the team unstuck, get reverse the trend in the open ticket count, come up with a way to eventually drive it down to zero.
So I used the same trick as the magician, which is no trick at all: I did the work. I printed out all the issues — one page of paper for each issue. I read each page. I took over a huge room and started making piles on the floor. I wrote tags on sticky notes and stuck them to piles. I shuffled pages from one stack to another. I wrote ticket numbers on whiteboards in long columns; I imagined I was Ben Affleck in The Accountant. I spent almost three weeks in that room, and emerged with every bug report reviewed, tagged, categorized, and prioritized.
The trend reversed immediately after that: we were able to close several hundred tickets immediately as duplicates, and triaging new issues now took minutes instead of a day. It took I think a year or more to drive the count to zero, but it was all fairly smooth sailing. People said I did the impossible, but that’s wrong: I merely did something so boring that nobody else had been willing to do it.
Sometimes, programming feels like magic: you chant some arcane incantation and a fleet of robots do your bidding. But sometimes, magic is mundane. If you’re willing to embrace the grind, you can pull off the impossible.
UML: my part in its downfall
With the benefit of hindsight, I think UML had quite possibly reached not only its actual, but also its potential, peak in 2000: as a medium for software sketching, people only ever needed the basics from it. However, the standardisation community developed an ambitious vision for UML that far exceeded sketching. Whether or not that vision could ever be realised can be seen as a matter of genuine debate: what seems unarguable to me is that such a vision was deeply unsuited to any standardisation process. QVT is the most succinct example of trying to standardise what was, at best, early-stages research, with failure inevitably resulting. However, while the standardisation overreach inherent in QVT stayed largely within OMG’s confines, MDA’s failure was widely noted. Not only was MDA seen to fail, but by association it undermined the success of UML as a sketching language, turning it into the butt of jokes that it has largely remained to as these days.
I could not have guessed this at the time, but my involvement in all this taught me several valuable lessons, two of which I think are worth highlighting.
First and foremost, group dynamics can develop in such a way that reasonable optimism turns into blind optimism and expressing doubts becomes a taboo. When that happens, it is easy for the group to drift towards extreme positions that guarantee the group’s failure. The UML standardisation community became ever more invested in UML 2’s success: at first, doubting views were dismissed as referencing trivial problems; eventually such views stopped being expressed at all. The community only talked about success, even when there was significant evidence that failure was the most likely outcome. Similarly, QVT was the wrong idea at the wrong time, but people were so desperate for success that they chose to ignore fundamental problems.
Second, when standardisation moves from “standardise what already exists” to “standardise things that we think would be good but don’t yet exist” it enters dangerous territory. I rather like research, but standards committees are about the worst possible place to do research. At best an unsatisfying lowest common denominator ends up being chosen, but at worst the process collapses. There should be no shame, in my opinion, in a standardisation process realising that it has raced ahead of where the state-of-the-art is, and that it would be better to revisit matters when meaningful progress has occurred.