Code bases that are not cared for tend to rot. That does not mean they are converted into valueable soil. When a line of code is written it captures the information, knowledge and skill you had at this moment. As you continually learn and improve and get to know new bits of information many of those code lines become more and more inappropriate with the passage of time. Although your initial solution solved the problem you discover better ways for doing so.
It is plainly wrong to deny the code the chance to evolve with your growing abilities.
For the initial problem solving solution TDD is one very useful approach. It gives your code an environment (tests) to be executed in and validates that it actulally solved the problem (passing the tests). The design is driven implicitely to a testable state that has a higher design quality already. But you shouldn't stop there. The TDD mantra is red-green-REFACTOR.
So after solving your problem you must look at your code base from a wider perspective. How does it fit with your systems architecture, the programming model principles (e.g., object oriented, functional, etc.), the language idioms? How well is it integrated with existing code.
While reading, maintaining and writing code it is quite easy to spot pathologies also known as "code smells" (releated by Kent Beck to bad baby smells)
Do you notice things like:
- duplication (near and far),
- inconsistent or uninformative names,
- long pieces of code
- unintelligible boolean expressions
- long sequences of conditionals
- working in the intestines of other units (objects, modules)
- exposing your internal state?
Your IDE may help you with this. The code intentions they provide offer a good starting point as they are tailored to the language and sometimes offer quick fixes (dubbed refactoring by example by Kent Beck).
If so you have an opportunity to take action. Try deodorizing the smelly code. Don't rush. Just take small steps. And keep your tests running so that you can easily see if you broke something. In Martin Fowler's Refactoring the steps of the refactorings presented are outlined in great detail, so it's easy to follow. I would suggest doing the steps at least once manually to get a feeling for the preconditions and side effects of each refactoring. Thinking about what you're doing is absolutely necessary when refactoring. A small glitch can become a big deal as it may affect a larger part of the code base than anticipated.
Ask for help if your gut feeling does not guide you in the right direction. Pair with a co-worker for the refactoring session. Two sets of eyes and experiences can have a significant effect — especially if one of these is unclouded by the initial implementation approach.
Another great way to look for the target of your refactoring is design patterns. Although the original work by Martin Fowler et al does not mention design patterns very much, the additional work of Joshua Kerievsky does a good job in discussing design patterns as goals for refactorings.
Fortunately in our time we have the tools available to help us with automatic refactoring. Most IDEs offer an impressive range of refactorings that work on the syntactically sound parse tree of your source code (and so can even refactoring partially defective or unfinished source code). So there is no excuse for not refactoring.
When refactoring you often encounter an epiphany at some point. This happens when suddenly all puzzle pieces fall into the place where they belong and the sum of your code is bigger than its parts. From that point it is quite easy to take a leap in the development of your system or its architecture.
This work is licensed under a Creative Commons Attribution 3
Back to 97 Things Every Programmer Should Know home page