5 ms·
One of the many ironies of modern software development is that we sometimes introduce complexity because we think it will "save time in the end". Sometimes we'r
by GMoromisato 1y ago
One of the many ironies of modern software development is that we sometimes introduce complexity because we think it will "save time in the end". Sometimes we're right and it does save time--but not always and maybe not often.
Three examples:
DRY (Don't Repeat Yourself) sometimes leads to premature abstraction. We think, "hey, I bet this pattern will get used elsewhere, so we need to abstract out the common parts of the pattern and then..." And that's when the Complexity Demon enters.
We want as many bugs as possible caught at compile-time. But that means the compiler needs to know more and more about what we're actually trying to do, so we come up with increasingly complex types which tax your ability to understand.
To avoid boilerplate we create complex macros or entire DSLs to reduce typing. Unfortunately, the Law of Leaky Abstractions means that when we actually need to know the underlying implementation, our head explodes.
Our challenge is that each of these examples is sometimes a good idea. But not always. Being able to decide when to introduce complexity to simplify things is, IMHO, the mark of a good software engineer.
- zie1ony 1y agoYou must be a Rust developer.
- GMoromisato 1y agoWorse--C++
- mplanchard 1y agoFor folks who seek a rule of thumb, I’ve found SPoT (single point of truth) a better maxim than DRY: there should be ideally one place where business logic is defined. Other stuff can be duplicated as needed and it isn’t inherently a bad thing. To modulate DRY, I try to emphasize the “rule of three”: up to three duplicates of some copy/paste code is fine, and after that we should think about abstracting. Of course no rule of thumb applies in all cases, and the sense for that is hard to teach.
- GMoromisato 1y ago100% agree. Duplication is far cheaper than the wrong abstraction. Student: I notice that you duplicated code here rather than creating an abstraction for both. Master: That is correct. Student: But what if you need to change the code in the future? Master: Then I will change it in the future. At that point the student became enlightened.
- deleted 1y ago[deleted]
- bluefirebrand 1y ago> To modulate DRY, I try to emphasize the “rule of three”: up to three duplicates of some copy/paste code is fine, and after that we should think about abstracting Just for fun, this more or less already exists as another acronym: WET. Write Everything Twice It basically just means exactly what you said. Don't bother DRYing your code until you find yourself writing it for the third time.
- noisy_boy 1y agoAnd to those who feel the OCD and fear of forgetting coming over by writing twice, put TODOs on both spots; so that when the third time comes, you can find the other two easily. If you are the backlogging type, put JIRA reference with the TODOs to make finding even easier.
- ghosty141 1y ago> I’ve found SPoT (single point of truth) a better maxim than DRY I totally agree. For example having 5 variables that are all the same value but mean very different things is good. Combining them to one variable would be "DRY" but would defeat separations of concern. With variables its obvious but the same applies to more complex concepts like functions, classes, programs to a degree. It's fine to share code across abstractions but you gotta make sure that it doesn't end up tying these things too much together just for the cause of DRY.
- PaulHoule 1y agoI still believe that most code, on average, is not DRY enough, but for projects I do on my own account I've recently developed a doctrine of "there are no applications, only screens" and funny enough this has been using HTMX which I think the author of that blog wrote. Usually I make web applications using Sinatra-like frameworks like Flask or JAXB where I write a function that answers URLs that match a pattern and a "screen" is one or more of those functions that work together and maybe some HTML templates that go with them. For instance there might be a URL for a web page that shows data about a user, and another URL that HTMX calls when you flip a <select> to change the status of that user. Assuming the "application" has the stuff to configure the database connection and file locations and draw HTML headers and footers and such, there is otherwise little coupling between the screens so if you want to make a new screen you can cut and paste an old screen and modify it, or you can ask an LLM to make you a screen or endpoint and if it "vibe coded" you a bad screen you can just try again to make another screen. It can make sense to use inheritance or composition to make a screen that can be specialized, or to write screens that are standalone (other than fetching the db connection and such.) The origin story was that I was working on a framework for making ML training sets called "Themis" that was using microservices, React, Docker and such. The real requirement was that we were (1) always adding new tasks, and (2) having to create simple but always optimized "screens" for those tasks because if you are making 20,000 judgements it is bad enough to click 20,000 times, if you have to click 4x for each one and it adds up to 80,000 you will probably give up. As it was written you had to write a bunch of API endpoints as part of a JAXB application and React components that were part of a monolithic React app and wait 20 minutes for typescript and Docker and javac to do their things and if you are lucky it boots up otherwise you have to start over. I wrote up a criticism of Themis and designed "Nemesis" that was designed for rapid development of new tasks and it was a path not taken at the old job, but Nemesis and I have been chewing through millions of instances of tasks ever since.
- GMoromisato 1y agoFascinating! I also recoiled at the complexity of React, Docker, etc. and went a different path: I basically moved all the code to the server and added a way to "project" the UI to the browser. From the program's perspective, you think you're just showing GUI controls on a local screen. There is no client/server split. Under the covers, the platform talks to some JavaScript on the browser to render the controls. This works well for me since I grew up programming on Windows PCs, where you have full control over the machine. Check it out if you're interested: https://gridwhale.com https://gridwhale.com. I think pushing code to the server via HTMX and treating the browser like a dumb terminal has the same kind of advantage: you only have to worry about one system. Fundamentally, IMHO, the client/server split is where all the complexity happens. If you're writing two programs, on the client and one on the server, you're basically creating a distributed system, which we know is very hard.
- Symmetry 1y agoOn my laptop I have a yin-yang sticker with the yin labeled DRY and the yang labeled YAGNI.
- GMoromisato 1y agoI love it!
- 0xbadcafebee 1y agosometime grug spend 100 hours building machine to complete work, but manual work takes 1 hour. or spend 1 hour to make machine, lead to 100 hours fixing stupid machine. dont matter if complex or simple, if result not add value. focus on add more value than detract, worry complexity after
- frollogaston 1y agoDRY isn't very dangerous. It's not telling you to spin off a helper that's only used in one place. If a ton of logic is in one function/class/file/whatever, it's still DRY as long as it's not copied. Premature abstraction is a thing. Doesn't help that every CS course kinda tells you to do this. Give a new grad a MySQL database and the first thing they might try to do is abstract away MySQL.