Although of course solving the abstract problem does not have to be 10 times as much code. The best solutions are often those, that recognize the more general problem, solve it with little and elegant code, then turn to the specific problem, expressing it in terms of the abstract problem and thereby solving it in just a few lines of code. Such an approach should usually be accompanied by some documentation.
To give a trivial example: Binary search or any other bog standard algorithm. You would want to have an implementation for the algorithm and named as such and then only apply it in the specific case you have. Sorting algorithms. You don't want to rewrite it all the time. Actually rewriting it all the time would be the thing that creates "10 times" the code.
Tell that to Richard Hamming: "Instead of attacking isolated problems, I made the resolution that I would never again solve an isolated problem except as characteristic of a class." [1]
I have seen this "premature abstraction" warning creep through our discourse lately, but I don't clearly understand it. I feel like I'm making calls all the time about when to introduce functions or classes that will save you time or effort in the future without forcing yourself to see the repetition before you do. Not only that but Hamming's advice has rung true in my career. Solving the general problem is often easier than solving a specific case and can be re-used for later instances, too.
> Solving the general problem is often easier than solving a specific case and can be re-used for later instances, too.
You and the other person are both correct. What you're saying makes sense and it is what everybody is trained to do. However, it leads to a lot of useless code exactly because you're applying an abstraction that is used only once. That's why most codebases are bloated and have a huge number of dependencies.
To your point, we use abstractions all the damn time. They're everywhere. Even programming languages are an abstraction (especially high level ones). You and I, and everybody else here doesn't pick a cylinder and a block to write to and tell the hard drive to move it's arm into place and record the magnetic data, no we all talk about inserting a row into the DB.
Abstractions are essential to productivity or you'll never get out of the "make it from scratch" trap
Yes, but there is a line to be drawn somewhere. Filesystems are sufficiently advanced such that there’s no meaningful gains to be had from manually allocating CHS for data, and they provide housekeeping. C abstracts a lot of architecture-specific information away, but still requires that you understand a modicum of memory management; if you understand it (and cache line access) well, you can get even more performance. Python abstracts that away as well, and gives you a huge standard library to accomplish many common tasks with ease.
You can quickly make a prototype in Python, but it won’t be as performant as C. You can spend time profiling it and moving computationally-heavy parts into C extensions (I do this for fun and learning), but you’ll likely spend more time and get worse results than if you just rewrote it.
Docker is an abstraction over already-existing technology like cgroups. It provides an easy-to-understand model, and a common language. This is quite valuable, but it does allow one to not know about what it’s hiding, which is problematic when troubleshooting – for example, naïvely assuming that querying /proc in a container shows the CPU resources allocated to the container, rather than the host.
That’s how I view abstractions. They can be incredibly useful, but they usually have trade-offs, and those should always be considered. Most importantly, you should at a minimum be aware of what you’re giving up by using them, even if you don’t fully understand it.
If we ignore 'buggy' part I think you projecting current good state back into not so good old times. I am pretty sure you will not replace radix sort that uses domain knowledge of reduced value range with qsort circa C++98.
Things became much better after relatively recent improvements in generalist sorting libraries: when pattern defeating quick sort variants became norm, when mostly sorted cases got covered ...
Tbh I do have a case from 2013-16 when I regret not doing it for one AAA project on ps4.
Well known data structures and algorithms are well know because they have been used more than three times. That said: if you are dealing with a situation where you have to implement them yourself, you may want to consider whether the rule of three applies. (Clearly this depends upon the situation.)
If the well known data structures and algorithms are not provide by your language get a better language.
There are exceptions. If you are implementing the language it is your job to write them. It is useful as a student to implement the basics from scratch. Your language may decide something is not allowed and thus not implement it (a doubly linked list is almost always a bad idea in the real world. Likewise you don't need to provide all the sorting algorithms)
I am wondering where the "generalize code after doing it for the 3rd time" rule of thumb comes from? I also subscribe to it, and read it somewhere 15/20 years ago.
To give a trivial example: Binary search or any other bog standard algorithm. You would want to have an implementation for the algorithm and named as such and then only apply it in the specific case you have. Sorting algorithms. You don't want to rewrite it all the time. Actually rewriting it all the time would be the thing that creates "10 times" the code.