Refactoring: When, what and where?

Posted on July 7, 2011

0


The basis of refactoring

Refactoring or Core Refactoring is a process in which code and the organization of code and classes are restructured, to:

  1. Increase clarity – As a project grows, code can become more and more messy, as chosen paths changes and solutions that once were sufficient had to be expanded or changed as a whole.
  2. Remove redundancy – Like checks and double checks on specific values, or repetition of code.
  3. Remove dead code – During the process of change, dead code will remain. In general dead code is code not removed “because it might become useful someday” or “because we do not know what it does exactly”
  4. Reduce complexity – Sometimes a specific solution seems to be the best at that time, but in time grows into a Kafka-eske structure of over-complication.
  5. Assure application stability – Unclear processes, redundant code, dead code and unneeded complexity lead to an increase of potential places where the code can fail.

Not only for Agile

While refactoring is an important part of Extreme Programing and Agile, it is not limited to those approaches. Any process that results on code should involve periods of clean up.

Why and when code becomes messy

There are several reasons. I will try to list a complete list of the most common reasons here:

  1. More than one person is involved – As soon as two or more codes are working on the same code base, different approaches will be introduced to solve a problem, name variables and using design patterns. Each will lead to increasingly messy code.
  2. Requirements change – With each functional change, code has to change. And usually changes on several places. Each change introduces small pieces of messy code, which – if not cleaned up from time to time – leads to messy code.
  3. Understanding of requirements change – As your understanding of requirements grow, it might be that your initial approach is not covering it and needs to be revised or even completely redone. The size and impact of the changes also determines the size of the mess left behind.
  4. Requirements are misunderstood – See: “understaning of requirements change”. The impact of the code changes when requirements change is usually deep. usually existing code is re-used or bent into the new direction, as that seems a more sane solution at that point than simply throwing the old code away.
  5. Code for another purpose is re-used for something else – See: “Requirements are misunderstood”. When starting it seems logical to use “A” for “B” as they follow similar processes and re-use is preferred before starting from scratch. This usually leads to messy code.
  6. Bug fixes and patches are stacked onto bug fixes and patches – As fixes continue, they will overlap other fixes and patches. Introducing more mess
  7. Programmers have no clue what they are doing – Proper and clean coding takes experience. Understanding OOP takes even more time and experience. In most cases, coders do what they can do to solve a problem to their best knowledge. This can lead to a lot of messy code that HAS to be refactored.
  8. Refactoring is postponed indefinitely due to fear or lazyness – The fear can be : “If we refactor now, we might not make the deadline”. Lazyness can be: “I want to finish this and will fix the mess some day later”.

The negative impact of refactoring

Refactoring hase some negative impacts for the short term:

  1. It blocks progress – As long as code is refactored, no new things can be done. Everyone who is not refactoring has to wait until refactoring (on their part) is done.
  2. It breaks the project – During the process of refactoring, the code and the project is broken as things are moved around and exctracted and replaced into other parts of the code.
  3. It takes time – A refactoring can take from 3 hours to 3 days and 3 weeks. During that period, no progress is made, except from the code being cleaned up.
  4. It can impact negatively on the deadline – As it takes time to refactor, a deadline might be missed due to it.

The negative impact of not refactoring

  1. Code becomes increasingly messy – To the point where it is no longer maintainable.
  2. Code becomes increasingly overly complicated – Due to entanglements of dependencies that should not be there. Straight lines become messy and wobbly. Processes that can be handled in three steps in one or two classes are done in five ore more, over three or more objects.
  3. More and more strange bugs and strange behavior are introduced – As code becomes more messy, it becomes more and more unclear who does what and what happens where.
  4. Bugs that were solved start re-appearing over and over – More mess means it is easier to lose stuff and undo stuff that was fixed for specific situations as it is unclear what it does and what it is meant to do.
  5. Changes and change request take more and mote time – Due to the increased complexity and increased entanglement of dependecies in the messy code.

Why you MUST clean up messy code

  1. On the long run messy code jeopardises your project – The more mess, the more entanglements of dependencies, the more steps needed to fulfill a process, the bigger the chance your code will work against you by:
    1. Breaking down on you – Due to bugs and strange side effects of messy code
    2. Halting progess – Due to the increased amount of time it takes to find and implement a solution
    3. Blocking any new development – As the old code has become so big and messy that any big change will break the entire project
  2. Messy code slows down progress – Imagine a house full of boxes you have to move aside every time you want to move from “A” to “B”. That is what coding in messy code is like. The more mess, the more time you will waste just dealing with that mess.
  3. Messy code becomes unmanageable – Any change, any improvement, any new person joining the team will have increased costs, to the point where that cost is larger than the benefits.
  4. Messy code creates (unwanted) dependencies on a specific set of people – If “A”, “B” and “C” are the only ones understanding what is going on inside the code, the project becomes dependent of them. Clean code makes people replaceable as clean code is easier to understand by others. This is mostly crucial when people will be bound to a project for only a limited time, or people leave for another job at another company.

When to refactor

There is no real rule. I use the following four indicators:
  1. When the direction of my solution changes – Say I was moving towards solution “A” but halfway through discover that “B” is actually better or what the requirements ask from me.
  2. When stuff is happening in the wrong places – Say we have a handler for incoming calls. And we have a book of rules stating which calls to redirect to what person. Originally it seems logical to have all this in one place. The part that deals with the incoming calls also deals with the rules. But at a certain point, other parts also need access and start using the same Class that handles incoming calls. “IncomingCalls” suddenly has become “IncomingCallsAndRulesToDistributeIncomingCalls”. Clearly time to separate the responsibilities.
  3. When code becomes messy/if I no longer understand what is going on – Usually due to rows and rows of “if/then/else” based on specific rules, situations and exceptions. In most cases, the responsibility of this decision making is delegated to other places and specific classes are created to deal with this. I started applying a principle I called separation of responsibility to deal with this and defined five base type of classes to deal with specific tasks. Read more about that here on the Daymo Approach to MVC. Scroll down to find the section on: “Separation of responsibilities”.
  4. When debugging a relatively simple issue or implementing a change takes too much time – It should be like tooth brushing: you pick up the brush, put some tooth paste on it and start brushing away the errors. Unless you first have to do a lot of other things first, every time you even want to find that brush and your tooth paste. Things shoulod be clear, easy to find and easy to change. Even – and certainly! – when the project becomes increasingly complex.

What I gain from refactoring

  1. Clean, clear code – The difference of my code before and after refactoring is sometimes like night and day. Where the old code can be like heaps of crumpled paper – including junk mail from my post box – where each part might or might not be related and relevant, the new code strives to more like straight sheets of paper organized in logical stacks on a clean surface: where all waste is disposed of.
  2. Agility and increased speed of working – In the sense of: If I want to change direction now, I can do so. All parts are neatly compartmentalized. If I change parts, it will not break my project. If I want to move stuff around, in radical ways, I can do so in a matter of minutes or hours. My code does not block me. Clutter in my code does not block me.
  3. Less code and less bugs in the code – Messy code also introduces a lot of redundant code. Less code means that things go wrong on fewer places.
  4. Increased stability – Messy code can lead to messy and unpredictable results. Clean code is predictable. The cleaner the code, the more predictable the results. Even when major changes are made, as everything has a clear place and a clear purpose and when disconnected and re-connected will not crash due to entanglement of dependencies that should not be there.
  5. Easier to understand by other people – As I work on some projects that have (Reed Elsevier in 2010 and 2011) and will involve (HotForestGreen, the Flash RAD framework) other people, clean code is almost a MUST.

Limiting factors

  1. Deadlines – Deadlines are the biggest factor limiting your refactoring efforts. If you have to deliver tomorrow, you will not do a last minute thorough refactoring. You will do what is possible without endangering the project.
  2. Coding environment – Tools with limited support allow only limited type of refactorings. Tools with a very rich support allow you to unleash the Refactoring Beast. Eclipse (and the coding tools built with and on Eclipse) is quite good. Refactoring, Java and Eclipse seem to be invented for each other. Moving from AS3 projects in FDT (which is quite good), Flex3 (which is horrid), Flex4 (which improved a lot) and Visual Studio 2010 (which did not impress me much) the tools make a huge difference in how far, how deep, how drastic and how thorough you can be in moving stuff around. Another view on refactoring I use – thanks to FDT and Eclipse – is: “breaking the code in a smart way, moving stuff around and fix all broken parts again until it works again”. Good tools help you by telling you immediately what is broken, the moment you break it and by offering full support when you move and rename parts. On the other side of this spectrum is TextPad, with no understanding at all and where you – as a person – have to go through all your code manually. Until it stops giving you compiler errors.

Simplicity – And frameworks that do not really solve a problem

I have ranted about frameworks that introduce and promote specific approaches like Injection of Dependency (Swiz, RobotLegs, Mate for ActionScript, Spring for Java).

As they do help you to work faster within a specific context, they – in contrary what they claim in most cases – are not a solution for maintainable code.

Maintainable code is made by people. As people also produce a complete unmanageable mess.

Boilerplate code

Some approaches lead to extra code and the addition of extra classes, because it is part of the approach, or because they are required to make it work with that framework.

This code is called boilerplate code.

If your project explodes into an increase of a lot of additional classes: “because you need them within that framework”, the question is if you are doing the right thing and using the right approach.

Maintainable code is mainly:

  1. Clear
  2. Short
  3. Simple
  4. Without redundancy

Abusing design patterns and “best practices”

Design patterns are what evolves from your initial coding. And usually your code project is a mix. See again this post: where I explore the different approaches of MVC from “The Daymo point of view”.

There is not one approach to solve all problems and sometimes, by trying to force everything into specific design patterns you achieve the exact opposite of what you want to achieve: code that is maintainable by yourself and others.

Some examples:

  1. Throwing exceptions is not a solution for every unhandled situation – If something happens in your application that is not supposed to happen, the main question is not: what exception shall we throw? But: “Do we really need to put our responsibility to solve this somewhere else?” Sometimes an exception can be solved locally by coding in alternative strategies or returning empty data sets or a status object as part of a “soft failure”: telling the caller something went wrong and he or she needs to do a retry later on.
  2. MVC is not the answer for everything – MVC might seem cool once you get the hang of it, but it is a very limited model for a very limited use: to separate presentation logic (your View) from the logic that deals with your data and business logic (Model) and the part that deals with user input (your Controller).
  3. Events are not your binding glue for all things that happen within your applications – Events have a limited use and should be carefully used. Events are mainly shout-casts accross your application. Someone shouts: “Something has happened” and others can respond to that. But when all your processes depend on events, unexpected things can start happening, as they can be fired from anywhere and do not have centralized business logic dealing with specific exceptions and situations. Another issue is: “Who is referencing what via what Event?” Since Events do not have strong binding or references to your Classes, it is hard to find out who is referencing what as well.
  4. Sometimes a design patterns simply sucks for what you really need – As said, design patterns usually emerge when coding. What might start as “A” might slowly move towards “B” because “B” is simpler and more suited and effective. Trying to stick to specific patterns: “because it is according to the book” is usually only introducing more complexity in a – vary likely – already overly complex project.

Concluding

Above and beyond any design pattern or development method: “from the book” is refactoring.

Good code – in my book – is code that is:

  1. Clear – In structure, naming of classes, methods and variables, purpose and rule sets applied and in separation of responsibilities or: “Who does what”?
  2. Clean – No redundancy, no dead code, no over complexity, no mess.
  3. Well structured – Regardless and apart from any Design Pattern that might be applied.

Any project with people who think code is “good”: “because specific principles are used” but which is an internal mess due to everything it is not according to my list of three (clear, clean, well structured) will automatically raise a lot of resistance in me. And my first intention is to walk away from such projects.

I did not in the past five years as I needed the money.

I have seen a lot of bullshit code due to that under the pretense of: “proper coding” or: “use of standards”. And I have cleaned up a lot of mess.

It has made me very wary of any “framework” including some stuff in Flex that does not solve a clear problem like: “Reading QR codes” or “Converting bitmaps to JPG” or “Connecting to a Server via binary sockets over port 80”. I really have to practice a lot of patience when my questions and input on: “proper coding principles” are continuously understood by the other side as “using this awesome framework that is the new shit”.

It also made me grow as a coder.

In 2008 I knew absolutely nothing about design patterns. MVC, mediators, factories, proxies and the concept of observer patterns would completely confuse me. Partly because the examples made no sense, partly due to my own ignorance and in some cases because the people showing me those examples had no fucking clue what they were talking about.

The move to AS3 and my work on cleaning up other peoples mess gave me the head start to grow into this. And I am thankful for that.

As said: claer, clean, well structured code is king above all else.

Refactoring is the only tool to get there: by rewriting and cleaning up what does not work.

Advertisements
Posted in: Uncategorized