The Evils of Cut and Paste

February 24, 2011

Its a given in programming that bad habits are hard to get rid off. Its also a given that when you try and go fast, and when you feel pressure bad habits re-surface.

Cut and paste programming may be the worst habit of all. Its so enticing, easy to do and so productive! At least that’s how it appears, but really its a cancer, destroying the joy of programming and at the same time destroying your code.

Such strong words, can they be justified? Well lets have a closer look at cut and paste and see what it does

For my evils I am having to work with PHP at the moment. So the following examples will be in PHP. PHP is very conducive to cut and paste, it has a clunky verbose syntax, and is a language I don’t particularly want to learn. Cut and pasting will help with that.

So I want to create a new class in PHP. This class is a Service that consumes a feed containing gossip. So lets find a similar class and copy it. As I’m doing TDD I’ll copy the test first

<?php
include_once 'IdealServiceTest.php';
include_once 'FootballStoriesService.php';

class FootballStoriesServiceTest extends IdealServiceTest
{
    # copy another 40 lines of code
    ...

So what have I achieved by doing this. Well I’ve

  1. Missed the opportunity to learn that php files start with <?php and don’t need to close that tag. If instead I typed <?php each time I would quickly learn this and never forget it.

  2. Included two files that I may well not need. This stops me learning the include_once syntax. It also stops me considering what files my service test will need

  3. Avoided properly considering if this is an IdealServiceTest. Not only have I accepted the use of inheritance without any thought, but I’ve also included a number of tests in the parent class which may not be applicable

So even in just the first 5 lines of pasting I’ve missed multiple learning opportunities, and spurned the opportunity to think about my new class.

So what about the rest of the code I pasted and the replacing I need to do?

<?php
public function instantiateService()
{
    $this->service = new FootballStoriesService();
}

public function testGetDataShouldDecodeDataIfServiceIsUp()
{
// no it shouldn't because SimpleXML will do this.
// however we need to overide the method in IdealServiceTest
}

public function testGetDataShouldReturnDataIfServiceIsUp()
{
    $this->mockGoodResponseWithData();
    $this->assertNotNull($this->service->getData());
}

protected function defaultHeaders()
{
    return array('Content-Type' => 'text/xml');
}

protected function defaultBody()
{
    return file_get_contents("../modules-test/cruyff/fixtures/football_stories_response.json");
}

So now I can go through this code and replace Football with Gossip, and then run my test and fix the failures. Simples!! But wait a minute what does this entail?

Well for starters I’m going to be running 6 unit tests at once that will all fail. I’m going to be running one test that doesn’t make sense for Gossip (its data should be encoded). I’ve got the wrong header type in defaultHeaders() which is going to break my service. I’m going to create a json fixture for Gossip, when I could just imbed 4 lines of json in the default body method instead. Wow I’m getting confused. But never mind I can just cut and paste the FootballService code change Football to Gossip and everything will work won’t it?

THIS IS MADNESS !!!

After 20 minutes of hacking I might get my unit tests passing, but in that time I haven’t even thought about what I am testing, or why I am testing it. I’ve missed so many learning opportunities, in fact I may actually know less about my code, my tests and my editor than when I started. And worst of all I haven’t even considered what Gossip is!

So I do my 20 minutes of hacking get my tests passing and now I can check my code in. WAIT, what about refactoring, what about reviewing the work? Why waste time doing that? After all the pattern worked for Football it will be fine for Gossip, and what needs refactoring?

Because I cut and pasted I can’t review the work because I don’t understand it. Instead of thinking about what I’m creating I’ve been thinking about cutting and pasting. In fact there is no creation, design or the application of any real intelligence when you cut and paste. All you are doing is blindly following a pattern and sorting out where you haven’t followed it accurately. Still I’ll check it in anyhow. After all if the tests pass its good enough!

Some more fundamentals:

  1. You know less at the beginning of a project, than you do at the end.
  2. The more times you have followed a patten the harder it is to refactor.

Cut and pasting is all about following established patterns of existing code. But the points above show that the patterns you are following were established in relative ignorance, and that each time you repeat the pattern you are making improvement more difficult.

If you cut and paste stupid code you are repeating your stupidity without any chance of being able to become less stupid. If instead you actually write the code again you will quickly become aware that what you are doing is stupid.

Oh no … inheritance is a form of cut and paste!! Does this mean I should never use inheritance? Well not quite, but it certainly means that every use of inheritance should be treated with great care. The following should be asked every time.

  1. Can I compose instead
  2. Is foo really a bar
  3. What am I specialising here

A Week Without Cut and Paste

Here is something to try, have a week that is free of any sort of cut and paste. Abandon ctrl C and ctrl V (cmd C and cmd V for the lucky ones :)) and type everything for a week. I suspect that you will:

  1. Learn lots
  2. Improve your typing
  3. Think more
  4. Improve the quality of all the things you do
  5. Be less bored