The Artima Developer Community
Sponsored Link

Agile Buzz Forum
Working Effectively with Legacy Code

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
James Robertson

Posts: 29924
Nickname: jarober61
Registered: Jun, 2003

David Buck, Smalltalker at large
Working Effectively with Legacy Code Posted: Mar 27, 2006 9:40 AM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by James Robertson.
Original Post: Working Effectively with Legacy Code
Feed Title: Cincom Smalltalk Blog - Smalltalk with Rants
Feed URL: http://www.cincomsmalltalk.com/rssBlog/rssBlogView.xml
Feed Description: James Robertson comments on Cincom Smalltalk, the Smalltalk development community, and IT trends and issues in general.
Latest Agile Buzz Posts
Latest Agile Buzz Posts by James Robertson
Latest Posts From Cincom Smalltalk Blog - Smalltalk with Rants

Advertisement

The afternoon session I decided to attend is Michael Feather's talk. With luck, the Capucchino will keep me awake during the talk :)

For purposes of this talk, "Legacy Code" = "Large existing codebases that lack any real tests" - i.e., the language of the codebase isn't relevant. Michael started with a desire to do XP 6 years ago, but he was transitioning from a dirty codebase - as opposed to the C3 project, where they mostly abandoned the legacy codebase and started over. So, what are the possible approaches?

  • Pretend the existing code isn't there
  • Use TDD to create new classes, make direct changes to the old ones, hope they are correct (tends to miss code coverage)
  • We'd like to be more conservative - i.e., avoid going back to the existing code for changes
  • Is that viable?

Heh. His first example is a "legacy" C++ HTML generator. Sadly, it doesn't look all that different from my original HTML generation approach for the Silt server. I've gotten smarter about that over time, but it's an example I can definitely identify with. The testing approach to use here is one of documentation - the idea is to have tests demonstrate what the code does. Michael calls these characterization tests.

When you have legacy code, try not to add inline code to it. Instead, write tested code in new classes and methods, and delegate to them (IMHO, this can lead to interesting maintenance problems itself - hard to understand code). The techniques for doing this are called Sprout Method and Sprout Class. Interesting sideline here - refactoring is harder in a C++ codebase, due to the lack of good tools.

So the question is, why is he advocating a conservative approach? There are no tests for the existing codebase, so making changes can introduce unintentional (and hard to find) errors. Most of the development in this case is about preserving existing behavior. Most applications are glued together - we only find this out when we test pieces in isolation (unit tests). Some examples of glue:

  • Singletons
  • Internal Instantiation (class creating a hard coded instance of another class)
  • Concrete Dependency (when a class uses a concrete class, you'd better hope that class lets you know what is happening to it)

Tools matter a lot here - having the ability to safely extract methods and interfaces in the IDE help a lot. If not, then it's all manual (and harder). Breaking dependencies can get ugly. The Legacy Code Change Algorithm:

  1. Identify Change Points
  2. Identify Test Points
  3. Break Dependencies
  4. Write Tests
  5. Refactor Change

What about testing expensive operations? i.e., you don't want to call the real method in test because it will take too long (computationally expensive, lots of db interaction, whetever). There are mock frameworks for various languages, and you can always use polymorphism for test purposes. The rationale here is that you want to replace behavior without changing the base method. Sounds to me like people using C++ are on the short end of the stick here :)

Breaking Dependencies:

  • Extract an interface - safe for legacy code, but can take a little time. Mock interfaces stand in for real ones. The list of things to watch out for in C++ is long.
  • Extract Implementor - like extract interface, but we push the code down instead of up

Up next - a test exercise using Java or C#. Heh - I translated the Java code to Smalltalk and then did the exercise - as fast as everyone else refactored :). Interesting discussion over interfaces (Java, C++, C# - all of this is implicit in Smalltalk). The whole exercise dealt with parameterizing constructors, and then went into parametrizing methods. All one and the same thing in Smalltalk. From how these exercises are going, it's very, very obvious that it's tons easier to restructure and refactor code in Smalltalk than in the languages being used here.

What about methods in classes that cannot be (easily) instantiated? Try making it a static (Class, in Smalltalk terms) method until the mess can be cleaned up. "Utility classes" - ones with all static (Class) methods are a problem as well. Bottom line, the methods aren't where they belong. Preserve signatures, but create the appropriate methods in the appropriate classes.

Ahh, templates. Saved by dynamic typing :)

Summary: Tests are the ultimate safety net, providing an "exoskeleton" for your code. Fascinating thing at the end: We have to develop languages and tools which make it easier to recover. Smalltalk is not a silver bullet - you can write bad code using any language or toolset. However, I claim that Smalltalk makes it easier to get yourself out of the corners you code yourself into.

Read: Working Effectively with Legacy Code

Topic: Arbitrary "River of News" in BottomFeeder Previous Topic   Next Topic Topic: Five Signs of Code Rot

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use