11 ms·
Yes let's please do this. I'm tried of this book being brought up at work. My clean code book: * Put logic closest to where it needs to live (feature folders
by dham 5y ago
Yes let's please do this. I'm tried of this book being brought up at work.
My clean code book:
* Put logic closest to where it needs to live (feature folders)
* WET (Write everything twice), figure out the abstraction after you need something a 3rd time
* Realize there's no such thing as "clean code"
- anonyfox 5y ago> WET (Write everything twice), figure out the abstraction after you need something a 3rd time so much this. it is _much_ easier to refactor copy pasta code, than to entangle a mess of "clean code abstractions" for things that isn't even needed _once_. Premature Abstraction is the biggest problem in my eyes. Write Code. Mostly functions. Not too much.
- allo37 5y agoI think where DRY trips people up is when you have what I call "incidental repetition". Basically, two bits of logic seem to do exactly the same thing, but the contexts are slightly different. So you make a nice abstraction that works well until you need to modify the functionality in one context and not the other...
- bradstewart 5y agoSo much this. Especially early in project's life when you aren't sure what the contexts/data model/etc really need to be, so much logic looks the same. It becomes so hard to untangle later.
- fiddlerwoaroof 5y agoIf you mostly deduplicate by writing functions, fixing this problem is never very hard: duplicate the function, rename it and change the call-site. The interesting thing about DRY is that opinions about it seem to depend on what project you’ve worked on most recently: I inherited a codebase written by people skeptical of DRY, and we had a lot of bugs that resulted from “essential duplication”. Other people inherit code written by “architecture astronauts”, and assume that the grass is greener on the WET side of the fence. Personally, having been in both situations, I’d almost always prefer to untangle a bad abstraction rather than maintain a WET codebase.
- jakelazaroff 5y agoConversely, fixing duplication is never hard. Just move the duplicated code into a function. Going in reverse can be much tougher if the function has become an abstraction, where you have to figure out what path each function call was actually taking. Or, put another way: I'd much rather deal with duplication than with coupling problems.
- fiddlerwoaroof 5y agoThe issue I have is that duplication is a coupling problem, but there’s no evidence in the coupled parts of the code that the coupling exists. It can be ok on a single-developer project or if confined to a single file, but my experience is that it’s a major contributor to unreliability, especially when all the original authors of the code have left.
- jeremysalwen 5y agoI think the opposite is true. Bad abstractions can be automatically removed with copy/paste and constant propagation. N pieces of code that are mostly the same but have subtle differences have no automatic way to combine them into a single function.
- 0xffff2 5y agoN pieces of code that are mostly the same but have subtle differences isn't repetition and probably shouldn't be combined into a single function, especially if the process of doing so it non-obvious.
- jakelazaroff 5y agoI think they mean if the code does the same thing but has syntax differences. Variables are named differently, one uses a for loop while the other uses functional list operations, etc.
- bluGill 5y agoOften it is. Earlier in this thread I used the example of a unit system - one example of there there can be a ton of repetition to remove, but there are fundamental differences between liters and meters that make removing the duplication hard if you didn't realize upfront you had a problem. Once you get it right converting meters to chains isn't that hard (I wonder how many reading even know chain was a unit of measure - much less have any idea how big it is), but there are a ton of choices to make it work.
- SassyGrapefruit 5y agoYep. Re-use is difficult. When you overdo it you cause a whole new set of problems. I once watched a person write a python decorator that tried unify HTTP header based caching with ad hoc application caching done using memcached. When I asked exactly what they were trying to accomplish they kept saying "they didn't want caching in two places". I think anyone with experience can see that these items are unrelated beyond both having the word "cache" This is what was actually hanging them up ... the language. Particularly the word "cache". I've seen this trap walked into over and over. Where a person tries to "unify" logic simply because two sets of logic contain some of the same words.
- watwut 5y agoThen you copy function and modify one place? I don't get what is so hard about it. The IDE will even tell you about all places where function is called, there is no way to miss one.
- ummonk 5y agoThis, exactly this
- nodoodles 5y ago> Write Code. Mostly functions. Not too much. Just wanted to appreciate the nod to good nutritional advice here ("Eat food. Mostly plants. Not too much"), well done
- latk 5y agoProbably a call-back to this post that made the rounds on HN a few months ago: https://www.brandons.me/blog/write-code-not-too-much-mostly-functions https://www.brandons.me/blog/write-code-not-too-much-mostly-...
- pjc50 5y ago> it is _much_ easier to refactor copy pasta code So long as it remains identical. Refactoring almost identical code requires lots of extremely detailed staring to determine whether or not two things are subtly different. Especially if you don't have good test coverage to start with.
- tuatoru 5y ago> requires lots of extremely detailed staring Yeah, nah. There are diffing tools.
- cerved 5y agoproblem is you only diff if things look different
- macNchz 5y agoI personally love playing the game called "reconcile these very-important-but-utterly-without-tests sequences of gnarly regexes that were years ago copy-pasted in seven places and since then refactored, but only in three of the seven places, and in separate efforts each time".
- beckingz 5y ago"Write Code. Mostly Functions. Not too much" made my day and is excellent advice.
- macNchz 5y ago>it is _much_ easier to refactor copy pasta code I totally agree assuming that there will be time to get to the second pass of the "write everything twice" approach...some of my least favorite refactoring work has been on older code that was liberally copy-pasted by well-intentioned developers expecting a chance to come back through later but who never get the chance. All too often the winds of corporate decision making will change and send attention elsewhere at the wrong moment, and all those copy pasted bits will slowly but surely drift apart as unfamiliar new developers come through making small tweaks.
- _huayra_ 5y agoI worked on a small team with a very "code bro" culture. No toxic, but definitely making non-PC jokes. We would often say "Ask your doctor about Premature Abstractuation" or "Bad news, dr. says this code has abstractual dysfunction" in code reviews when someone would build an AbstractFactoryFactoryTemplateConstructor for a one-off item. When we got absorbed by a larger team and were going to have to "merge" our code review / git history into a larger org's repos, we learned that a sister team had gotten in big trouble with the language cops in HR when they discovered similar language in their git commit history. This brings back memories of my team panicked over trying to rewrite a huge amount of git history and code review stuff to sanitize our language before we were caught too.
- icedchai 5y agoIf you were "caught", what would happen? You probably have a couple of zoom calls and get forced to watch sensitivity training videos. Who cares.
- morbicer 5y agoWait whaat? I am not from USA and hail from a much more blunt culture, what's wrong with those 2 statements? To me they're harmless jokes but maybe someone will point out they're ableist?
- _huayra_ 5y agoIt wasn't just these phrases, but we took them out to be safe (thinking that them being jokes about male... "performance", like from pharma ads on TV with an old man throwing a football through a tire swing). The biggest thing is that we used non-PC language like "retarded" very casually, not to mention casually swearing in commit messages e.g. "un-fucking up my prior commit". Our sister team got in trouble for "swears in the git commit history", so we wanted to get ahead of that if possible. In a healthy company culture, we'd just say "okay we'll stop using these terms", but the effort was made to erase their existence because this was a company where non-engineering people (e.g. how well managers and HR liked you) was a big factor in getting promoted. Once I realized how messed up that whole situation was, I left as fast as I could.
- haspok 5y ago> Write Code. Mostly functions. Not too much. Think about data structures (types) first. Mostly immutable structures. Then add your functions working on those structures. Not too many.
- throwaway2037 5y agoOMG. This is exactly my experience after trying to write code first for 10+ years. (Yes, I am a terrible [car] driver, and a totally average programmer!) "Bad programmers worry about the code. Good programmers worry about data structures and their relationships." - Linus Torvalds He wasn't kidding! And the bit about "immutable structures". I doubted for infinity-number-of-years ("oh, waste of memory/malloc!"). Then suddenly your code needs to be multi-threaded. Now, immutable structures looks genius!
- dragonwriter 5y ago> it is _much_ easier to refactor copy pasta code, Its easy to refactor if its nondivergent copypasta and you do it everywhere it is used not later than the third iteration. If the refactoring gets delayed, the code diverges because different bugs are noticed and fixed (or thr same bug is noticed and fixed different ways) in different iterations, and there are dozens of instances across the code base (possibly in different projects because it was copypastad across projects rather than refactored into a reusable library), the code has in many cases gotten intermixed with code addressing other concerns...
- cerved 5y agoThere's a problem with being overly zealous. It's entirely possible to write bad code, either being overly dry or copy paste galore. I think we are prone to these zealous rules because they are concrete. We want an "objective" measure to judge whether something is good or not. DRY and WET are terms often used as objective measures of implementations, but that doesn't mean that they are rock solid foundations. What does it mean for something to be "repeated"? Without claiming to have TheGreatAnswer™, some things come to mind. Chaining methods can be very expressive, easy to follow and maintain. They also lead to a lot of repetition. In an effort to be "DRY", some might embark on a misguided effort to combine them. Maybe start replacing `map(x => x).reduce(y, z => v)` with `mapReduce(x => x, (y,z) => v)` This would be a bad idea, also known as Suck™. But there may equally be situations where consolidation makes sense. For example, if we're in an ORM helper class and we're always querying the database for an object like so `objectContext.Orders.Select(e => e.id = y).Include(e => e.Customers).Include(e => e.Bills).Include(e => e.AwesomeDogs)...` then it with make sense to consolidate that into `orderIncludingCustomersBillsAndDogs(id) => ...` My $0.02: Don't needlessly copy-pastes that which is abstractable. Don't over abstract at the cost of simplicity and flexibility. Don't be a zealot.
- BurningFrog 5y agohttps://en.wikipedia.org/wiki/Rule_of_three_(computer_programming) https://en.wikipedia.org/wiki/Rule_of_three_(computer_progra...
- innagadadavida 5y agoHow do you deal with other colleagues that have all the energy and time to push for these practices and I feel makes things worse than the current state?
- Spivak 5y agoExplain that the wrong abstraction makes code more complicated than copy-paste and that before you can start factoring out common code you need to be sure the relationship is fundamental and not coincidental.
- bluGill 5y agoAnd get ignored or laughed at. Some people know the rules and nothing but.
- 29athrowaway 5y ago> Realize there's no such thing as "clean code" You can disagree over what exactly is clean code. But you will learn to distinguish what dirty code is when you try to maintain it. As a person that has had to maintain dirty code over the years, hearing someone saying dirty code doesn't exist is really frustrating. Noone wants to clean up your code, but doing it is better than allowing the code to become unmaintainable, that's why people bring up that book. If you do not care about what clean code is, stop making life difficult for people that do.
- rendall 5y ago> hearing someone saying dirty code doesn't exist is really frustrating Not sure why you're being downvoted, but as an unrelated aside, the quote you're responding to literally did not say this. > that's why people bring up that book I think the point is that following that book does not really lead to Clean Code.
- 29athrowaway 5y ago> I think the point is that following that book does not really lead to Clean Code. And that is why I started saying "You can disagree over what exactly is clean code". Different projects have different requirements. Working on some casual website is not the same as working on a pacemaker firmware.
- rendall 5y agoYeah, I don't know that we're disagreeing here. For me, there's definitely good code and bad code. I read OP more as saying perfect is the enemy of good, not that all code is good code. I'd say if you want good code in your working environment, set up a process for requiring it. Automated testing and deployment, and code reviews would be the way to go. Reading Clean Code won't get you there.
- ljm 5y agoI think it's more that clean code doesn't exist because there's no objective measure of this (and those services that claim there are are just as dangerous as Clean Code, the book); anyone can come along and find something about the code that could be tidied up. And legacy is legacy, it's a different problem space to the one a greenfield project exists in. > As a person that has to maintain dirty code This is a strange credential to present and then use as a basis to be offended. Are you saying that you have dirty code and have to keep it dirty?
- UglyToad 5y agoI think this ties in to something I've been thinking, though it might be project specific. Good code should be written to be easy to delete. 'Clever' abstractions work against this. We should be less precious about our code and realise it will probably need to change beyond all recognition multiple times. Code should do things simply so the consequences of deleting it are immediately obvious. I think your recommendations fit with this.
- Benjammer 5y ago>code should be written to be easy to delete This article tends to make the rounds on here every once in a while: https://programmingisterrible.com/post/139222674273/how-to-write-disposable-code-in-large-systems https://programmingisterrible.com/post/139222674273/how-to-w...
- msluyter 5y agoAligns with my current meta-principle, which is that good code is malleable (easily modified, which includes deletion). A lot of design principles simply describe this principle from different angles. Readable code is easy to modify because you can understand it. Terse code is more easily modified because there’s less of it (unless you’ve sacrificed readability for terseness). SRP limits the scope of changes and thus enhances modifiability. Code with tests is easier to modify because you can refactor with less fear. Immutability makes code easier to modify because you don’t have to worry about state changes affecting disparate parts of the program. Etc... etc... (Not saying that this is the only quality of good code or that you won’t have to trade some of the above for performance or whatnot at times).
- munificent 5y agoThe unpleasant implication of this is that code has a tendency towards becoming worse over time. Because the code that is good enough to be easy to delete or change is, and the code that is too bad to be touched remains.
- jasonwatkinspdx 5y agoOne of John Carmack's maxims is "Fight code entropy." As simple as that is it carries a lot of meaning imo.
- thefourthchime 5y agoAh hahha. I love WET. I always say the only way to write something correctly is to re-write it.
- HWR_14 5y agoThat's not what WET means. The GP is saying you shouldn't isolate logic in a function until you've cut-and-pasted the logic in at least two places and plan to do so in a third.
- amelius 5y ago> WET (Write everything twice) In practice (time pressure) you might end up duplicating it many times, at which point it becomes difficult to refactor.
- Frost1x 5y agoIf it's really abstractable it shouldn't be difficult to refactor. It should literally be a substitution. If it's not, then you have varied cases that you'd have to go back and tinker with the abstraction to support. It's a similar design and planning principle to building sidewalks. You have buildings but you don't know exactly the best paths between everything and how to correctly path things out. You can come up with your own design but people will end up ignoring them if they don't fit their needs. Ultimately, you put some obvious direct connection side walks and then wait to see the paths people take. You've now established where you need connections and how they need to be formed. I do a lot of prototyping work and if I had to sit down and think out a clean abstraction everytime I wanted to get for a functional prototype, I'd never have a functional prototype--plus I'd waste a lot of cognitive capacity on an abstraction instead of solving the problem my code is addressing. It's best, from my experience, to save that time and write messy code but tuck in budget to refactor later (the key is you have to actually refactor later not just say you will). Once you've built your prototype, iterated on it several times had people break designs forcing hacked out solutions, and now have something you don't touch often, you usually know what most the product/service needs to look like. You then abstract that out and get 80-90% of what you need if there's real demand. The expanded features beyond that can be costly if they require significant redesign but at that point, you hopefully have a stable enough product it can warrant continued investment to refactor. If it doesn't, you saved yourself a lot of time and energy worrying trying to create a good abstract design that tends to fail multiple times at early stages. There's a balance point of knowing when to build technical debt, when to pay it off, and when to nullify it. Again, the critical trick is you have to actually pay off the tech debt if that time comes. The product investor can't look bright eyed and linearly extrapolate progress so far thinking they saved a boatload of capital, they have to understand shortcuts were taken and the rationale was to fix them if serious money came along or chuck them in the bin if not.
- 5y ago
- golergka 5y ago> * WET (Write everything twice), figure out the abstraction after you need something a 3rd time There are two opposite situations. One is when several things are viewed as one thing while they're actually different (too much abstraction), and another, where a thing is viewed as different things, when it's actually a single one (when code is just copied over). In my experience, the best way to solve this is to better analyse and understand the requirements. Do these two pieces of code look the same because they actually mean thing in the meaning of the product? Or they just happen to look the same at this particular moment in time, and can continue to develop in completely different directions as the product grows?
- whimsicalism 5y agoSolving the former is generally way uglier/more obnoxious IMO than solving the latter, esp. if you were not the person who designed the former.
- throw1234651234 5y agoWET is great until JSON token parsing breaks and a junior dev fixes it in one place and then I am fixing the same exact problem somewhere else and moving it into a shared file. If it's the exact same functionality, move it into a service/helper.
- Cthulhu_ 5y agoThis is what I'm doing even while creating new code. There's a few instances for example where the "execution" is down to a single argument - one of "activate", "reactivate" and "deactivate". But I've made them into three distinct, separate code paths so that I can work error and feedback messages into everything without adding complexity via arguments. I mean yes it's more verbose, BUT it's also super clear and obvious what things do, and they do not leak the underlying implementation.
- KaiserPro 5y agorepeat after me: Document. Your. Shit. everything else can do one. Just fucking write documentation as if you're the poor bastard trying to maintain this code with no context or time.
- caymanjim 5y agoDocumentation is rarely adequately maintained, and nothing enforces that it stay accurate and maintained. Comments in code can lie (they're not functional); can be misplaced (in most languages, they're not attached to the code they document in any enforced way); are most-frequently used to describe things that wouldn't require documenting if they were just named properly; are often little more than noise. Code comments should be exceedingly-rare, and only used to describe exception situations or logic that can't be made more clear through the use of better identifiers or better-composed functions. External documentation is usually out-of-sight, out-of-mind. Over time, it diverges from reality, to the point that it's usually misleading or wrong. It's not visible in the code (and this isn't an argument in favor of in-code comments). Maintaining it is a burden. There's no agreed-upon standard for how to present or navigate it. The best way to document things is to name identifiers well, write functions that are well-composed and small enough to understand, stick to single-responsibility principles. API documentation is important and valuable, especially when your IDE can provide it readily at the point of use. Whenever possible, it should be part of the source code in a formal way, using annotations or other mechanisms tied to the code it describes. I wish more languages would formally include annotation mechanisms for this specific use case.
- KaiserPro 5y ago> Documentation is rarely adequately maintained, yes, and the solution is to actually document. > wouldn't require documenting if they were just named properly I mean not really. Having decent names for things tells us what you are doing, but not why. > only used to describe exception situations Again, just imagine if that wasn't the case. Imagine people had the empathy to actually do a professional job? > The best way to document things is to name identifiers well, write functions that are well-composed and small enough to understand, stick to single-responsibility principles. No, thats the best way to code. The best way to document is to imagine you are a new developer to the code base, and any information they should know is where they need it. Like your code, you need to test your documentation. I know you don't _like_ documenting, but thats not the point. Its about being professional, just imagine if an engineer didn't _like_ doing certification of their stuff? they'd loose their license. Sure you could work it out later, but thats not the point. You are paid to be professional, not a magician.
- TeMPOraL 5y ago> Put logic closest to where it needs to live (feature folders) Can you say more about this? I think I may have stumbled on a similar insight myself. In a side project (a roguelike game), I've been experimenting with a design that treats features as first-class, composable design units. Here is a list of the subfolder called game-features in the source tree: actions collision control death destructibility game-feature.lisp hearing kinematics log lore package.lisp rendering sight simulation transform An extract from the docstring of the entire game-feature package: "A Game Feature is responsible for providing components, events, event handlers, queries and other utilities implementing a given aspect of the game. It's primarily a organization tool for gameplay code. Each individual Game Feature is represented by a class inheriting from `SAAT/GF:GAME-FEATURE'. To make use of a Game Feature, an object of such class should be created, preferably in a system description (see `SAAT/DI'). This way, all rules of the game are determined by a collection of Game Features loaded in a given game. Game Features may depend on other Game Features; this is represented through dependencies of their classes." The project is still very much work-in-progress (procrastinating on HN doesn't leave me much time to work on it), and most of the above features are nowhere near completion, but I found the design to be mostly sound. Each game feature provides code that implements its own concerns, and exports various functions and data structures for other game features to use. This is an inversion of traditional design, and is more similar to the ECS pattern, except I bucket all conceptually related things in one place. ECS Components and Systems, utility code, event definitions, etc. that implement a single conceptual game aspect live in the same folder. Inter-feature dependencies are made explicit, and game "superstructure" is designed to allow GFs to wire themselves into appropriate places in the event loop, datastore, etc. - so in game startup code, I just declare which features I want to have enabled. (Each feature also gets its set of integration tests that use synthetic scenarios to verify a particular aspect of the game works as I want it to.) One negative side effect of this design is that the execution order of handlers for any given event is hard to determine from code. That's because, to have game features easily compose, GFs can request particular ordering themselves (e.g. "death" can demand its event handler to be executed after "destructibility" but before "log") - so at startup, I get an ordering preference graph that I reconcile and linearize (via topological sorting). I work around this and related issues by adding debug utilities - e.g. some extra code that can, after game startup, generate a PlantUML/GraphViz picture of all events, event handlers, and their ordering. (I apologize for a long comment, it's a bit of work I always wanted to talk about with someone, but never got around to. The source of the game isn't public right now because I'm afraid of airing my hot garbage code.)
- thebackup 5y agoI read Clean Code in 2010 and trying out and applying some of the principles really helped to make my code more maintainable. Now over 10 years later I have come to realize that you cannot set up too many rules on how to structure and write code. It is like forcing all authors to apply the same writing style or all artists to draw their paintings with the exact same technique. With that analogy in mind, I think that one of the biggest contributors to messy code is having a lot of developers, all with different preferences, working in the same code base. Just imagine having 100 different writers trying to write a book, this is the challenge we are trying to address.
- TheCoelacanth 5y agoI'm not sure that's really true. Any publication with 100 different writers almost certainly has some kind of style guide that they all have to follow.
- bosswipe 5y agoYour name WET is kind of funny but it is usually refered to as the rule of 3, https://en.m.wikipedia.org/wiki/Rule_of_three_(computer_programming) https://en.m.wikipedia.org/wiki/Rule_of_three_(computer_prog...
- Chris2048 5y ago> figure out the abstraction after you need something a 3rd time That's still too much of a "rule". Whenever I feel (or know) two functions are similar, the factors that determine if I should merge them: - I see significant benefit too doing so, usually the benefit of a utility that saves writing the same thing in the future, or debugging the same/similar code repeatedly. - How likely the code is to diverge. Sometimes I just mark things for de-duping, but leave it around a while to see if one of the functions change. - The function is big enough it cannot just be in-lined where it is called, and the benefit of de-duplication is not outweighed by added complexity to the call stack.
- bengale 5y agoI’ve never heard the term WET before but that’s exactly what I do. The other key thing I think is not to over-engineer abstractions you don’t need yet. But to try and leave ‘seams’ where it’s obvious how to tease code about if you need to start building abstractions.
- YeBanKo 5y ago> WET One of the most difficult to argue comments in code reviews: “let’s make it generic in case we need it some place else”. First of all, chances that we need it some place else aren’t exactly high, unless you are writing a library and code explicitly design to be shared. And even if such need arises, chances of getting it right generalizing from one example are slim. Regarding the book though, I have participated in one of the workshops with the author and he seemed to be in favor of WER and against “architecting” levels of abstraction before having concrete examples.
- heisenbit 5y agoMy experience interviewing recently a number of consultants with only a few years experience was the more they mumbled clean code the less they knew what they were doing.