I used to work with someone (a mechanical engineer) who used to ask would you prefer something that's over-engineered or under-engineered. Not sure why anyone would say under-engineered so since we obviously can't get anything just perfectly engineered it's gonna be over engineered. A bridge will take more load than it's rated load, an elevator will take more load than it's rated load etc. etc.
Sometimes we deal with under-engineered stuff, that's the stuff that breaks the first time you put it to use. We don't tend to think highly of those things.
Under-engineering can sometimes be fixed. But not always. Sometimes the fix is to throw it all away and re-do it because the issues run so deep the design is not fixable.
Good software has good "bones". The right decisions were made early on to support building more stuff over it... I completely disagree that you can refactor bad software into good software. Though presumably that's not what the parent meant. Perhaps the idea of the parent is defer big decisions that don't have to be made early. That doesn't mean don't make good decisions that do have to be made early.
> since we obviously can't get anything just perfectly engineered it's gonna be over engineered. A bridge will take more load than it's rated load, an elevator will take more load than it's rated load etc. etc.
That's not what overengineering mean.
To re-use your bridge example, over engineering a bridge would be something like using a complex suspended bridge design with expensive alloys, small tolerances, etc to cross a 10 meters gap where a simple slab of concrete would have done the job.
And "underengineering" would be to pour more steel and concrete at the bridge until it sticks.
well, for sure there are many axis to over engineering. I would say that a system that exceeds some specification is "over engineered" while on that is below the spec is "under engineered". At least I think that's the common use of the term.
I guess "expensive" might fall under costs, so something that was targeting a cost of $1000 but the design costs a million dollars to make is certainly over engineered.
I'd consider load ratings of an elevator to be similar. We mandate those are over-engineered by some margin. But if the load rating of an elevator is 1 ton and you designed an elevator to be able to carry 100 ton then it's certainly over-engineered by more than the safety requirements...
There are plenty of cases where exceeding the requirement is cheaper / easier / simpler than to try to match it more closely because you have some common and standard, hence cheaper, parts available.
So IMHO over and under engineering doesn't refer to the requirements, but to the engineering effort deployed.
I agree the terminology is sometimes used in this sense. Though if there is a bigger engineering effort it's often because the engineers are attempting to go beyond the requirements. I.e. the requirements may be satisfied with SQLlite on a raspberry Pi but the engineer builds a large distributed system that runs in the Cloud. This could just be because they have no idea what they're doing, or it could be because they're intentionally building something beyond what is asked. It's definitely possible to build more complicated systems that just barely satisfy the same requirement that a simpler system would satisfy. I'm tempted to call this "bad engineering" rather than "over engineering".
> Good software has good "bones". The right decisions were made early on to support building more stuff over it.
I like how you expressed this. It puts into words what I've experienced with various codebases, both my own and others'.
This kind of understanding is difficult to teach and learn, what it means for software to have good bones. It also brings up the thought that it's not just about under- and over-engineered, but the qualitative differences between well- and (for lack of a better word) badly engineered software.
Fortunately there are various concepts that people have developed which help in talking about things like "good bones", such as modularity, encapsulation, functional purity, idempotence..
I suppose over-engineered software goes too far in applying these concepts (especially OOP?) where it starts to go against it being "well-engineered". But in general I'd personally prefer working with such a codebase, in comparison to under-engineered software where things are disorganized, too interdependent, rampant use of globals, etc. It follows your analogy of an "over-engineered bridge".
On the other hand, there is something to be said for software architecture that is tastefully and minimally engineered, with just enough strong bones as the basis - but no more! - to achieve its purpose. It's a quality that is somewhat rare to see, where the code is almost deceptively simple to understand, such as small functions with clear singular purposes, and not being too clever. It takes many years of experience (and perhaps certain kinds of personality) to reach that level of simplicity.
I believe such simplicity cannot be taught, but perhaps can be "absorbed" via osmosis, haha..
This statement is dangerous. Badly defined abstraction have understandable and well defined caveats, and used consistently across system. The problems might catastrophic, but you have something to hang on in the hope in fixing it.
under-engineered systems grows into chaos, where every moving part can influence others, the combinatory is exponential, and so is the volume of work needed to fix it
overengineering can be fixed as well, usually by undoing things and doing them over again in the underengineered style. I would grant you that overengineering is typically more work and harder work to fix, though.