Wednesday, September 20, 2006

Namespace Refactoring

Originally Posted On: 06/16/2005 12:05:04
Over the last week or so, I've spend some time refactoring some namespaces in the C# codebase at work. There were some things that had just been languishing in the wrong
place for a long time. We had a few days to spend cleaning up things that we normally have to live with, so this is one of the things we decided to fix. Like any other refactoring, NS refactoring shouldn't change any system functionality, but just improve the overall design. It's a lot like renaming a class, and maybe it boils down to the exact same thing in the end. Luckily, this codebase is well covered with tests. We have unit tests (class level functionality), acceptance tests (system or subsystem level functionality), and smoke tests (installation functionality).

This process would have taken many times longer with much higher risks without the tests. Even with these tests, there was plenty of pain to feel. I'm a sharing kind of guy, so here's what hurt:

  • File Names: Don't ever use a object type for any part of a configuration file, log (thanks to Andy Sipe for the catch here) file, etc. For the actual code itself, there isn't a problem since all you have to do is rename the file. Whenever the config or logfile depends on the namespace, however, there are several locations you have to go to fix things up again. This bubbles through the system, and clear up to the install process. For upgrades, there will be difficulties merging from old to new when configuration file names change from version to version. For me, it helped to think about the fully qualified type as a unique ID in the db. Whenever that ID is used for something other than identity, trouble follows. The same is true for types. The rule here is name all files after their intended use, not after their location or namespace.

  • Object Identifiers: We used a persistable object layer to insulate the code from the db in the system. When we implemented the object identifier subsystem, we had the object ID's generated from the type of the object. This means that we had an OID table in the database, with type names in one of the columns. This makes the design very resistant to NS refactoring, since it would require a db upgrade to change the code. We ended up putting a mapping layer in the OID layer that takes a type and returns the "name" of the OID object to use. For now, we're using the "old" OID names to prevent an immediate database upgrade. In a future refactoring, that's another thing we'll want to fix.

  • Configuration File Locations: Use the "once and only once" (DRY) principle ruthlessly when setting up config files. We use VSS (don't start with me) and had originally shared various files between all of the projects that needed them. After going through the process of NS refactoring, I believe the correct way to do this would be to store any shared configuration files in a single location, then make it part of the build process (NAnt) to copy the configuration file from the one location to the test location before running the test. For files that are used only once, put them where they're used, but if they ever become shared, spend the time to put them in the single location, and change the build/test to get them there.

  • Tests: Don't even try to do this over a large set of files without good tests around them. The unit tests aren't really useful for this refactoring. The compiler tells you most of the errors that are at this level. The acceptance tests are good since they pull all the parts of the system together, but they can be fairly long running (some of ours take many minutes to run). The best tests for this kind of refactoring are the smoke tests. These validate the installation for the various end products, and generally only take a few seconds to run for each one (plus file copy & startup time).

  • Conclusion: Overall I think this kind of maintenance should be a regular activity.  Company names change, the overall design change.  Failure to update the NS is another "broken window" or "smell" that Andy Hunt and Dave Thomas mention in their Pragmatic Programming book.

No comments: