6 ms·
I feel like some of the earliest advice and an early learning principle was: - design for reasonable extensions in the future. Is your design modular, will it b
by seadan83 4y ago
I feel like some of the earliest advice and an early learning principle was:
- design for reasonable extensions in the future. Is your design modular, will it be easy to add features? Did you solve problems in a generic way that could be re-used for other other solutions? For example, instead of doing a graph traversal, did you solve the problem by first writing a graph traversal library?
I don't think it's quite as widespread, I would posit that this has changed to:
- (1) YAGNI, build the simplest solution you can for the problem at hand. That will make it as easy as possible to change when future requirements are presented. (2) Design your code to be replaced, not extended. Design your modules in a way that someone can rewrite one without having to rewrite the whole system.
Points (1) & (2) really contradicts the generalized solution approach. The generalized solution tends to be very abstract, hard to understand, and because there are layers - not very modular (which makes it hard to re-write)
- Buffout 4y ago"generalized" means the most simple code you can come up with. This simple code is then used thousands times from very different programs, and it never changes.
- seadan83 4y agoSee XKCD comic : ) I would disagree that "generalized" equates to simple. Generalized means generic and usually abstract. - https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming) https://en.wikipedia.org/wiki/Rule_of_three_(computer_progra... - https://blog.codinghorror.com/rule-of-three/ https://blog.codinghorror.com/rule-of-three/ "It is three times as difficult to build reusable components as single use components" I think implicit in the point, most code does not go on to be used thousands of time (let alone dozens or even more than once). Even if some code is used more than once, making it the same and shared has significant costs as well.
- Buffout 4y agoNever said it is easy to build simple reusable code. Code itself must be simple but process behind devising it may not be. If the code is not simple it is more prone to error.
- gopalv 4y ago> Points (1) & (2) really contradicts the generalized solution approach So I was told this as bluntly as possible by someone 15 years ago - "you don't have to code all parts of your design". Every line of code you write makes it harder to replace it. That the feature is an asset, the code is a liability. That the goal they had set out for me was to write as little as possible, but achieve the same result. The only reason to rewrite a system out of tech debt, which is what I was doing then & getting lost in a sort of second system "this is the last rewrite" effort, is to write the next feature with less code - even if the next system needs you to rewrite bits you just wrote. This might sound simplistic, but I've seen that work over and over again. Plus, it was much easier to hand over the code to the next person when I wanted to do something new and interesting, if it was functionally complete and not full of half designed extension points all over.
- knlb2022 4y ago> "you don't have to code all parts of your design". That's an excellent articulation of what I think as "invisible seams" when I write code: they're soft points of extensions that don't need a separate interface/function/class _yet_. Sometimes I just mark them for myself with an extra newline within a function.
- einpoklum 4y ago> So I was told this as bluntly as possible by someone 15 years ago - "you don't have to code all parts of your design". I would say that's more false then true. Why? Because the design you haven't coded is a fantasy design. I'll give you a counter-quite, and excuse the military context: "No plan survives contact with the enemy" (simplification of a longer quote by Helmuth von Moltke.) I have yet to make any non-trivial design which does not require significant changes while it is being coded. Perhaps some design geniuses with much deeper insight can make this happen - but those are very few. Most people probably need several rounds of implementation and user interaction before reaching an acceptable design. So those parts of your design which you haven't coded are likely partially irrelevant and un-implementable. This is not to say that you should write huge amounts of code. But - try to avoid designing much beyond what you actually code.
- andreareina 4y agoAt work I'm looking at exactly the simplest solution I could have written for the problem then at hand, and now that requirements have changed I'm going to have to rip it all out.
- seadan83 4y agoI'm curious, had you written a generic version, would you still be ripping the code out? (I'm guessing so, otherwise presumably you would now be making that code generic). I still find it funny that the place to optimize is generally for 'rip-out-and-replace', rather than 'extend' (caveat emport, not always the case, but seems to be a better design to optimize for replaceable than it is to optimize for abstract & extensible)
- andreareina 4y agoWe built to the wrong abstraction. We had a choice of two optimisation variables that were conjugates (i.e. you can convert from one to the other like frequency/time, momentum/position etc). We chose the one that was simpler to implement, it turns out the other one is actually more expressive and useful for our purposes.
- travisjungroth 4y agoThere's something in between those two sides that relieves the tension. You don't have to build a graph traversal library and you certainly don't have to publish it. My opinion is that while solving for the immediate problem, you slice off that bit about traversing graphs and handle that generic problem on its own. Now, you only have to think about that one narrow thing. This is also makes it much easier to modify or replace, no need to extend. If you don't do this, the overwhelming tendency is that the graph traversal algorithm snakes its way through your program and is a huge pain to change and reason about.
- Chris_Newton 4y agoIf you don't do this, the overwhelming tendency is that the graph traversal algorithm snakes its way through your program and is a huge pain to change and reason about. Indeed. There is a balance to be found. Over-engineering and using the wrong abstractions hurts. Working with an “organic design” that has no useful separation of concerns, or forever reimplementing the basic data structures and algorithms of whatever domain you’re working in instead of doing creative work that is going to solve your real problem, also hurts. Too far towards either extreme and development progress slows to a crawl while quality plummets. I wish we had a bit less emphasis in the industry on always using the latest shiny tools and a bit more on learning good fundamentals like data structures, algorithms and design patterns. Of course abstractions sometimes need to evolve as requirements do, but identifying which abstractions are helpful at present or when an existing abstraction isn’t helping any more and should be removed or replaced is absolutely a skill that can be developed with knowledge and experience, and it pays huge dividends to those who cultivate it.
- Gigachad 4y agoWe've had a few people who like to do the first option. They would build a semi library inside the app to solve a problem and then when they move on to another company, no one really understands the design principals behind it and make changes that make it not so reusable and years later it never ends up getting reused for anything new anyway so we are just left with a very abstract, pseudo library with no documentation and years of tacked on changes. Now we advise people to build the simplest and most obvious version for the problem you are looking at right now and if in the future you need it all over the place, replace it with a reusable one. The exception seems to be React components, those generally seem to be very easy to identify as reusable UI and can be made generic from day one.
- MathMonkeyMan 4y ago> [...] with no documentation [...] There's the problem.
- lukifer 4y agoGood code should serve as documentation, in addition to serving a functional purpose. “Programs must be written for people to read, and only incidentally for machines to execute.” Depending on the use case, this may not obviate the necessity of plain-text/wiki documentation; but I do note a tendency for many wikis, documentation web sites, etc, to be incomplete and/or woefully out of date. Half-assed docs can almost be worse than no docs; if it’s a priority, then it has to be folded into the entire SDLC, or have its own dedicated staff.
- jayd16 4y agoThe problem with the "generalized" approach is you often only have one data point to generalize across. If you try to focus on solving problems you don't yet have then you end up writing a lot of useless cruft. If you write clean code that is easy to groc and easy to replace, then its also easy to extend once you know where you want to extend to.
- spc476 4y agoCode that is easy to modify slowly becomes code that isn't easy to modify.
- deleted 4y ago[deleted]
- twodave 4y agoI think both can be true. I practice and encourage “solving for a class of problem,” but typically where I see people generate technical debt is when attempting to apply that advice to domain/business logic that isn’t and shouldn’t be very abstract. Apply it to the things that make your app go, such as white-labeling or bulk inserts. Do not apply it to things like your new employee groups feature or payroll calculations. Applied to those you just add additional and arbitrary rules that have to be obeyed or else the abstraction begins to leak. And it always leaks.