Friday, June 17, 2005

Versioning pt 2

Hmm, I just found this pdf file by Steve Vinosky of CORBA fame.

He details the problems of versioning in both the distributed object world as well as the messaging world. What's the difference? Well, when you think distributed object you typically think CORBA and COM. Basically, there is an object that has an interface and you call that interface to get work performed. The interface is fixed and is known at compile time. With a messaging system, like XML-RPC, you pass messages around that are flexible. When the system recieves the message, it parses it out and determines what to do with it.

Personally, I'm a fan of the messaging world. I think that objects are not meant for distributed computing. However, I also like the benefits of CORBA. I think, by using the any type, you can achieve a messaging system with CORBA. This will allow you to have a good level of typing mixed with the ability to version.

For instance, let's say you are creating a service to cook eggs. The distributed object looks something like:

enum EggType { ET_FRIED, ET_SCRAMBLED, ET_POACHED };

interface EggCooker
{
void cook_eggs( in EggType type );
};

The message type would look more like:

enum EggType { ET_FRIED, ET_SCRAMBLED, ET_POACHED };

interface EggCooker
{
void cook_eggs( in any message );
};

For version 1, message would be of type EggType. An exception would be thrown otherwise. However, in version two we want to add a boolean flag to determine whether to use EggBeaters or not.

For the distributed objects version, we have to do one of three things. First, we could make a completely separate interface and have it call the other interface internally. Second, we could add a new function to the interface...something like cook_eggs_ex. Third, we could make a non-backwards compatible change to cook_eggs to add the boolean flag. Most of the time, we opt for the third choice and our clients are the ones getting heartburn. However, it is worth noting that both the first and second choices are backwards compatible.

For the message based version, we simply create a struct that holds the important information.

struct eggs_parm_v2
{
EggType type;
bool useEggBeaters;
};

Now, the new clients wishing to use egg beaters can upgrade to version 2 and pass in a eggs_parm_v2 as the parameter instead of passing in just the EggType. However, version 1 users are satisfied b/c nothing need change, our internal processing function can switch on the type of the any and be completely backwards compatible.

In most cases, it is really a matter of style. I think the new interface way is a bit overboard, but adding a new function and using the any type are both good choices.

To me, the main thing is a concious effort by programmers to make thier works backwards compatible. It is not any harder than writing code that breaks compatibility and it makes our users so much happier. There are no silver bullets.

No comments: