Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Working Effectively With Legacy Code

Working Effectively With Legacy Code

Prepared for ThoughtWorks Agile Technical Practices series

Chris Bushell

August 23, 2011
Tweet

More Decks by Chris Bushell

Other Decks in Programming

Transcript

  1. Cover and modify !  To modify the code we need

    tests !  To write tests we need to modify the code Cover and modify (modify and cover and modify)
  2. Legacy code change algorithm 1.  Identify change points 2.  Find

    test points 3.  Break dependencies 4.  Write tests 5.  Make changes and refactor
  3. Getting under test #1 public class CarFactory { ... public

    void buildCars(int number, String model, String brand) { for (int i = 0; i < number; i++) { String serialNumber; // // 20 lines of code to calculate new serial number // Car car = new Car(model, brand, serialNumber); Car.save(car); // triggers write to prod db } } } new CarDAO().save(car); // same problem Example thanks to http://www.wakaleo.com/component/content/article/240
  4. Getting under test #2 public class CarFactory { private CarDAO

    dao = new DefaultCarDAO(); public void setDao(CarDAO dao) { this.dao = dao; } public void buildCars(int number, String model, String brand) { for (int i = 0; i < number; i++) { String serialNumber = generateSerialNumber(model, brand); Car car = new Car(model, brand, serialNumber); dao.save (car); } } }
  5. Getting under test #3 public class CarFactoryTest { @Test public

    void carFactoryShouldGenerateTheRightSerialNumber() { CarFactory factory = new CarFactory(); CarDao mockDao = mock(CarDao.class); factory.setDao(mockDao); // I know what the serial number should be String expectedSerialNumber = "123456"; Car aToyotaPrius = new Car("Toyota", "Prius", expectedSerialNumber); factory.buildCars(1, "Toyota", "Prius"); verify(mockDao).save(refEq(aToyotaPrius))); } } // Though might be better to test (and code) serial number generation in isolation...
  6. Problematic code !   Real problem areas are the usual

    suspects: •  Long, excessively complex methods •  Missing abstractions (manifested as code-level duplication) •  Rampant interdependency •  Generally, poor choice of units !   Given poor units, difficult to derive meaningful unit tests (prior to change) !   May be wise to focus on mini integration tests first
  7. Characterization tests !   Characterization tests are an attempt to

    lock existing behaviour into an untested or undocumented system. !   It may be impossible to know the "correct" behaviour or safe way to refactor when making changes to or around legacy code that has no tests. This is especially true when no specification is available, or when other code may rely on flaws in the code. !   In these situations, it is typically best to assume that the old behaviour must be preserved unless explicit changes are required. !   In order to preserve the old behaviour, characterization tests are written that use the system as it has already been used. http://c2.com/cgi/wiki?CharacterizationTest
  8. When to refactor? Worst case: we don’t touch this area

    of code again for years (unlikely) It’s likely code will be read periodically regardless of whether or not it gets changed Likely case: changes cluster, code changed today is likely to be changed again soon
  9. Background !   Familiar domain !   Some level of

    relevance !   Old, no unit tests !   Not too big, not too small !   Java !   No (or minimal) dependencies
  10. The fiction ! HtmlStreamTokenizer is an important player in our

    systems landscape !   Carelessly, the original developers all died in a plane crash !   Safe to edit (refactor) – not a public library !   Some progress already made on characterization tests !   Pressing issues: Ø  Add support for H7 Ø  Pull definition of known tags (like H7) out of HtmlTag and into a separate class (intent, later, is to load these from config) Ø  HtmlException is a pain. We want to switch to using (well-chosen) subclasses of RuntimeException, but currently have little or no understanding of various scenarios where a HtmlException is thrown
  11. References !   Michael Feathers, Working Effectively With Legacy Code

    !   Robert C. Martin, Clean Code !   Martin Fowler, Refactoring: Improving the Design of Existing Code !   Chris Stevenson and Andy Pols, An Agile Approach to a Legacy System !   Dan North, Pimp My Architecture (http://www.infoq.com/ presentations/north-pimp-my-architecture)