12 ms·
Write code that is easy to delete, not easy to extend
- jcyw 11y agoI stil think it would be better to guide programming using principles, not practices. To avoid fragile code, remember the open closed principle. To avoid wrong abstraction, think single responsibility and substitution principle. The author is right about interface segregation. :)
- DanielBMarkham 11y agoInteresting. Thanks. This looks like it's from a very functional (FP) view of the world. I like a lot that's here, but I fear it will make my OOP friends' heads explode. Also, as a nit, the essay feels a bit on the "thrashing" side. I know when I'm trying to express complex concepts many times I will hit the same topic a few different times from multiple angles until I get something that's tight. This essay feels like on of those attempts -- nothing here to throw rocks at; it just doesn't feel "done" yet.
- braythwayt 11y agoHopefully, he has--cough--written paragraphs that are easy to delete, not easy to modify. Ha. Ha. Ha. But seriously, this general idea strikes me as being applicable to writing words as well. Write in such a decoupled way that it is relatively easy to excise paragraphs and sections without destroying the rest of the piece. That may introduce a small amount of redundancy, but it also makes it easy to read in smaller chunks, reorder, or remove outright rather than edit portions that no longer fit.
- tariqali34 11y agoDo remember to actually delete paragraphs though. I've seen long-form articles that were really just several short-form articles that are linked together by a listicle format. If I'm lucky, there may even be a few transition phrases or a narrative that would justify the listicle format. All these articles may have been good if the writer wasn't so wedded to the long-form format and started deleting paragraphs. But since he was wedded to the format, all the reader sees is a rather meandering and dull piece. It's an example of the sum of pieces being greater than the whole.
- mabbo 11y agoI think there's a general tend towards things being more functional these days, where possible, even in OOP languages. Since lambdas were added to Java, I know my code style has dramatically changed.
- pmarreck 11y agoIf you're not doing even OO in a functional style, wherever possible, you may be doing it wrong... but don't take my word for it http://gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php http://gamasutra.com/view/news/169296/Indepth_Functional_pro...
- marcosdumay 11y agoIt's not even that OOP developers won't get it. The problem is that this recommendation is harmful on code organized on the Java/.Net/C++ OOP style.
- jrochkind1 11y agoI dunno, with years of experience in OO (ruby for the past 7 or so, Java before that), this still definitely hits home. I don't agree with the commenter that suggests these suggestions are harmful on code written in OO style -- although they specifically say code written in "Java/.Net/C++" style; maybe? But not ruby/smalltalk/ObjC style.
- jonahx 11y agoTo add to his point about copy and pasting... Notice the contradiction implicit in these bits of accepted dogma: 1. Copy and pasting code is evil (DRY). 2. Tight coupling is bad, loose coupling is good. A system written with no abstracted functions (ie, where copy/pasting was the rule) has no coupling. Of course, I'm not advocating such a style. But it's worth keeping in mind that practicing DRY through abstraction necessarily increases coupling, which means that it's always a tradeoff and that you have to get your abstraction right so that the coupling, on average, makes change easier rather than harder.
- arjie 11y agoThat's not really true, is it? Coupling is independent from explicit code dependencies. Take, for instance, two programs. One writes to "/tmp/file". The other reads from "/tmp/file" but neither shares the code to determine the path ("/tmp/file", in this case). They're code independent, but very tightly coupled. In fact, I'd hypothesise that copy-paste codebases are more tightly coupled than otherwise.
- jonahx 11y agoIt's certainly true. Coupling measures the degree of interdependence between modules/functions/classes. If everything is copy/pasted, no module depends on any other -- it simply has its own copy of the code. This will result in a terrible system, but it will be very loosely coupled. When you change something, you won't have to worry about it's impact on anything else. In your example, if the content of the file is changing the behavior of the program reading it, then the programs are sharing code via the file. It just happens to be going on at runtime rather than at compile time.
- stepanhruda 11y agoIf you need to delete code in a codebase full of copy pastes, it's likely that you will need to delete it in many many places and miss some of them. So although the code isn't coupled, it is hard to delete.
- 11y ago
- danbruc 11y agoJean-Paul Sartre’s Programming in ANSI C I am pretty sure that is at least a misattribution.
- frostmatthew 11y agoI believe all (or at least most) of the "quotes" have been re-purposed/altered by the [blog's] author. e.g. "Every line of code is written without reason, maintained out of weakness, and deleted by chance" is based on Jean-Paul Sartre's actual quote[1] "Every existing thing is born without reason, prolongs itself out of weakness and dies by chance." [1] https://en.wikiquote.org/wiki/Jean-Paul_Sartre https://en.wikiquote.org/wiki/Jean-Paul_Sartre
- RamshackleJ 11y agoIt's kinda sad that no one else seams to be picking up on this. To lead off with that intentionally altered quote and looking at code in terms of how existentialists frame reality is the most significant insight. edit: Radical Freedom in my programs intensifies
- bshimmin 11y agoWasn't it Sartre who famously wrote Three o'clock is both too early and too late to start refactoring?
- curlew 11y agoHell is other people's code.
- cake42 11y ago“Every line of code is written without reason, maintained out of weakness, and deleted by chance” Jean-Paul Sartre’s Programming in ANSI C. I just started the article and I already have problems with it, not a good sign. While it may be obvious when you research the timelines of JPS (he died a few years before ansi c 89 was established) and C , not to mention the miles of metaphorical distance between Computer Programming and JPS's work) . I guess the author was trying to be cute?? but that fabrication should be made clear as such. he's undermining his own inherent credibility as an author, however much the reader decides to put in. Serious problem in my book.
- Cpoll 11y agoI'm sure you're joking, but for those who are curious, Sartre's actual quote is "Every existing thing is born without reason, prolongs itself out of weakness, and dies by chance."
- ryandrake 11y agoI thought it was pretty funny. Lighten things up from the start.
- kweinber 11y agoAgreed, I usually warm up the engineering crowd with a few quotes from Shopenhauer or Malthus, but Sartre is always good for a few yucks.
- michaelwww 11y agoI hope you are being serious cake42, because if you're not you're only mildly funny, but if you are, you're hilarious.
- deleted 11y ago[deleted]
- netghost 11y agoThe trick really boils down to: be messy, but clean up. If you don't do the second half of each thing (copy and paste / don't copy and paste), then you end up with that unmaintainable mess. If you do the second part too early, you end up with the wrong abstraction. Sandi Metz also talks about this: http://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction http://www.sandimetz.com/blog/2016/1/20/the-wrong-abstractio...
- dhimes 11y agoYes. It is an iterative process, like writing a story.
- manyxcxi 11y agoYes yes yes. When I'm coaching junior developers (or developers that like over engineering) this is what I try hammering home more than anything. If you looked at my code at 10-70% solution complete you'd find so many egregious DRY errors, magic values (likely with a comment above for my own memory), functions that are too long, and a whol ball of mess. If you looked at it 90-100% complete it would be nearly unrecognizable in writing style to what you saw earlier. Figure out how to solve the problem first, spend all your efforts on that, then figure out how to clean up your code and make things nice/performant/secure/etc. I've found that as I've gotten better my sloppy is better than a lot of people's clean, which means I'm spending less time on cleanliness, and getting done faster.
- JoeAltmaier 11y agoWhen I teach teenagers I tell them, there's never time to do it right OR to do it again. Time is money; Engineers are paid to solve the problem and not make a beautiful thing. Get used to just-in-time solutions and just-good-enough implementations. With that in mind, its important to have some discipline, to make your 'just good enough' not too crappy. That's the difference between an apprentice and an expert.
- Flip-per 11y agoI do not like this approach, and I think it is not the right way for medium to large software projects. Maintaining lots of interacting 'just-good-enough' code is a nightmare - and it can easily cost you way more than you saved writing it. People get lazy and sloppy early enough. I would try to encourage teenagers to go for the best and most beautiful solution that they can possibly find.
- chairleader 11y agoThis is extremely validating to read. How many times have I battled with DRYists over which solution is "better." I've happened upon the pattern of code growth described here after years of encountering and resolving pain points in code, often as the maintainer. DRYness for DRYness sake might feel satisfying when writing the code, but when changes need to be made, it often scatters constraints and requirements throughout the codebase, making any change an unestimatable mess of sneaky traps. Try writing your boilerplate initialization code straight some time... no metaprogramming, no helper functions, just get your configuration, create your objects and wire them together. It is oddly liberating to have your bootstrap code flat and unmagical.
- mathgeek 11y ago> DRYness for DRYness sake might feel satisfying when writing the code, but when changes need to be made, it often scatters constraints and requirements throughout the codebase, making any change an unestimatable mess of sneaky traps. This also applies to "one-liners" when writing code. For example in ruby: Model.where(...).foo { ... }.flatten.bar { ... }.join(...) Looks nice and all, but it can be confusing when you need to fix a bug that you aren't sure of the source (foo or bar? maybe we shouldn't flatten yet? maybe we should join sooner?). It's much better, IMHO, to write more lines of code here so that you can easily test each step manually when a bug occurs.
- GavinMcG 11y agoI'm not addressing your overall point, but one thing I learned about recently that could help in debugging those Ruby one-liners is the `tap` method. You can stick it in the chain and set a breakpoint. http://ruby-doc.org/core-2.2.3/Object.html#method-i-tap http://ruby-doc.org/core-2.2.3/Object.html#method-i-tap
- tvon 11y agoFurther off-topic, but I almost exclusively see tap used as some kind of clever way around a return line, e.g. instead of: def build_hash_or_whatever hash = Hash.new hash[:foo] = some_generating_method hash[:bar] = "some value" if whatever? # More hash construction nonsense hash end With tap it is: def build_hash_or_whatever Hash.new.tap do |hash| hash[:foo] = some_generating_method hash[:bar] = "some value" if whatever? # More hash construction nonsense end end I think the tap-form is significantly worse and I don't understand the aversion to a single line that clearly shows what you are returning.
- xamuel 11y agoCorollary: Write code such that it's easy to use grep to find where things are used. One of my pet peeves is when OO programmers take class's separate namespaces as a license to use super-generic method names. Method names like "add", "update", etc. Makes it near impossible to figure out where those methods are used!
- pm215 11y agoC preprocessor macros can also be a great tool for making things ungreppable -- building families of functions using preprocessor token pasting to put together the function names certainly avoids repetition in all those function bodies but it also makes it really hard to find the definition later...
- Too 11y agoThat's why you should use an IDE with proper "find all references" instead of coding in notepad.
- falsedan 11y agoBit trollish, but: IDEs which let you ignore the complexity of the code let you write complicated code. Writing in less-fully-featured environments forces you to consider simplicity as a desirable feature (so you can, y'know, work out what's going on when using a 80x24 terminal).
- jrochkind1 11y agoThis is awesome. Easier said then done, but, yes. Coding is a craft, and always will be.
- lostcolony 11y agoI 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.
- progx 11y agoStep 8: Listen what other programmers say Step 9: Don`t listen to other programmers
- picrow 11y agoPretty much this. This is the saddest industry on earth.
- userbinator 11y agoThis article could be summarised as "abstract only when you see a clear need to", something that seems to be the exact opposite of what a lot of programming courses teach; especially those dealing with object-oriented design. I think abstraction should be viewed not as a technique to be applied generously and whenever possible, but a necessary evil, resorted to only when nothing else can simplify the code. A bonus of this style is that it often also makes the resulting code more efficient for the machine to execute, reducing the need for optimisations later.
- V-2 11y agoMakes sense - you can't know the right abstraction upfront. As Hejlsberg said: "If you ask beginning programmers to write a calendar control, they often think to themselves, "Oh, I'm going to write the world's best calendar control! It's going to be polymorphic with respect to the kind of calendar. It will have displayers, and mungers, and this, that, and the other." They need to ship a calendar application in two months. They put all this infrastructure into place in the control, and then spend two days writing a crappy calendar application on top of it. They'll think, "In the next version of the application, I'm going to do so much more." Once they start thinking about how they're actually going to implement all of these other concretizations of their abstract design, however, it turns out that their design is completely wrong. And now they've painted themself into a corner, and they have to throw the whole thing out. I have seen that over and over. I'm a strong believer in being minimalistic. Unless you actually are going to solve the general problem, don't try and put in place a framework for solving a specific one, because you don't know what that framework should look like." http://www.artima.com/intv/handcuffsP.html http://www.artima.com/intv/handcuffsP.html So of course write it dirty, clean it up afterwards seems like a better idea, because it lets you feel what the right abstraction is. However it takes discipline to always refactor something that works fine already
- vmorgulis 11y agoA neat response to the same problem posted yesterday: http://akkartik.name/post/wart-layers http://akkartik.name/post/wart-layers Basically, layers with numbers and preprocessor directives to inject (tangle) code in the proper places without worrying too much about language abstractions.
- deleted 11y ago[deleted]
- swalsh 11y agoFrom his about: "I am not a very good programmer. I forget to write tests, my documentation is sparse, and i’m pretty apologetic about it at any code review. " Guy who doesn't write unit tests suggests work around to dealing with problems in code that has no unit tests...
- tommorris 11y agoThe point of a lot of tef's writing and speaking isn't that he's a bad programmer, but that he is tearing a large hole in people who claim to be great programmers. tef is actually a pretty good programmer; good enough Heroku employ him. It's dry British humour: "I'm a terrible programmer, but that's okay, because programming is terrible, the whole industry is terrible, and pretty much everyone is terrible at programming, especially if they are lecturing you about how to do it because they are probably full of quite a large amount of shit; now let's try and do better". If you are used to chest-thumping evangelical "rah rah, everything is awesome" sermons, the British cynical attitude of "no, this is all shit, and I'm awful too, but maybe, if we make a nice big cup of tea, and thought about it a bit more, we might be able to be a bit less rubbish" sounds grating. The opposite certainly is true.
- kapitalx 11y agoRule Of Three covers steps 2 and 3. This they used to teach in our 1st year computer science program: https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming) https://en.wikipedia.org/wiki/Rule_of_three_(computer_progra...
- codazoda 11y agoI read this and thought it sounded a lot like my reality. Then, I expected to come to these comments and hear arguments against these thoughts. Glad to see a lot of us agree with much of it.
- d0m 11y agoTotally agree. One thing that I still find really hard to maintain/delete is CSS code. More and more I feel like it should be included with components rather than in a plain .scss or .css files. It feels good to be able to delete a component knowing that there isn't css crap left behind..
- cjcenizal 11y agoTo solve this issue I keep component-specific .scss files alongside each component's JS file, in a component-specific folder. A root index.scss file imports all of the components' .scss files. Coupled with BEM, this also helps prevent component style interdependencies.
- akkartik 11y agoOver the past year I can't stop thinking about this article: http://250bpm.com/blog:51 http://250bpm.com/blog:51
- andy_ppp 11y agoThis all goes to how hard code reuse is - is it worth having two very similar functions that do the same thing instead of having one function that's DRY but more complex than both. Maybe you should have a shared function that has the similar bits of both but then when you remove pieces you have this added complexity. Everyone can give you arguments either way... I remember reading this beautiful explanation about an idea for a functional language where you simply install collections of methods that do specific things and each method you install should be as simple as possible and do just one thing etc. - maybe it was somewhere on the Elixir mailing lists but I can't find it!
- yahyaheee 11y agoI love this article. I have been at war with some of the tech teams at my company that force reusablilty. Personally I find it a nucance, and in most cases can write a new function in less time. You have articulated this ideology very well, thanks for the insight
- dclowd9901 11y agoThis is a super frustrating article, so I'm going to post the quote I think that sums up the art: "To write code that’s easy to delete: repeat yourself to avoid creating dependencies, but don’t repeat yourself to manage them." This is such a hard thing to explain to someone, so I'm impressed the writer described it so accurately and succinctly.
- ianamartin 11y agoI feel like this is a little like learning to cook. At the very beginning, I would just create a huge mess as I was going, use a different pan for every item, a different mixing bowl for each thing, and I didn't know how to prep effectively in advance. This left a train wreck in the kitchen after each meal that I was forced to clean up before I could cook again. I learned how to do a little up front prep, and that saved time and mess, and made the whole process smoother. As I learned more, I started trying to clean up as I went and conserve the number of pots and pans, I made a lot of mistakes washing everything after I used it, which slowed the whole process down, and I didn't need to reuse some of it. Now I'm in a place where I have enough experience that I know in advance what I can reuse for this meal and whether it needs to really be washed (maybe you just deglaze a pan and wipe it down with a paper towel instead of hauling it over to the sink and scrubbing it), when I'm actually done with an item, what the approximate cooking times are, etc. Now I can cook complex multi-course meals to a good restaurant quality and have just a couple of things left to clean up by the time dinner is ready to serve. It doesn't take a genius to figure these things out, just the acknowledgement that these things are important, and the more people you are working with, the more important they are. Sure, I could have spent the rest of my life cleaning the huge mess after every meal at home, and it wouldn't really matter. But if you are going to be a line cook at a large restaurant, you must get a hold of these concepts.
- infinite8s 11y agoAnd this requires introspection on your process, whether the domain is programming, cooking or really any human endeavor. Unfortunately not many people can do this effectively (and this is related to the 10k hours to mastery dictum - it requires effortfull practice which includes exactly this sort of introspection)
- kazinator 11y agoEasy to replace not simply to delete. Anything that is easy to delete (and just leave it deleted) is superfluous. Don't write superfluous cruft, obviously. Easy to replace is important in all engineering. A product or structure with easily replaceable parts is better than one without easily replaceable parts, all else being equal. We'd never say "design a brake caliper with brake pads that are easy to delete". :)
- makecheck 11y agoThe importance of DRY is proportional to what could go wrong if the repeated code has an issue. For instance, if the code is performing some vital calculation or is part of your security architecture, it had better not be repeated anywhere because somebody will need to patch it later and will need to guarantee that the update has been applied consistently. Sometimes, it's just clearer to rewrite something yourself. For instance, just because you can express just about anything in terms of algorithms in the C++ standard library, you should instead write the simpler stuff by hand. (A great example is this silly idea of “copying to an ostream iterator” just to print out some data; I don’t care if that combination of standard functions happens to produce the desired result, because the code is painful to look at!) It's also helpful to using aliasing (e.g. C++ reference variables) to make similar code look as similar as possible; i.e. rather than have two similar blocks using entirely different variable names throughout, declare references at the top to give them the same names so that the similarities are obvious. This also makes it easier to later pull common parts into functions if desired.
- falsedan 11y agoAnother behaviour which makes these steps hard to follow is: someone wrote code and ships it, and thinks, because it works and is being used, it is great and doesn't need to be changed. These feeling get even more in the way when the code took a while to write! I keep telling my team to write bad code: write something that's correct (but not great) quickly, with an eye to replacing it with something better next sprint. Then, once you have something which works end-to-end, work out which bit is most terrible, and replace it. Repeat & launch when the quality is acceptable (and keep repeating after you launch).
- auvrw 11y agoreally appreciated this b/c I've lately felt bad a/b not "getting it right the first time," and one point here seems to be that that never happens, exactly; that development always involves some amount of evolving a body of code, at one level or another
- z3t4 11y agoI might not have understood the article, but avoiding to write code is dangerous, and will lead to code that after a while will be impossible to understand because there will be hacks upon hacks. Your boss loves this though. And about cleaning up the code after you got it working, ha ,ha, like that would ever happen. It doesn't have to look nice, but make sure you get it right from the start, and that it covers all those twenty edge cases. It will cost more time ... So most software projects fail anyway!? Maybe it's because of your shitty code ? (grin)
- deleted 11y ago[deleted]
- MaybiusStrip 11y agoSome of the good advice in this blog post is taken too far, like the advice about intentionally writing shitty code in order to learn from your mistakes. Coding is like any other skill. You learn from your mistakes only if you're trying your best not to make them. Otherwise there's no differentiating between your real mistakes and your carelessness. > A lot of programming is exploratory, and it’s quicker to get it wrong a few times and iterate than think to get it right first time. It's really hard to get it wrong a few times if you have 10 other developers building on top of your mistakes. Then you're pretty much stuck with the code you thought you were going to get rid of. In accordance with Murphy's law, it seems like the code you push out knowing it sucks always ends up being the foundation for something really important. So yes, build the simplest thing possible. Yes, don't abstract pre-maturely. No, don't write shitty code on purpose. Similarly advice with "copy-paste 10 times". While I agree that you should copy-paste 2-3 times, 10 times is way too many. By the time you've copy pasted something 10 times, it's too expensive to refactor. Or if you do refactor it, 5 of those 10 instances have changed beyond recognition and will be missed. They continue to evolve and now for the rest of the life of your software, you're fixing 6 times the amount of bugs.