Remix.run Logo
mpweiher 2 days ago

Always a great read!

However, things have actually changed since then. A lot.

Here is Brooks at a "20 year retrospective" panel[1] at OOPSLA '07:

"When ‘96 came and I was writing the new edition of The Mythical Man-Month and did a chapter on that, there was not yet a 10 fold improvement in productivity in software engineering. I would question whether there has been any single technique that has done so since. If so, I would guess it is object oriented programming, as a matter of fact."

Large-scale reuse, which was still a pipe-dream in 1986, and only beginning in 1996 is now a reality. One which has brought its own set of problems, among them the potential for a lot of accidental complexity.

We now use 50 million lines of code to run a garage door opener[2]. I can declare with some level of confidence that the non-essential complexity in that example is more than the 90% that Brooks postulated as a safe upper limit. Five million lines of code is not the essential complexity of a garage door opener.

And while that is an extreme example, it seems closer to the normal case these days than the comparatively lean systems Brooks was thinking about.

[1] https://www.infoq.com/articles/No-Silver-Bullet-Summary/

[2] https://berthub.eu/articles/posts/a-2024-plea-for-lean-softw...

godelski 2 days ago | parent [-]

I have a hypothesis as to how much of this happens. There's a seemingly paradoxical idea: simplicity requires complexity while complexity can be achieved with simplicity.

I think a lot of this complexity happens with everyone's efforts to make things simple. The problem is actually in that there's multiple ways to define simple. I have a good example of this that happened just last week. I have some Papa John's gift cards from Costco, and was ordering a pizza with a friend. So we put in the gift card amount and find out it is just short of the order. No problem, we'll pay the difference with another card. Problem is... that's not possible. We had to edit the order so we could bring the price down. In fact, I think we see things like this happen with pre-paid Visa/MasterCard cards. The problem was that the programmer made too simple of an assumption: customers only need one gift card. How would we fix this? Well now we should probably write a method that allows an arbitrary number of gift cards and credit cards. Our method would need to allow this arbitrary splitting and the question then is how much we'd want to expose to the user (do we want them to split a purchase with two credit cards?).

I think all of us can hear a likely conversation in our head about implementing such a payment system. Some person is going to say that we're over complicating the problem. Especially when we're talking about credit cards, it probably is a safe assumption to make that the order will be completed with a single card. Except it's fucking pizza. How often do you end up venmoing your buddy after you order a pizza? You didn't actually make the things simpler, you just moved the problem elsewhere and actually added complexity. It's definitely more complex to have a whole other app to facilitate a second transaction than it is to just allow arbitrary payment cards.

I see this happen in code all the time and I think Unix Philosophy[0] can be really beneficial. You try to make your functions to very basic things and then put them together. I try very hard to write flexible code because the only thing I know is that the code is the goals of the code are going to change over time. So you don't write just to get a specific problem solved, you write to get your problem solved and for others to be able to use your building blocks. And your building blocks should just allow things to be modifiable. How many of those 50 million lines of code are redundant? I'd wager a fair amount![1] Not hard, you just use very minimal abstraction. Like if you write a function to calculate tax you don't fix the value of sales tax even though your business only operates in <state of your choosing>, you just expose the variable. Extrapolate this out. This is technically more complex if we're viewing the function in isolation, but it is simpler if we are viewing the program as a whole (and viewing it over it's progressive lifetime). Of course, you can go too far with this too and striking that balance is difficult. But typically I see code that is inflexible rather than too flexible.

[0] https://en.wikipedia.org/wiki/Unix_philosophy

[1] I hear people say they get a lot of utility from coding agents because it takes care of boiler plate and repetition. I think that says something. If you're doing something over and over (even with a bit of variation) then there's probably a better way to do this. Problem is that if you're just rushing to finish Jira tickets you'll probably never have the time to figure that out. If your manager just measures your performance in number of tickets completed they'll never care how many tickets your code created. Sometimes to go fast you gotta slow down. IMO the most time consuming parts of writing code are the planning and debugging (analyzing, rethinking, rewriting, not just fixing errors) stages. Writing lines is much smaller in comparison and can be done in parallel.