Book Club: Working Effectively With Legacy Code - Chapters 3,4 & 5 (Michael Feathers)
In our latest technical book club we discussed chapters 3,4 and 5 of Michael Feathers' 'Working Effectively With Legacy Code' - 'Sensing and Separation', 'The Seam Model' and 'Tools'.
These are some of my thoughts from our discussion of these chapters:
-
Feathers suggests two reasons why we break dependencies when trying to get tests in place - sensing and separation. The former involves the breaking of dependencies in order to get access to the values computed in our code and the latter is necessary so that we can get our code into a test harness to start with. In my time coding professionally I have experienced difficulties with the former than the latter. I have certainly written code which is bordering impossible to test but it seems like maybe writing code which is difficult to sense against is less problematic than code which we struggle to get into a test harness.
-
I really like the idea of seams and enabling points to describe how we can alter the way that our code works by making changes in other places. Most of the enabling points in the code I’ve worked on are object seams but Halvard and I did make use of a link seam when we wanted to override the behaviour of a specific class in a 3rd party library. We were able to do this by including our own class with the same class signature earlier in the application’s class path. Ahrum described a time when he had to do something similar to get rid of some noisy warning messages which one of the dependencies was emitting. He was able to verify that nothing important was going on in that particular class before overriding the behaviour with the same trick.
-
Dave pointed out that it’s useful to remember that a test is also a client of an object in particular reference to Feathers pointing out that software doesn’t seem to be designed to be easily testable. From what I’ve seen the best way to make code easily testable is to design in that testability when we’re writing it. It’s much more difficult and time consuming to try and do that later on.
-
We had some discussion around big tests in terms of the size of test fixtures and individual tests. The consensus seemed to be that when we have an important class where we care a lot about the edge cases then we’ll probably write a large number of tests. On the other hand if our individual test methods are big - which usually means there’s lots of setup required - then it might indicate that a method or class is doing too much.
-
The problems in code that these two chapters come generally happen because we are doing too much in single classes instead of separating the responsibilities. Ahrum pointed out that if we had lots of small classes we would have to solve the problem about how to organise these classes effectively instead. On the projects I’ve worked on we tend to end up with packages which contain a huge number of classes but I haven’t noticed it as being particularly painful so far. Ahrum suggested an alternative approach to grouping objects by the layer they reside in would be to group them by feature instead. I haven’t done this before but it’d be interesting to see if it would make it easier to manage our code.
About the author
I'm currently working on short form content at ClickHouse. I publish short 5 minute videos showing how to solve data problems on YouTube @LearnDataWithMark. I previously worked on graph analytics at Neo4j, where I also co-authored the O'Reilly Graph Algorithms Book with Amy Hodler.