I have just finished reading Michael Feathers’ Working Effectively with Legacy Code. The book gives a lot of very concrete examples of how to improve code and make it testable. To my tastes he is exaggerating the whole code without test is legacy code thing, which in the naïve reader might lead to the impression that a test is all it needs for code to be good.
As I am going to sell my copy at favourable rate to one of my colleagues, I’ll just list a few things I found intersting:
- Chapter 15 – My application is all API calls. He uses a very good example of a mailing list server to illustrate how to get around problems with external API dependencies. He distinguishes to approaches “Skin and wrap the API” and “Responsibility-based extraction”. I am strongly favouring the second approach as I find it leads to more relevant abstractions and not wrapping for the sake of wrapping…
- Another theme in the book is understanding code. He mentions a few nice techniques:
- Effect analysis (dataflow analysis) using pen and paper
- Mapping out your way through the code base while reading on a piece of paper
- Printing the code and doing listing markup using marker pens.
- Scratch refactorings while reading code, that are thrown away and whose sole purpose is helping to understand the codebase and on a similar note delete unused code you find while reading
- Telling the story of the system. This is done at several levels starting with a high level description that has a lot of generalisations. This practice helps people to develop a shared vision and also highlights discrepancies between the system and the story.
- Naked CRC-Cards – somewhat poorly named as actually it’s naked object cards. Use white index cards on a table to represent objects. Tell story as you place and rearrange them.
There is a nice example for separation of concerns (he uses the term SRP, which I don’t like) on page 247 describing an expression evaluator.
Adapt parameter refactoring. If the parameter type is too complicated for stubbing etc, look at the use of the parameter and introduce a wrapper around the original object that just exposes what the current method needs. Example HttpServletRequest gets wrapped in ServlatParameterSource which implements a new ParameterSource interface.
On a similar note primitivise parameter. Instead of passing in our wrapping an expensive abstraction just pass in the required “values”. This of course very dangerous as it breaks encapsulation. On the other hand it could lead to the extraction of roll based primitive interfaces (a bit like the adapt parameter pattern)