6 ms·
I think this highlights a common issue when development is (or feels) rushed. You either end up with developers having only done the first part of each of these
by lostcolony 11y ago
I think this highlights a common issue when development is (or feels) rushed. You either end up with developers having only done the first part of each of these pairs (repeating themselves, ball of mudding, etc), without time to clean up as part of each iteration, or, you find developers immediately shooting for the latter half of each pair (DRY, modular, etc) without having done the former, and so you get abstractions that make no sense, overly complex interactions in a shared function as they attempt to be DRY, etc.
This latter is also, I feel, what informs a lot of the monolithic frameworks used for 'enterprise' development, Spring and the like, where a predetermined architecture and structure of the app is imposed by the framework, and which leads to, once you get down in the weeds of dealing with odd edge cases and things, hackery on the part of the developer, or framework bloat if the framework attempts to address the most frequent of those cases.
- buren 11y agoCouldn't agree more. Sandi Metz has an excellent talk touching on this topic. Developers exaggerated willingness to keep things DRY and elaborates a bit on why: it's one of the easiest things for a not-so-experienced developer to identify and one of the easiest things to teach. Edit: One of the best quotes from that talk is (paraphrased): "The wrong abstraction is a lot more expensive than duplicated code". https://youtu.be/OMPfEXIlTVE https://youtu.be/OMPfEXIlTVE
- deleted 11y ago[deleted]
- buren 11y agoCouldn't agree more. Sandi Metz has an excellent talk touching on this topic. Developers exaggerated willingness to keep things DRY and elaborates a bit on why: it's one of the easiest things for a not-so-experienced developer to identify and one of the easiest things to teach.
- filipedeschamps 11y agoYour first paragraph just nailed. You were able to mix the post content with real world mechanics. One thing I find really hard is where to draw the line to an uncleanable ball of mud.
- lostcolony 11y agoFor myself, I find the most important thing is to have clear interfaces (contracts). That is, I can write the hackiest code inside of a module, but I will spend time upfront to make sure that what that module exposes is the cleanest I can make it. Then, I can isolate and fix a function by itself. It may have been written to be 200 lines long, filled with hackery and half measures, but my complexity is contained within it, and the functions it calls out to (so nothing calling it should need to be substantively changed). Those called functions, in turn, may also need improvements, but my focus every step of the way was keeping the interfaces clean, so I can always get down to some base set of functionality that has no dependencies, fix that up, make the necessary fixes to keep things working in the functions directly calling them, test, and then move up to those calling functions and repeat, until I get back up to the big ugly 200 line monstrosity. Every step of the way I can make sure things are still working, and I don't have to substantively touch anything above the 200 line monstrosity until I've cleaned it up. So by keeping my interfaces clean, I can figure out how, inductively, I can make progress. The lack of clear interfaces is the real problem; if you don't put in the effort when writing your code to keep those clean, you end up with circular dependencies, implicit workflows and state between functions ("to call X you first have to call Y to get a foo, pass that into X, then call Z to reset the foo value"), and other nightmares, that move you more to needing a full rewrite.
- jstimpfle 11y agoAlso, plain old data interfaces are very desirable. They decrease coupling. Get the data off the interface and restructure it (renaming, preprocessing...) in a suitable way before implementing the actual functionality. This decrease in coupling makes representation changes in the interface more practiceable. Of course sometimes stateful (procedural) interfaces are a must, but it's surprising how many painful OOP classes can be replaced with a "const struct foo" interface with a clear meaning to the data in it.
- mmatants 11y agoFunny you mention monolithic code. The post expressed a very important thought: > we’re trying keep the parts that change frequently, away from the parts that are relatively static Modularization and good code contracts help "sequester" code parts with different rates of change. And this is my guiding hunch when I am splitting up code and deciding on module surfaces: how confident are we in the subject matter that given code describes? This does not replace actual proper concept modeling, but it is an intuitive feel for helping future iterations go smoothly.
- michaelfeathers 11y agoIntuition is great but we often forget that we can use churn stats too. find . -name "*.rb" |xargs -n1 -I file sh -c 'echo `git log --oneline file | wc -l`: file'|sort -nr Applying a time period window is good too as some parts of code churn for a while and them reach stability.
- voltagex_ 11y agoWhat does that command do?
- chairleader 11y agoBy my read, it prints a list of all Ruby files in your project sorted by number of commits against them. It may take a long time to complete depending on number of files and number of commits. YMMV, I am not a BASH shell.
- voltagex_ 11y agohttp://explainshell.com/explain?cmd=find+.+-name+%22*.rb%22+%7Cxargs+-n1+-I+file+sh+-c+%27echo+%60git+log+--oneline+file+%7C+wc+-l%60%3A+file%27%7Csort+-nr http://explainshell.com/explain?cmd=find+.+-name+%22*.rb%22+... doesn't quite get it either, but I think you're right. I'm not near a machine with Bash to check.
- sjg007 11y agoYeah.. Spring batch comes to mind.