6

I am often in a situation where I have to design and code a process/algorithm, and there are different variants on that algorithm. For example, I am currently writing some database sychronisation code that has methods for download and upload. Those methods are similar, but not identical.

Having written the code, and got it to work, I am now refactoring it to remove the repetition.

I am not so bad at writing classes which mainly hold data, in those cases I find it easier at the beginning to apply entity-relationship modelling, break the data down into classes, and I can fairly quickly see common elements that can be put into base/abstract classes.

But I find it harder with algorithms/processes.

My questions are: is it better to take more time to design it up-front, thus avoiding the need for refactoring? And, if so, how do you get good at doing this?

I find that when I design up-front, because there is no way to test the design, I often go way off tangent and as soon as I start writing the code the design gets discarded. In contrast, with incremental development, you can regularly test your code to ensure it works. But the disadvantage of the way I do incremental development, is the extra time spent refactoring at the end so that I write "DRY" code.

2
  • This is not a duplicate! The other question says "Is it a good rule of thumb to always write code for the intent of re-using it somewhere down the road?". In my example, you know in advance that the code has more than one intended purpose, its a different scenario to making code generic that currently only has one use. Aug 4, 2015 at 7:49
  • 2
    Don't sweat it. I agree, but the mods here are super-strict about what they decree to be duplicates and non-relevant questions. Personally I think that's a shame, but it's to do with the history of the site where once anything went and the place got to be an unholy mess.
    – Bob Tway
    Aug 4, 2015 at 8:00

3 Answers 3

10

For some reason, designing up front feels to our brains like it should be faster, but it rarely is. It's much easier to factor out duplication from two working, concrete functions already in your editor than it is to factor out duplication from two ephemeral functions that haven't been written yet.

The trick is to make your refactor cycle tight. If you're doing a refactor every few minutes, that's basically indistinguishable from being really good at up front design. On really complex algorithms, I might go an hour or two without a refactor phase. You're doing it wrong if you ever tell your boss something like, "It works, but I need a couple more days for refactoring."

2
  • 3
    +1 "You're doing it wrong if you ever tell your boss something like, "It works, but I need a couple more days for refactoring.""
    – JSBach
    Aug 3, 2015 at 15:54
  • I think you are right @Karl. It comes down to making yourself write more atomic code where you can test the individual parts Aug 4, 2015 at 7:47
4

You are pretty much pointing at the issue yourself: "...is the extra time spent refactoring at the end so that I write "DRY" code"

The point of writing incremental code and coding under test is to achieve the so-called 'red-green-refactor' cycle:

  • Red: write a failing test that shows a shortcoming in your code
  • Green: write the code to make the new test, along with the other existing tests, pass
  • Refactor: get rid of code smells, duplication, things like that. While doing this, keep the tests running and make sure they pass

It feels to me like you are doing the entire coding and, only when you are finished, you get to shoring up the code. Don't do that. Write a well-defined piece of your code, get it under test and then start refactoring it. The tests you just wrote will help you and prevent you from breaking the code while refactoring.

2
  • What I do is 1. Write the code to do process A. 2. Copy and paste the code, and change it to do process B. 3. Refactor to remove the duplication. The code is tested at regular intervals during (1). Then it is tested at the end of doing (2) and the end of (3). Aug 3, 2015 at 14:28
  • @PaulRichards - But you stated there was no way to test the design when done all up-front.
    – JeffO
    Aug 3, 2015 at 14:33
2

I find that when I design up-front, because there is no way to test the design, I often go way off tangent and as soon as I start writing the code the design gets discarded.

The difference between and up-front (waterfall) type of design compared to incremental, is the amount of requirements you approach. To help you solve your current problems, you need to be able take a design (start small) and build it without going off tangent. Once you realize the design has flaws, stop coding, and redesign.

The goal is not to be able to come up with a perfect design up-front nor to be able to blindly code a flawed design, but to come to some sort of happy medium. The extreme to your current practice would be to create an over-all design, but then just cowboy code the project. When the design isn't going to work, stop coding and fix the design.

Some of the refactoring and/or code sharing may get recognized when creating that second class or even later. As you improve, you'll not only get better at up-front designing, but also better at recognizing how much up-front design is possible or even practical. When you work with a team and they assume everyone agrees on the current design, there is going to be trouble if one programmer goes off on a tangent. Don't let it be you. Stop the team if necessary and redesign.

Not the answer you're looking for? Browse other questions tagged or ask your own question.