7 ms·
For me, it was that I thought that being a good programmer was that I write clean code with enough abstraction and indirection to make it future proof. Boy I w
by jehlakj 4y ago
For me, it was that I thought that being a good programmer was that I write clean code with enough abstraction and indirection to make it future proof.
Boy I was wrong. Unless you’re doing the same thing you’ve done for years, you can’t tell the future. And just when your unnecessary abstraction is wrong, this the reason why we’re talking about tech debt in the first place. Because nobody wants to touch it.
Unfortunately, I didn’t have anyone to tell me this for the longest time. It wasn’t until I had to fix a bug for something I wrote in the past, and I couldn’t figure out just what I wrote.
Now I try to write dumb and simple (yet sensible) code until there’s a good reason for abstractions. I have nothing to prove at this point in my career.
- snarf21 4y agoWell said. We are having a similar "fight" where I work now. Way too much premature optimization. I think the one concept that takes a while to really understand is to focus on separation of concerns. It is far too easy to get encapsulation wrong. People (imo) generally forget to apply the SoC test that tells them where to draw the line. We also have a lot of parallel development where re-factoring a View (Screen) could cause a rippling failure to other people's code. Too often they try to reuse everything instead of the important part of extracting the shared business logic into a XXManager and letting the Views just be dumb. Who cares if two Views developed in parallel might be too similar? It might change in the future and don't have to worry about 1 View doing 2 jobs.
- li2uR3ce 4y ago> Now I try to write dumb and simple (yet sensible) code until there’s a good reason for abstractions. Good abstraction is a lot harder than most think. We tote abstraction's benefits as a reason for abstraction but fail to recognize bad abstraction and how it completely negates any would be benefit. Too often abstraction (so called) requires a lot of research into how it's implemented. By the time you figure out enough implementation details to use the abstraction, you could have implemented it yourself quicker. It saves no time or effort but you're stuck with it because existing code is hard to part with. If you're not reliving your users of a shit ton of implementation details then it's just not good abstraction. Code for someone who doesn't want or have time to learn the details. Often this will be your future self despite your current believe that your mind is a steal trap and would never forget how you did this stuff.
- deleted 4y ago[deleted]
- tomxor 4y ago> you can’t tell the future This is so true. It's tricky because some things are worth thinking ahead a little - but on average, I've learned that it's far better to focus on making things easy to change than make them directly accommodate future requirements but (often at the cost of immense complexity)... If the code is so small and simple that you can easily re-write it, then it is future proof and easy to understand and maintain, win win. It's easy enough to understand this abstractly, but takes practice to know when to plan ahead and when not to - but a good oversimplification is that: if you know it's a future requirement, it's worth thinking about, maybe even worth making a space in your architecture/api/data whatever; If it's an unknown, just don't bother, try to keep the code simple instead so that you can adapt.
- cooervo 4y agothis is exactly what I like in Golang
- didibus 4y agoThere's a level beyond that where you actually figure out how to write good abstractions. It's likely you thought you were making good abstractions and useful indirections, but you weren't, hence the problem. Concrete code with little indirection will be better than badly thought out abstractions that are incorrectly designed with unnecessary layers and indirections. That said, good ones, that are well done and thought out are worth their salt and can result in huge force multipliers for future runway.
- baobabKoodaa 4y ago> actually figure out how to write good abstractions. There's an element of no-true-scottsman in this argument. Most codebases that I've seen have excessive amounts of unnecessary abstractions. It's rare to see a codebase that has too few abstractions. You can of course make the argument that "they just weren't creating the right abstractions", and it's not necessarily incorrect - it's just unhelpful as a piece of advice. You can take any methodology - no matter how bad it is - and claim that any seeming faults in the methodology are simply the result of people applying it incorrectly. "No true scottsman would have created this abstraction". Since the needle is currently pointing in one direction more often than the other, I think it's generally helpful to shell out advice that moves the needle in the other direction: advice such as "less abstractions is generally better".
- didibus 4y agoThere was a time where there was seldom any abstractions, people wrote in assembly code, it was as close to the concrete machine as you could be. It was painful, complicated, and doing anything was tedious, effortful and slow. Abstractions were clearly needed. Higher level languages abstracting over the machine lower level details were needed. Then there was a time where abstractions themselves were very simple, branching and looping were all just done with "goto", it was error prone, confusing, and made working with other people's code bases difficult. Abstractions were clearly needed, something to abstract over the lower level details of branching and looping and memory management with relations to those. Fast forward to Java. Now we already started with quite a lot of abstraction, yet there were still times when things were more tedious then they needed to be, more abstraction was still needed, it led to the addition of Interfaces, the development of frameworks like Spring, the creation of template languages like JSP, the addition of code-generation tools like Lombok or API generation like Open API. Once again more abstraction became hugely benefitial, delivering real productivity boosts and still helping to make things clearer, not more obfuscated. Even though it is true at each layer it becomes harder to understand how all these abstractions reduce themselves back to some concrete instance at the end of it all. But if you can trust in them, you need not worry about that, a good abstraction lets you forget and ignore the complex details underneath it, freeing you to focus on more of your higher level concerns progressively closer and closer to your real domain problem and away from the computer machine concerns. Finally enterprise software reached a point where managing complexity got difficult, so people tried to promote best practices they had learned, basically ways to fit in more abstractions in certain situations that again benefited them greatly. There was a big push to advocate for "design patterns" and other judicious use of abstractions. Lots of people, often mid-level developers, including me at the time, we went seeking for advice, while we didn't understand why, what's the need that drove this advice, what's the use that benefits from it, we took them to heart: SOLID principles, GRASP, YAGNI, inheritance, interfaces, composition, we took it all at face value and tried to arbitrarily use our limited understanding of them everywhere we could, religiously and impartially. This frivolous misuse of abstractions yielded the plagued over-engineered, obfuscated, puzzle-like, code bases that a lot of enterprise software suffers from. Where the hell is the actual code doing the actual thing? This had more senior engineer once again try to push some new "best practice", a new commandment to amend for the misunderstanding of the prior ones: "less abstractions is generally better". Or in other words, just use the abstractions more experienced people have already put in place, stick to your popular framework, follow its existing patterns, stick to simple usage of your programming language, and don't try to be smarter than you are, aka too clever. This is great advice, I'm absolutely in support for it, and to some developers, they're not ready to hear the more nuanced version of it, it might lead them down the wrong path again. But, my point is, good abstractions are really awesome, and by definition of what makes them "good" is that they actually help rather than hurt. There's countless examples of good abstractions throughout the history of software development. There's even so many more minor abstractions that everyone implements on a daily basis without even realizing that once again being better at results in better code, like simply choosing what the method will be and what the arguments and return value for it will be. Or choosing where the data will live. So my point is, in my opinion, a senior engineer is one that knows about the "generally" part of "less abstractions is generally better". A senior engineer knows exactly when less abstractions is better and when more abstractions is better. Don't stomp your growth by once again being religious about a best practice and arbitrarily being against all abstractions because the best practice said to try to avoid them.
- userbinator 4y agoI blame this tendency to overabstract on the emphasis on top-down design / teaching methods. Beginners are taught to abstract whenever possible, and aren't taught when to stop. They don't see the reason behind it, and instead add abstractions dogmatically, dramatically increasing complexity in the process. When abstraction is used well it definitely decreases effort and increases flexibility, but all too often it's overused and results in "object-oriented obfuscation" instead.
- watwut 4y agoI dont know ... way more common problem I see is unwillingness to abstract. Spaghettis are way more frequent than massive abstractions. Now the popular thing is move toward functional-like style, which leads to one stream of flow that is quite difficult to decipher.
- _gabe_ 4y ago> which leads to one stream of flow that is quite difficult to decipher. By the definition of one stream of flow, this is literally easier to follow lol. One stream of flow as opposed to what? Several streams that branch and intermingle? Spaghetti is several intermingling branching streams which is very hard to follow. Following one stream is easy, you just follow the stream /shrug
- watwut 4y agoNo it is not easier to follow practically. Second, definition "stream of flow" is not "easier to follow". It is "one steam of code". It forces you to have all the ifs and for in head all the time and does not explains what those means. Yes, opposed to named structures you can understand in isolation and then treat as units.
- tempodox 4y agoAn approach that has been working for me is to work bottom-up. Think about what basic functionalities you need, and start implementing them. Once they work properly, you can start composing an orchestrating them to larger units. That way you're less prone to build Babylonian towers of superfluous abstractions and indirections. Doing it in a way that's maintainable and extensible should come with experience.
- wruza 4y agoProbably unpopular/heretic sub-opinion: if given a chance to change the past, I’d rather NOT read books like TAOUP and other books on the same shelf. Or at least wouldn’t take them close to the heart. Because instead of collecting my own experience and fitting it to my projects, I’ve invested heavily in these patterns and rules and “gems” and built something in me that I now have to destroy with advanced therapy (not kidding). Last few weeks I said screw it (as a self-forced experiment, because I get anxious without structure, abstractions, etc) and began to write “just code” without any pre-principles, only using programming methodics as an extreme measure. It’s like I’ve never felt better than that. Like walking new streets after you’ve been paralyzed for years. I write f--king code like I’m 15, it is easy and simple, time to deploy / market / test ideas is several times less. My boss gets happily confused being not sure what’s left for the next week, I hear it in his voice. I still have huge respect to Fathers like ESR, but… just make sure this knowledge makes you any good, okay? I don’t think I’ll stop this experiment any soon. Maybe will reassess everything in a year or so.
- mrtksn 4y agoThe more I learned about "best practices" the less productive I've become. I think it happens because I spend my mental energy on solving the problem in a way that fits those so called best practices instead of solving it however I can in the most robust way possible. It took me quite a bit time to re-learn to write a code that solves my problems and is inelegant enough that I can jump on it and modify it as my needs change without thinking how to do it elegantly again. I start to think that the tools must fit elegantly to the domain, the solution built with these tools can only then be elegant. Code can get hard to read and maintain when the way of thinking about a problem doesn't mach well the way of the toolset works. Things get messy when you try to think of ways to make your tools work in a way they are not designed to. For example, there are some domain specific languages and frameworks for stuff like maths or physics or engineering that work the way the mathematician or physicist or an engineer will think about a problem. If you try to make the code made with these elegant in a sense that it's optimised and nicely structured from software developers perspective, it will be a huge mess and very hard to understand from the mathematician/physicist/engineer perspective. Therefore, when working on something I find that the most productive AND maintainable code is the one that matches my thought process - no matter how many sins(like repeating myself or writing non-reusable) are commit. Also, optimisation for the sake of the optimisation is evil. Abstractions work well only when they are intended to match the mental model in the solution and are evil when they are made to optimize something(like making it re-usable for all kind of situations).
- yobbo 4y agoThere are also abstractions that are so good they are invisible until someone tries to reinvent the wheel without them. They are then forced to confront some reality which is more complex than they thought. What seems to be the common thread is: "you are not as good as you think you are". Never enough humility.
- tpoacher 4y agoI don't understand this distinction between futureproof vs simple code. I would have thought they are the same.
- chii 4y agoThey might be the same, but often, future proof code is written with a future requirement in mind, and extra "hooks" added in to allow easy addition of that future requirement. For example, you might add an orm to abstract the db specific SQL, even though you only run off one database type, because it allows you to switch in the future.