Working Effectively with Legacy Code

Michael C. Feathers

July 21, 2018

In fact, if you can’t present a compelling case to your coworkers, you might get beat up in the parking lot or, worse, ignored for the rest of your workdays, so let me help you make that case. The biggest obstacle

October 16, 2022 Chapter 2: Working with Feedback

The Legacy Code Dilemma When we change code, we should have tests in place. To put tests in place, we often have to change code

October 18, 2022 Chapter 2: Working with Feedback

When you break dependencies in legacy code, you often have to suspend your sense of aesthetics a bit. Some dependencies break cleanly; others end up looking less than ideal from a design point of view. They are like the incision points in surgery: There might be a scar left in your code after your work, but everything beneath it can get better. If later you can cover code around the point where you broke the dependencies, you can heal that scar, too

October 18, 2022 Chapter 2: Working with Feedback

  1. Identify change points. 2. Find test points. 3. Break dependencies. 4. Write tests. 5. Make changes and refactor.

October 18, 2022 Chapter 3: Sensing and Separation

  1. Sensing—We break dependencies to sense when we can’t access values our code computes. 2. Separation—We break dependencies to separate when we can’t even get a piece of code into a test harness to run.

October 25, 2022 Chapter 4: The Seam Model

A seam is a place where you can alter behavior in your program without editing in that place.

October 25, 2022 Chapter 4: The Seam Model

In general, object seams are the best choice in object-oriented languages. Preprocessing seams and link seams can be useful at times but they are not as explicit as object seams. In addition, tests that depend upon them can be hard to maintain. I like to reserve preprocessing seams and link seams for cases where dependencies are pervasive and there are no better alternatives.

October 28, 2022 Chapter 6: I Don’t Have Much Time and I Have to Change It

Wrap Method. We create a method with the name of the original method and have it delegate to our old code. We use this when we want to add behavior to existing calls of the original method

October 29, 2022 Chapter 6: I Don’t Have Much Time and I Have to Change It

I usually use Sprout Method when the code I have in the existing method communicates a clear algorithm to the reader. I move toward Wrap Method when I think that the new feature I’m adding is as important as the work that was there before.

October 29, 2022 Chapter 7: It Takes Forever to Make a Change

The Dependency Inversion Principle When your code depends on an interface, that dependency is usually very minor and unobtrusive. Your code doesn’t have to change unless the interface changes, and interfaces typically change far less often than the code behind them. When you have an interface, you can edit classes that implement that interface or add new classes that implement the interface, all without impacting code that uses the interface. For this reason, it is better to depend on interfaces or abstract classes than it is to depend on concrete classes. When you depend on less volatile things, you minimize the chance that particular changes will trigger massive recompilation.

November 02, 2022 Chapter 9: I Can’t Get This Class into a Test Harness

When you are writing tests and an object requires a parameter that is hard to construct, consider just passing null instead. If the parameter is used in the course of your test execution, the code will throw an exception and the test harness will catch the exception. If you need behavior that really requires an object, you can construct it and pass it as a parameter at that point. Pass Null is a very handy technique in some languages. It works well in Java and C# and in just about every language that throws an exception when null references are used at runtime. This implies that it really isn’t a great idea to do this in C and C++ unless you know that the runtime will detect null pointer errors. If it doesn’t, you’ll just end up with tests that will crash mysteriously, if you are lucky. If you are unlucky, your tests will just be silently and hopelessly wrong. They will corrupt memory as they run, and you’ll never know.

Updated:

Leave a comment