Sensing and separation

Sensing and separation

Generally, when we want to get tests in place, there are two reasons to break dependencies: 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.

There are many techniques for Separation but one dominant technique for sensing.


One of the big problems that we confront in legacy code work is dependency. If we want to execute a piece of code by itself and see what it does, often we have to break dependencies on other code. But it’s hardly ever that simple. Often that other code is the only place we can easily sense the effects of our actions. If we can put some other code in its place and test through it, we can write our tests. In object orientation, these other pieces of code are often called fake objects.



Michael, C. (2008). Feathers. Working Effectively with Legacy Code.

Working with feedback

Changes in a system can be made in two primary ways.

Edit and Pray is pretty much the industry standard. When you use Edit and Pray, you carefully plan the changes you are going to make, you make sure that you understand the code you are going to modify, and then you start to make the changes. When you’re done, you run the system to see if the change was enabled, and then you poke around further to make sure that you didn’t break anything. The poking around is essential. When you make your changes, you are hoping and praying that you’ll get them right, and you take extra time when you are done to make sure that you did.

Cover and Modify is a different way of making changes. The idea behind it is that it is possible to work with a safety net when we change software. The safety net we use isn’t something that we put underneath our tables to catch us if we fall out of our chairs. Instead, it’s kind of like a cloak that we put over code we are working on to make sure that bad changes don’t leak out and infect the rest of our software. Covering software means covering it with tests.

Cover and modify means coding with tests, when we have a good set of test around a piece of code we can make changes and find out very quickly whether the effects were good or bad. Testing this way is “testing to attempt to show correctness“.

“Testing to detect change” is called regression testing. We periodically run tests that check for known good behavior to find out whether our software still works the way it did in the past.

Unit testing is one of the most important components in legacy code work. System-level regression tests are great, but small, localized tests are invaluable. They can give you feedback as you develop and allow you to refactor with much more safety. And we want unit testing because large tests have the following issues.

Issues with larger tests:

  1. error localization
  2. execution time
  3. Coverage. Its hard to see the connection between a piece of code and the values that exercise it.

Here are qualities of good unit tests:

  1. They run fast.
  2. They help us localize problems.

The main issue when making unit testing is the breakage of dependencies, legacy code can or cannot be as easy to segment as one might think sometimes but when we are looking for change we have to leave the aesthetics to the side some of the time, if you come around code that was altered this way you can heal the scar.


When you have to make a change in a legacy code base, here is an algorithm you can use.

  1. Identify change points. Where is the code you need to change?
  2. Find test points. Where is the place to put your test?
  3. Break dependencies. How to get past dependencies problems.
  4. Write tests.
  5. Make changes and refactor.


Michael, C. (2008). Feathers. Working Effectively with Legacy Code.

Changing Software

Changing code is a complicated task, there are ways we can come up to make the code extremely difficult to read for other developers on the other hand there is also ways to make it easier for future developers to understand.

But why do we change software ?

four main reasons.

  1. adding features,
  2. fixing a bug
  3. improving the design
  4. optimizing resources

Adding features and fixing a bug are only different in which point of view you are looking at it, for the customer something trivial may be a bug because he dosnt want it that way but for developers it may mean a new feature, never the less they have the same basic mechanic, we change the behavior in our application.

Behavior is the most important thing about software. It is what users depend on. Users like it when we add behavior (provided it is what they really wanted), but if we change or remove behavior they depend on (introduce bugs), they stop trusting us.

The act of improving design without changing its behavior is called refactoring. The idea behind refactoring is that we can make software more maintainable without changing behavior if we write tests to make sure that existing behavior doesn’t change and take small steps to verify that all along the process.

Optimization is the same as improving the design only with a different goal, we want to make the resource of a program low being this time or memory.

In general, three different things can change when we do work in a system:

  1. structure,
  2. functionality,
  3. and resource usage.

Preserving existing behavior is one of the largest challenges in software development. Even when we are changing primary features, we often have very large areas of behavior that we have to preserve.

Risky change

To make good code we have to embrace change, if we avoid making new classes, making or new methods then the current ones are gonna get bigger and larger to the point where they became imposible to understand.

Another advantage that comes with changing code is that we get better developers, experienced developers that are not afraid of doing code changes and making changes with good insight of the code make it easier to maintain a team that knows what they are making.


Michael, C. (2008). Feathers. Working Effectively with Legacy Code.

Introduction to legacy code

The code must be flexible enough to accommodate to change, change is a constant matter in software development and is such an important task that many if not most of the developers that join the software forces at the moment are tasked primarily with the task of maintain software and not many of them will ever get to write code or

develop new features or applications.

But what is legacy code?

In the industry, legacy code is often used as a slang term for difficult-to-change code that we don’t understand. But over years of working with teams, helping them get past serious code problems, I’ve arrived at a different definition.

To me, legacy code is simply code without tests. I’ve gotten some grief for this definition. What do tests have to do with whether code is bad?

Code without tests is bad code. It doesn’t matter how well written it is; it doesn’t matter how pretty or object-oriented or well- encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don’t know if our code is getting better or worse.

Michael, C. (2008). Feathers. Working Effectively with Legacy Code.