7 Tips for Software Versioning
Here's what you can do today to build apps that show resilience in the face of change.
by Alex Krapf
June 1, 2006
As developers, we tend to think of a software version as something represented by some metadata maintained in our version control system. This (mis)conception is responsible for a lot of grief for our users. Consider these questions: How does your version control system help you...
- Run several versions of your application concurrently, maybe even in one process?
- Ensure that changes you make to a published framework don't break the framework's users?
- Read/write data that was created by older versions of your software?
- Determine the version and version compatibility of an object or service at run time?
Remember how only a few years back, we used to think that thread-safe programming was something that only a select few had to worry about because most of us would never run into it? Well, today hyper-threaded multicore machines are appearing on every desktop, modern runtime environments are inherently multithreaded, and you'd better be familiar with writing and debugging multithreaded applications. I would say that today we are at a similar turning point with respect to "version-safe" software.
Many best practices in object-oriented programming are at odds with version-safe programming. Take the use of final (or "sealed," in .NET parlance) types, for example: A common recommendation is that any type you don't intend to be subclassed should be marked as final. What does it mean to have a final type when its implementation changes over time? That does not make the type look very "final," and a different design allowing multiple versions of the type to coexist peacefully might be more appropriate when taking versioning considerations into account.
My dream solution would put versioning support into the core language specification, be it Java or .NET, thereby allowing such things as "versioned" final types. But in the absence of such revolutionary updates, you can still do things today to build applications that show resilience in the face of change, or, as I like to say, "build applications that scale over time." Most programming languages have some specific features on which you would build your versioning support. Java, for example, has classloaders; .NET has application domains and codebases; and C++ has typedefs, templates, and dynamically loaded modules.
Due to the differences between these technologies, each language has its own versioning techniques and problems. You can find some good publications and presentations on this topic (see Resources), but they often include contradictory recommendations. Here are seven observations and recommendations, categorized as "non-controversial" and "controversial."
Non-Controversial Tip #1: Avoid using extensible implementation types as return types
Any method that returns an object should return it through an interface or at least an abstract type. The use of interfaces is actually controversial; we'll discuss why in the next section.
What does it mean if you return an extensible, concrete type from a method? You're tying yourself to the implementation of that type without guaranteeing that it won't change—a bad idea under any circumstances.
Constructors represent a special case of this rule. Some people think the flexibility benefits of using object factories are outweighed by the reduced readability and increased verbosity of the code you have to write. We will also revisit this issue in the next section.
Non-Controversial Tip #2: Include programmatically queryable version info
Whether you use .NET attributes, whether you go Java5 all the way and use a custom attribute, or whether you use a field/method doesn't matter much, but it's always a good idea to be able to "reflect" on the version of a type. This allows you to make runtime decisions based on class version, just like you can make decisions based on class type.
You might need to work a little harder to allow your versioned types to coexist in one process, but it can be done.
Back to top