4 ms·
No, it isn’t that black and white. Many of Martin’s suggestions are silly, especially when applied to languages that are more expressive (like Ruby, CoffeeScrip
by halostatue 13y ago
No, it isn’t that black and white. Many of Martin’s suggestions are silly, especially when applied to languages that are more expressive (like Ruby, CoffeeScript) or otherwise have no relationship to Java.
I distinctly remember arguments with people who were reading Clean Code at the time who really believed that one-line methods really were the ideal.
The trick is knowing what does “doing more than they should” actually mean? If you follow Clean Code as I’ve seen some people follow it (including the Ruby dev shop I mentioned elsecomment), you will end up having substantially larger interfaces at the cost of understanding just what it is that your program is supposed to do in the first place.
I’ve dealt with programs written the Clean Code way (an explosion of methods, classes, and objects), as well as programs written with too much going on in one place (e.g., 2+kloc functions). Neither is fun to deal with, but it’s generally easier to assimilate the basic business purpose of the 2+kloc function than something that has a larger interface surface than it needs to have.
- joshka 13y agoYour assertions are interesting. I'd like to see some concrete examples of where you've seen this actually play out to make not well understood code. Having recently read Clean Code as a C# dev, I feel the missing piece in this conversation is that the advice of small methods needs to be taken together with the idea that things should do one thing, and should be composed of items at the same level of abstraction. I feel the idea is hard to defend on its own, but with the other ideas quite easily defended. I'm also curious about the performance problems you've mentioned above.
- halostatue 13y agoI’ll address the performance problems first. Following the advice of Clean Code, you can exponentially increase the size of your object model and the number of methods on those objects without even trying. This can cause memory pressure and increased dispatch times. If you're incautious and don't pay attention to your object creation/destruction cycle in a tight loop, you're in for a world of hurt (no surprise there). The (potentially much) larger object model of many little things that do Just One Thing makes it harder to effectively reason about the system and understand how those object allocation/deallocation cycles can be killing your performance. This is precisely the sort of thinking that leads to FactoryFactoryFactories: some things can't meaningfully be broken down without losing the point of the abstraction. The problems with Clean Code are legion. It makes axiomatic arguments and then drives them further in the name of purity—which is anathema to shipping code, clean or otherwise. It is deeply rooted in the Java available in 2007–2008 and shows no awareness of just how broken Java was as a programming language at the time in comparison with many other languages. It makes prescriptions of things that are merely good advice. By all means, simplify your code. That does not mean making lots of little objects and lots of little methods. It means that your code should be as simple as possible and no simpler. Sometimes this means that you’re going to have methods that are a few dozen lines long, because splitting them apart increases the state you need to carry around between those methods. Sometimes it means taking advantage of features in your language that Clean Code (being an Old Java book, despite its grandiose title) exhibits no awareness of (even though, with generics, it could). It’s been a few years since did this, but to introduce the C# developers at a previous job to the ideas provided by C#’s functional constructs, I wrote a particular piece of code four or five times and wrote a substantial email about it describing the simplifications that the constructs provided at each step of the way, and ended it with the equivalent Ruby code, which was more or less: return container.any? { |e| e.subcontainer.any? { |f| f.boolean and f.subcontainer.any? { |g| g.boolean } } } Written iteratively, it took about 50 lines to achieve this, and required state to know when you had broken out of a particular loop (#any? is short-circuiting; as soon as anything matches, you’re clear). Written with several smaller methods each acting iteratively, it also took about 50 (you lose state tracking because you can break out with a return from each function), and you lose readability at the call-site (you now have to jump to a different function for each item to understand the logic). Written with named delegates, it took about 40, but was harder to understand since the logic was separated from the call-site. Written with anonymous delegates in C#, it took about 30—and was fairly understandable (readily understandable if you already understand anonymous delegates). The equivalent Ruby code was a third of that. The most readable code kept the logic at the call-site and used powerful features in the language; the next-most readable code was the iterative code. The above code clearly violates the rules of Clean Code; your method at object level D ('container') knows something about objects at level F and G, but making (and naming) methods at levels D, E, F, and G to return truth when you do not need those methods anywhere else? Foolish inconsistency, and Clean Code is full of examples like that where, given a better language (or even a better use of the Java language and generics), you don’t need or want to do those things. Clean Code is a dangerous book because it takes generally good ideas and applies them as Truth, and many of the people who should be reading and learning from a book like Clean Code simply don’t know any better and fall into even worse habits than if they had never read the book. Given a choice between following complex business logic across lots of little classes and files and a few larger functions, I know which I would choose.