
Write code that is easy to delete, not easy to extend - AndrewDucker
http://programmingisterrible.com/post/139222674273/write-code-that-is-easy-to-delete-not-easy-to
======
lostcolony
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
Couldn'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)

------
netghost
The 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-
abstractio...](http://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction)

~~~
manyxcxi
Yes 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.

~~~
codazoda
In the real world, at least mine, once it works you get pressure to move on.
Both my own mind saying, it works so it's good enough, and external pressure
from a stake holder who says it looks good lets move on. I don't feel like I
get to that latter 20% - 30%. As such, I try to be thoughtful on my design,
but I also realize the need to get stuff done. It's a balancing act.

~~~
BurningFrog
The pressure you put on yourself is your responsibility. Don't pretend it's
something you have no power over.

The stake holder should be your customer, not your boss. Even if that dynamic
is broken, you don't need to tell them it's shippable until you're happy with
the code.

~~~
chairleader
True, we are the ones responsible for our integrity and we often live with our
mistakes, paying for poor design choices during later maintenance cycles.

Just be aware that it can be easy to get lost in a yak-shaving exercise doing
your cleanup refactoring. I recommend timeboxing this phase to no more than an
hour, or prioritizing one or two specific tasks so that you don't go off the
deep end.

Don't forget that others are depending on you to get their work done. There
may be a 2-hour cleanup task somewhere else in the code that will help your
teammates far more than the itch you want to scratch here and now.

~~~
sheepmullet
> I recommend timeboxing this phase to no more than an hour, or prioritizing
> one or two specific tasks so that you don't go off the deep end.

When I read comments like this I think to myself "How can they possibly be so
productive that refactoring can be done inside of an hour?".

Then I remember that some developers split up projects/problems into really
small components and can complete a feature or two in a single day. Well then
an hour is actually a fair bit of time to spend refactoring.

------
chairleader
This 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
> 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
I'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
Further 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.

------
V-2
Makes 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

------
ianamartin
I 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
And 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)

------
jonahx
To 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
That'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
It'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.

------
d0m
Totally 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
To 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.

------
userbinator
This 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.

------
makecheck
The 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.

------
kazinator
Easy 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_ ". :)

------
swalsh
From 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
The 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.

------
xamuel
Corollary: 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!

~~~
Too
That's why you should use an IDE with proper "find all references" instead of
coding in notepad.

~~~
falsedan
Bit 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).

------
DanielBMarkham
Interesting. 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
Hopefully, 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
Do 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.

------
MaybiusStrip
Some 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.

------
progx
Step 8: Listen what other programmers say

Step 9: Don`t listen to other programmers

------
andy_ppp
This 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!

------
dclowd9901
This 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.

------
kapitalx
Rule 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_progra...](https://en.wikipedia.org/wiki/Rule_of_three_\(computer_programming\))

------
yahyaheee
I 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

------
falsedan
Another 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).

------
z3t4
I 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)

------
akkartik
Over the past year I can't stop thinking about this article:
[http://250bpm.com/blog:51](http://250bpm.com/blog:51)

------
vmorgulis
A 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.

------
jcyw
I 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. :)

------
jrochkind1
This is awesome. Easier said then done, but, yes. Coding is a craft, and
always will be.

------
codazoda
I 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.

------
auvrw
really 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

------
danbruc
_Jean-Paul Sartre’s Programming in ANSI C_

I am pretty sure that is at least a misattribution.

~~~
frostmatthew
I 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
It'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

------
cake42
“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.

~~~
ryandrake
I thought it was pretty funny. Lighten things up from the start.

~~~
kweinber
Agreed, I usually warm up the engineering crowd with a few quotes from
Shopenhauer or Malthus, but Sartre is always good for a few yucks.

