I was asked to describe this book in two sentences to someone when I mentioned that I read it, but I think it can be done in one assertion: Complexity makes software bad.
For starters, this book called out some practices that are advocated for in other books like “Clean Code” and others. I don’t recall the exact example given in clean code, but what stood out to me was that someone finally opposed it. Throughout college and into my early professional career, “Clean Code” has been a staple of code quality discussion. I enjoyed someone making a targeted, worthwhile criticism of one of its practices. This is not to say I want to watch “Clean Code” burn, but more so that there is no such thing as a doctrine for writing code. Some practices are good, but can be taken too far. Some practices are bad, but may have their place. I really liked the “taking things too far” sections in this book for this very reason.
I thought the “design it twice” idea was interesting. I think from a learning perspective, this is an excellent idea. In practice in a business environment, I think most people would have a hard time achieving this without raising a few eyebrows on why a seemingly small problem is taking so long. However, I have been doing something lately that maybe is a hybrid between the author’s recommendation and going with the first approach. I’ve been drafting my idea first. Looking at some implementation, thinking about ramifications, exploring edge cases, and drafting a simple plan. Then I’ll go to a frontier model LLM and give it my plan to poke holes. Sometimes I go with an enhanced version of my original idea and other times the LLM proposes something I hadn’t even considered that’s a great improvement!
Along the same principle as “design it twice,” I asked my colleague who originally recommended the book and he shared something that I think can be frustrating in the moment, but is necessary which is that we should be open to large refactors at any point in the process. Of course, as the author mentions, we should be getting better over time and designing software with less huge refactors as we get better, but in the interim, if we hit a snag or see a significantly better way of doing things, there’s no reason to not re-write a feature. I do feel this is probably something the developer has to silently undertake themselves as the saying “we don’t have the time/money to design it right, but we do have the time/money to go back and fix it” rings true in many of the environments I’ve worked in. But if the silent pursuit of excellence means the developer holds true to their values and the end user gets a better product, it’s probably not all that bad despite what the velocity charts may indicate.
Simplicity as the bedrock of all decisions. This was what stood out most to me. In the last year or so, I’ve gotten caught up in many coding “standards” like “Domain Driven Development,” “Code Smells,” and “Testing Hierarchies.” All of these have merit and there is a lot of good in some of the hazards they protect developers and users from. But what if it could all be replaced by “is this the simplest solution?” Obviously, there are some flaws in this approach as one developer’s definition of simple differs drastically from another’s. Additionally, simple can be hard to define as a group because you can run into situations like Go’s programming standards where variable names like a, b, c, d, e all in one function refer to complex ideas, making the software hard to follow. This is a point the author brought up and I strongly agree. So it’s not quite as easy and flushing all the books and gists and linting rules for one sentence, but in my opinion, it’s the easier of the two to remember while I’m hacking away.
No Test Driven Development (TDD) was another critique I was drawn to. This was preached somewhere I wrote software, although they didn’t actually do it funny enough, and it never sat well with me. I always felt that software was such an iterative process that unless I had waterfalled the heck out of what I was working on, there was no way I could write the tests first! I’d also say the same thing about the author’s Comment Driven Development (CDD) though. For example, say I was implementing a complex full stack feature and I CDD’d my way through the entire thing. Then I “designed it twice” either manually or with LLM support and realized there was a more elegant solution on all sides of the application. Do I go back and re-CDD the entire thing? That seems like a waste of time. I think it would be more valuable to implement the worse approach first, see why it didn’t meet the requirements, and re-implement. Especially for a complex feature.
I enjoyed this book because it was the first software book, not that I’ve read many, where I showed up to work or a personal project and thought about the work I was about to do and how it tied back to keeping things simple. I believe it had a real impact on how I think about building software and hopefully, with time and effort, will have a positive impact on how that software turns out.
While talking about this book with someone, I shared my findings, what I liked, found difficult, etc.. To my surprise, their response was a partial dismissal of the main point of this book because of the rise in LLM coding. To me, this felt incorrect. I don’t doubt that LLM assisted development will be a staple in the industry from here on out. I don’t doubt that because code can be pumped out that the floor to getting a prototype or small piece of working software has been all but eliminated. However, if I were to look at some of the best software that exists today and asked an LLM to create it from scratch, assuming the LLM was unaware of the software’s existence, that the only way to re-create the excellence of that software would be to abide by the intention, care, thought, experience, planning, and design the author(s) originally put in themselves. I have not seen evidence that crap code leads to an excellent product. Being wrong on this point would probably uproot me as a developer, I’ll admit that. But I don’t believe I’m wrong yet.
To clarify the sidebar, the point is not “human code good, LLM code bad” as that’s a simplistic, naive, and flat out incorrect viewpoint. It’s something like, irrespective of how the code came to be, it must provide value to the user and be maintainable long-term.