49 ms·
For ages now, I've been telling people that the best best code, produced by the most experienced people, tends to look like novice code that happens to work ---
by quotemstr 10y ago
For ages now, I've been telling people that the best best code, produced by the most experienced people, tends to look like novice code that happens to work --- no unnecessary abstractions, limited anticipated extensibility points, encapsulation only where it makes sense. "Best practices", blindly applied, need to die. The GoF book is a bestiary, not an example of sterling software design. IME, it's much more expensive to deal with unnecessary abstraction than to add abstractions as necessary.
People, think for yourselves! Don't just blindly do what some "Effective $Language" book tells you to do.
(For starters, stop blindly making getters and settings for data fields! Public access is okay! If you really need some kind of access logic, change the damn field name, and the compiler will tell you all the places you need to update.)
- jcoffland 10y agoOne of my guiding principals as a programmer is to never add code because I might need it some day.
- quotemstr 10y agoStill, apply common sense. Sometimes the probability of adding something is so high and the cost of making an extension point so low that it makes sense to design with extensibility in mind. These extension points are rare, though, and usually occur at major module boundaries (e.g., plugins), and don't need to be scattered through random bits of your code.
- jcoffland 10y agoStill, why not wait to add it?
- cle 10y agoThe cost to add it is often much higher, once the code has high fan in (lots of code paths depend on it). I am definitely a fan of not over engineering, but I'm more of a fan of thinking. Think about your problem and your use cases, and about your own ability to predict the future. If you can predict future changes with high probability, then go ahead and design for those. If you're not a domain expert, you probably can't predict the future at all (and you probably grossly underestimate how bad your predictions are), so you should stick to the bare minimum.
- jcoffland 10y agoIt's a slippery slope.
- Bahamut 10y agoBecause sometimes those choices have a large effect on the amount of work/mental tax in the future - for example, if you know that a new feature will have to interop with yours in the near future and the cost is low to implement the right extensibility point now, it would be absolutely stupid not to - I would call that bad engineering that costs time & effort. Obviously one doesn't want to go down the rabbit hole too early, but the other extreme is just as bad.
- ArkyBeagle 10y agoWaiting is good when the cost model for the added code is much more in focus later.
- sbov 10y agoYou also have to factor in the cost to maintain your extension which you currently have no use for, which most people forget. It also might be easy to include now, but your extension might complicate a new feature request. Or the new feature might complicate your previously simple extension. If it's still unused, don't be afraid to throw it away then either.
- Gibbon1 10y agoI find that unneeded functionality also complicates refactoring efforts.
- saint_fiasco 10y ago>the best best code, produced by the most experienced people, tends to look like novice code that happens to work Could it be that best practices are designed to make sure mediocre programmers working together produce decent code? After all, actual novice programmers write code similar to the best programmers except that it doesn't work.
- Retra 10y agoI think it's more that the best practices were adopted for specific reasons, but they are not understood or transmitted in a way that makes those reasons very clear. That is, a 'best practice' tends to solve a specific kind of problem under a certain set of constraints. Nobody remembers what the original constraints are or if they even apply in their current situation, even if they are actually trying to solve the same problem, which they might not be.
- RobertKerans 10y agoThis spirals as well: I've spent quite a lot of time recently helping people at the early stages of learning programming, and it's taken me a while to stop becoming frustrated with their misunderstandings. Sometimes it is just because something basic isn't clicking for them, but a lot of the time it's down to me trying to explain things through a prism of accumulated patterns that are seen as best practise/common sense, but stepping back and viewing objectively are opaque and sometimes nonsensical outside of very specific scenarios. There's a tendency to massively overcomplicate, but you forget very quickly how complicated, then you build further patterns to deal with the complexity, ad infinitum
- JamesBarney 10y agoYep the biggest issue we have with software is every developer knows Single Responsibility Principle, and no developers know why. Everyone knows decouple and increase cohesion, but few any know what cohesion is.
- mavelikara 10y ago"It took me four years to paint like Raphael, but a lifetime to paint like a child." - Picasso
- jwatte 10y agoThe GoF book is a dictionary. When it came out, we all got names for patterns we use when appropriate. The thing that must die is over-reliance on formalisms where they don't add value. (Because there absolutely are places where they do.) Having the taste to use the right approach for each job is what sets experience apart!
- rattray 10y agoSorry, what is the GoF book?
- detaro 10y agoDesign Patterns: Elements of Reusable Object-Oriented Software, whose four authors are sometimes called the Gang of Four (GoF). It established a vocabulary of common patterns. https://en.wikipedia.org/wiki/Design_Patterns https://en.wikipedia.org/wiki/Design_Patterns
- rattray 10y agoThanks very much!
- jwatte 10y agoGetters/setters are important when the data structure is used from a different linkage unit than where it is defined, and binary compatibility across versions is important. That doers happen, but usually in fewer cases than most intermediate programmers realize. Instead, they see cases where it is used for real (like winforms or Direct3D) and cargo cult it into all code they write.
- blahedo 10y ago> they see cases where it is used for real (like winforms or Direct3D) and cargo cult it into all code they write. And a lot, a lot, of my CS prof colleagues believe it is deeply important, for reasons most of them are unable to articulate, that in an intro CS1 class all instance variables be private and accessed only through getters and setters. This is actually baked into the College Board's course description for AP CS A, and a point or two (out of 80) often hinges on it in every exam, so it is near-universally taught in high-school level CS classes (in the US). Sigh.
- deleted 10y ago[deleted]
- qwertyuiop924 10y agoSo I have to write BS for AP CS A, and I have to do it in a bad language, and we'll never get Python or Lisp? Okay, College Board, I warned you... smack this is for the above. smack and this is for teaching CS poorly in general. smack and this is for making us all by overpriced graphing calculators, thus keeping demand high...
- quotemstr 10y agoThe kinds of things you need to do in order to maintain a stable ABI are not the kinds of things you should apply to all parts of your program. That way lies madness. BTW, you don't need accessors even for public ABIs. stat(2), for example, doesn't need "accessors" for struct stat and it's been stable for decades. The same idea applies to Win32 core APIs.
- tigershark 10y agoAre you seriously saying that the best code is an untestable mess of big God classes? Because in my experience this is by far the type of code written by inexperienced programmers. Abstractions and interfaces are the best way to make a system testable and extensible and it has nothing to do with using a pattern just because you read about it in the gof book 5 minutes ago. And using public fields in a non trivial project is a sure receipt for disaster.
- blablabla123 10y agoI think he is rather referring to cascades of function calls/types and "custom solutions". Of course abstractions are a great tool when used properly. Thinking of a best practice from Object oriented design: low coupling but high cohesion. When you use abstractions, you get low coupling. But like everything in life, also this has a price: you may need an extra line of code to instantiate the abstraction and sometimes have to write an extra getter because it's not perfect yet. That's a fine price to pay unless go you use way too many abstractions and they are nested deeply. It may have some aesthetics but it can be hell to debug and overly complicated to extend code. So that's why one must also focus on high cohesion. I really like the modern JavaScript way, the imports/requires are for low coupling and the build automation is for cohesion. Anyways, these things are best practices as well but not sure if the author took those into account... ;)
- et1337 10y ago> using public fields in a non trivial project is a sure receipt for disaster. This is just dogma. Every Python project in existence has 100% public fields. Some are disasters, some are beautiful. Only a Sith deals in absolutes.
- eximius 10y agoBetter than ugly, beautiful is. Better then implicit, explicit is. Better than complex, simple is. Better than complicated, complex is. Better than nested, flat is. Better than dense, sparse is. Counts, readability does. Special enough to break the rules, special cases are not. But beaten by practicality, purity is. Silently passed, an error should never be. Unless explicitly, is it silenced. In the face of ambiguity, the temptation to guess, refuse you must. One, preferably only one, way to do it, there should be. Not obvious, it might be. Better than never, is now. But often better than right now, is never. If hard to explain, bad it is. If easy to explain, good it may be. Namespaces are a honking good idea - more of them we should do! -- The Zen of Python, Yoda
- sanderjd 10y agoThose "Effective $Language" books make arguments for their advice, which you are free to find compelling, or not. Back when I first read Effective Java, maybe about a decade ago, I thought it was over-complicated hogwash that I knew better than. When I read it again starting about a year ago, I found the arguments for most of its advice very compelling, based on problems I've run into time and again in my own, and other people's, code, and not just in Java. YMMV I suppose! The backlash against "engineered" software definitely seems real, and I think that's great - questioning assumptions is critical - but I think a lot of the insinuations about peoples' motivations and talents are unnecessary and honestly kind of silly. Most of us are just trying to find ways to avoid issues we've seen become problems in other projects in the past, it's not some nefarious conspiracy against simple code.
- qwertyuiop924 10y agoBlind application needs to die in general. SOLID is a good idea (it should just be SID, IMHO, but anyways), so are design patterns... when they make sense. When you think "I need to delegate object construction to different subclasses contextually" use a factory. Don't use a factory when you don't think that. When you need to chose an approach that should be given by the caller, no, for the love of god don't use the Strategy pattern. The strategy pattern is a hack that belongs in the past, and you should use lambdas instead because it's the 21st freaking century, not 1996, and we all have lambdas now. So don't use strategy, unless you're stuck behind, and if you do, feel bad about it. /rant Anyways, yeah, just use your brain, insist on defined interfaces, and if something might be suboptimal, allow for it to be changed, and you'll be fine.
- quotemstr 10y ago> The strategy pattern is a hack that belongs in the past, and you should use lambdas instead In that case, your lambda object is your strategy. One of the good points that the "Design Patterns" authors make and that everyone else ignores is that these patterns are names for recurring structures that pop up independently of implementation language and environment. Some silly functor object is one way of implementing the more abstract "Strategy" pattern. Your lambda is a better one in many cases. It's the same high-level concept.
- qwertyuiop924 10y agoWell, yes, but if you every write a class for C#, modern Java, or pretty much any language save C++ or Python (and even then you should write functions) that has the word "strategy" in it, you're doing it wrong, and should be punished for reinventing language features using OO methodology. Was it Steve Yegge who menationed the Perl community calling Design Patterns "FP for Java"?
- quotemstr 10y agoI don't know, but this Yegge essay is one of my favorites ever: http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom...
- bartread 10y ago^^^This. The tendency for a certain type of software engineer to go around telling everyone else they're doing it wrong reminds me strongly of this scene from Justified: https://www.youtube.com/watch?v=LG4hOjJ9tEs https://www.youtube.com/watch?v=LG4hOjJ9tEs.
- crististm 10y agoThe problem with their advice is that they are not master Foo. They could tone it down a little for their wisdom is not absolute. Some rules works for some people in some situations and other rules are just practical conventions. One good advice I found is to just plain ignore status quo, and follow common sense when the context demands it.
- jimmaswell 10y agoThe getter/setter problem is solved very nicely by C#. You can change a field to a property at any time without changing the rest of the code, and a lot of the use cases for get/set can be handled compactly like public int X{get; private set;} instead of having to have 2 variables.
- userbinator 10y agoFor ages now, I've been telling people that the best best code, produced by the most experienced people, tends to look like novice code that happens to work --- no unnecessary abstractions, limited anticipated extensibility points, encapsulation only where it makes sense. In other words, the best code is the simplest code that works. It usually tends to be very flexible and extensible anyway, because there is so little of it that understanding it all and modifying it becomes easy. The most experienced programmers are the ones who can assess a problem and write code to capture its essence, and not waste time doing that which isn't necessary. I've observed that there is a "spectrum of complexity" with two very distinct "styles" or "cultures" of software at either end; at one end, there is the side which heavily values simplicity and pragmatic design. Examples of these include most of the early UNIXes, as well as later developments like the BSD userland. The code is short, straightforward, and humble, aptly described by "novice code that happens to work". At the other end, there's the culture and style of Enterprise Java and C#, where solving the simplest of problems turns into a huge "architected" application/framework/etc. with dozens of classes, design patterns, and liberal amounts of other bureaucratic indirection. The methodology also tends to be highly process-driven and rigid. I don't think it's a coincidence that the latter is heavily premised on and values "best practices" more than anything else. Here's another one of the "rebellion" articles against "best practices": ttp://www.satisfice.com/blog/archives/27
- ScottBurson 10y agoFor ages now, I've been telling people that the best best code, produced by the most experienced people, tends to look like novice code that happens to work --- no unnecessary abstractions, limited anticipated extensibility points, encapsulation only where it makes sense. I love this. "Perfection is reached, not when there is nothing left to add, but when there is nothing left to take away." -- Antoine de Saint-Exupery Have to disagree a little on getters and setters though. They're tremendously useful as places to set breakpoints when debugging. Well, setters are, anyway; I guess it's rarer that I'll use a getter for that. Anyway, perhaps we can agree that the need for these is a design flaw in Java; C# has a better way.