Software essays that shaped me
refactoringenglish.com276 points by mtlynch a day ago
276 points by mtlynch a day ago
I just read "I've locked myself out of my digital life"[1] and it explains a concern that I have and sometimes struggle to explain.
> In the boring analogue world - I am pretty sure that I'd be able to convince a human that I am who I say I am. And, thus, get access to my accounts. I may have to go to court to force a company to give me access back, but it is possible.
> But when things are secured by an unassailable algorithm - I am out of luck. No amount of pleading will let me without the correct credentials. The company which provides my password manager simply doesn't have access to my passwords. There is no-one to convince. Code is law.
Everyone should understand this problem before they advocate to remove the in-person version of a process. The article's example sounds unlikely at first, but the same consequences can happen with any natural disaster or a robbery.
[1] https://shkspr.mobi/blog/2022/06/ive-locked-myself-out-of-my...
I don't think anyone is advocating for this, and it's not limited to AI (software with a bunch of rules has the same problem). The EU went so far as enshrining into law the right to always escalate any decision to a person.
The crypto movement often advocates for this (e.g. code is law, not-your-keys), but their perception and commitment to it is usually paper thin.
But we DO advocate to have everything encrypted with no possibility to recover it if you lose access to the master key. That creates interesting ramifications in OP argument.
The Grug Brained Developer is one that always sticks in my head, but didn't make the list (to be fair, maybe more because I already agreed than because it transformed my thinking).
Very nice! I liked this one:
> grug quite satisfied when complexity demon trapped properly in crystal, is best feeling to trap mortal enemy!
As a bilingual, non-native, English speaker, grug brain is particularly hard to read and understand.
I don’t understand what « grug » is supposed to mean for example.
That being said I still enjoy the blog.
This is one instance where an AI might shine. Ask it to summarise the article in the style of an articulate intelligent person.
"Grug" in this context basically just means a kind of simplistic caveperson-like mindset.
For whatever reason, the sound "Grug" is associated with cavepeople in English.
Seems to be a reference to "The Croods" [1], a 2013 animated movie in which Nicholas Cage (!) voices the caveman Grug.
Grug was a caveman character in a movie, I figured that's where the name came from.
Whenever I run into such a problem I use google (or the web search of your choice). You may not have expected it (and you won't always be this fortunate), but this immediately makes the identity, nature, and meaning of grug evident.
Grug is simply the narrator's name. He's speaking in the third person.
Meant to be some generic "caveman" name/character.
grug is grug name
grug neighbours ugg, oog, thog, thag, tragg, yog, grr, argh not use computer
Concerning Fred Brooks "No Silver Bullet", I disagree on this conclusion:
> Modern AI has thrown a wrench into Brooks’ theory, as it actually does reduce essential complexity. You can hand AI an incomplete or contradictory specification, and the AI will fill in the gaps by cribbing from similar specifications.
The essential part is still not adequately covered by Generative AI, and probably never will be. Here is my detailed write-up about it: https://smartmic.bearblog.dev/no-ai-silver-bullet/
Thanks for reading and for sharing your post!
In your writeup, it seems like you're arguing that LLMs can't eliminate essential complexity, and I agree that they probably can't.
But I do think they can reduce essential complexity.
As a concrete example, here's me prompting Claude 4.1 Opus to define a domain-specific language for creating computer-generated paintings.[0] I just provided the requirements and left a lot of ambiguity in the specifics.
In that example, has LLM reduced essential complexity at all?
To me, the answer is clearly yes. It wrote a spec based on my requirements. I could potentially do better if I defined it from scratch, but if the LLM-generated spec is good enough, then it likely isn't worth the cost of me doing it myself for the marginal improvement in quality.
When LLMs first came out, I felt like I had no need for them because I think I can write code better than they can, and I enjoy writing code. But as I've started experimenting with them, I'm realizing that there are some problems that I can solve with software that I don't actually enjoy implementing and I don't care that much about specifying every aspect of my program's behavior, so LLMs fit well in those situations and eliminate essential complexity that would otherwise fall in my lap.
[0] https://kagi.com/assistant/1b8324a2-ae54-4a1b-9c69-51d76fc5c...
The only complexity AI reduces is the cognitive complexity of writing it. The code itself almost certainly will not be free of Brooks' nonessential complexity, and the reader is SOL.
It's like putting on an exo suit, lifting something very heavy and putting it on a shelf, then asking your teammate to go paint it.
The "Parse, don't validate" paper is classic IMHO.
I disagree with "Don't put logic in tests", with the example provided being a problem with using strings where a URI type is needed instead. Perhaps the source of my disagreement is I hold that test code is production code due to test suite failure(s) during an automated build stops desired deployment.
Still, each are definitely worth delving into and determining applicability for oneself.
>The "Parse, don't validate" paper is classic IMHO.
Yeah, I find it so baffling that 90% of programmers I talk to have never heard of it. My circle is more Go/Python/C++ folks, so maybe it's more well-known in functional programming circles.
>I disagree with "Don't put logic in tests", with the example provided being a problem with using strings where a URI type is needed instead. Perhaps the source of my disagreement is I hold that test code is production code due to test suite failure(s) during an automated build stops desired deployment.
Yeah, I think that's a fair criticism. I think the specifics of the example could be better, but I think the important underlying message is that even something that seems simple like a string concatenation is added complexity that can mask a bug in a test.
>> I disagree with "Don't put logic in tests", with the example provided being a problem with using strings where a URI type is needed instead. Perhaps the source of my disagreement is I hold that test code is production code due to test suite failure(s) during an automated build stops desired deployment.
> Yeah, I think that's a fair criticism. I think the specifics of the example could be better, but I think the important underlying message is that even something that seems simple like a string concatenation is added complexity that can mask a bug in a test.
I didn't mean to criticize so much as identify why I consider test code to be the same as production code.
Continuing with the example provided, the string concatenation is not the problem this test identifies IMHO. Instead, it is that:
nav.getCurrentUrl()
Returns a `String` instead of a type which disallows the formulation of the problematic `assertEquals` to begin with.In a more general sense, I have found treating test suites the same as one would production code (refactoring, commenting, sometimes testing support logic used to define tests, etc.) has led to tests benefiting the same way. This approach also has had a twofold benefit of "keeping the same energy" when producing all source artifacts along with serving as a great way to onboard new team members.
All the usual caveats apply, of course. YMMV, IMHO, etc. :-)
> Returns a `String` instead of a type which disallows the formulation of the problematic `assertEquals` to begin with.
I'm not sure what the best attribution would be but "Make illegal states unrepresentable" would be a fantastic addition to this list pairing well with "parse, don't validate".
A stricter type would force you to parse the URL and would either fix the error (because cleaning trailing/leading slashes might make sense here) or throw a clear error from the parser.
It can be slightly more verbose when you just want to write a string in your test for convenience but can (and does) save a lot of debugging pain for less trivial cases.
>> Returns a `String` instead of a type which disallows the formulation of the problematic `assertEquals` to begin with.
> I'm not sure what the best attribution would be but "Make illegal states unrepresentable" would be a fantastic addition to this list pairing well with "parse, don't validate".
The phrases I have seen describing using types to make illegal states incapable of being represented are "programming with types"[0] and "type level programming"[1].
HTH
0 - https://www.manning.com/books/programming-with-types
1 - https://rebeccaskinner.net/posts/2021-08-25-introduction-to-...
What you're talking about is changing the interface of the code under test. You usually can't do that. If the interface outputs a string then you need to deal with a string.
Ideally a test should be an extremely literal interpretation of the spec. The test in the example I would read as "the output should be the result of a string concatenation between the base URL string and this string". The problem is that spec doesn't exist because it's stupid and wrong. This test suite implements a bug-ridden, ad hoc URL builder.
The real spec is probably "the output should be a string containing a valid URL for the photos page". I see two options for writing this test:
1. Compare to a "trusted" string literal which conforms to the spec (ie. to be a valid URL for the photos page),
2. Decode the string using a "trusted" URL decoder and validate it at the URL level.
I think they are equally valid options and a matter of taste and convenience but both accurately reflect the spec, unlike the example.
The second option is essentially playing off two bits of code against each other, like testing a forwards operation against a reverse operation. This works if both bits of code are completely independent and therefore extremely unlikely to accidentally compensate for each other's bugs. If the URL decoder is in a widely used standard library then this could be considered OK (the robustness principle explicitly makes things asymmetric, though, so a URL decoder with a "strict" mode would be most appropriate). But ad hoc code written in a test suite is definitely not OK!
To me there's a certain appeal to not putting (too much) logic in tests. A test with a false positive because there's a bug in the test logic is a false confidence signal that I've seen enough times to resent. I shouldn't feel the need to have tests for my tests.
To be fair, it doesn't happen often, though what I'm seeing more of is just blatantly bad tests written by LLMs. It's hard to blame a philosophy when dealing with humans that have fully delegated their brains over to a random number generator.
> To me there's a certain appeal to not putting (too much) logic in tests. A test with a false positive because there's a bug in the test logic is a false confidence signal that I've seen enough times to resent. I shouldn't feel the need to have tests for my tests.
I agree that there is a balance to be had here. The "logic in tests" I imply is logic which should only exist to make test suites have all of the "ables" we want in production code; extensible, readable, maintainable, understandable, etc. And when I do think "tests for tests" are applicable it is not for the tests themselves, but for the rare cases of supporting logic pervasive in non-trivial test suites.
> To be fair, it doesn't happen often, though what I'm seeing more of is just blatantly bad tests written by LLMs. It's hard to blame a philosophy when dealing with humans that have fully delegated their brains over to a random number generator.
I can't agree with you more on this.
Bad code (test or otherwise) generated by LLM's does not make clueless people more productive. It just pushes crap upstream for someone else to deal with.
1. Nancy Leveson's Therac-25 investigation and review.
* An Investigation of the Therac-25 Accidents
https://cse.msu.edu/~cse470/Public/Handouts/Therac/Therac_1....
* The Therac-25: 30 Years Later
https://ieeexplore.ieee.org/document/8102762
2. Charles Fishman's They Write the Right Stuff
https://www.eng.auburn.edu/~kchang/comp6710/readings/They%20...
My favorite in this vein: The Parable of the Two Programmers, by Neil W. Rickert. Sums things up nicely.
https://c00kiemon5ter.github.io/code/philosophy/2011/10/30/T...
Ha ... I knew Neil well from interacting with him for many years on the comp.ai.philosophy Usenet newsgroup ... that sounds so like him.
"slumming with basic programmers" is one i think about a lot, as a programming languages person
Perhaps a bit more of an unorthodox blog post, but Gilad Bracha's Ban on Imports really changed the way I think about module systems: https://gbracha.blogspot.com/2009/06/ban-on-imports.html
He talks about how imports and exports are global state, and that they have all the issues that global state brings. It's made me appreciate dependency injection a lot more.
This was a watershed for me: https://stevemcconnell.com/articles/software-quality-at-top-...
McConnell isn't especially popular, hereabouts, though.
Thanks for sharing! I hadn't read that one, but I read Code Complete early in my career and loved it. It's been on my bookshelf forever, but I've only gone back to it a few times. But every time I do, I think, "This is really good! I should re-read this."
I was wondering whatever happened to him because the guys that were popular around his time (Kent Beck, Martin Fowler, Ward Cunningham) all continued writing, even if their popularity waned after the 2000s. But I just never saw anything from McConnell again.
It turns out he quit software to be a financial advisor, which is quite surprising.[0]
[0] https://raindogllc.com/steve-mcconnell-investment-advisor/
He wrote several more books (including a sequel/second edition of Code Complete). None of them had the same impact, though: https://stevemcconnell.com/books/
Rapid Development was, in opinion, better than Code Complete, but I was also a manager, by then, and it spoke to me.
I used to take classes from his Construx University school.
I think he made a pile of money, somehow, and does what he wants.
He did a whole bunch of data-mining around COVID: https://stevemcconnell.com/cdc-covid-19-forecast-evaluations...
Not sure if can be considered a software essay, but a goldmine (imho) is Patrick McKenzie's - Don't Call Yourself A Programmer, And Other Career Advice:
https://www.kalzumeus.com/2011/10/28/dont-call-yourself-a-pr...
Semantic Compression [0] had real impact on me.
From my end, it was this - https://blog.codinghorror.com/falling-into-the-pit-of-succes...
Changed the way I think software.
Tangent: It's more than an essay, but I have to mention https://every-layout.dev as an absolute game-changer for my perspective on CSS.
I have creatdd a readings page on my blog to serve a similar purpose. The theoretical intention is to update it from time to time.
> Test code doesn’t have its own test code, so the only way to verify correctness is by inspection.
This is about as incorrect as it gets.
The only way to verify correctness is to change the production code in a way that should cause the test to fail, then watch it fail. In TDD this is done by writing the test before you write the code that would make it pass, then watch it fail. Only then are you allowed to write the code that makes it pass.
Otherwise you have no proof the test can EVER fail - which means no proof it adds value to your codebase.
“Choose Boring Technology” by Dan McKinley (2015)
This is an odd essay to include in this list because I’ve never actually read it.
People have quoted this essay to me, and once I understood the idea, it felt so intuitive that I didn’t need to read it.
This is such a strange thing to include and obviously a lie as he goes on to describe the essay in detail.Idk, "choose boring technology" and "you get three innovation tokens" is enough to make the point. It's easy to understand and come up with your own examples imo.
Like, I've heard "no silver bullet" quoted enough that I could write about the main ideas, despite never having read the paper.