An API is Forever

An API is an interface. Those of you that have worked with COM already know that once an interface is published, it can never, ever change. Ever. Not until the end of time. The reason is that some bit of code somewhere is going to be using that interface, and if you change it you’ve just broken that code. Of course, not changing an interface also means not deleting a portion of it.

(I apologize for the corrupted formatting of the code. I’m in the middle of a conversion from Blogger to WordPress.)

At my job I have worked on, and currently maintain, a few APIs implemented in Windows DLLs, some of which have been around for a long time. I had to rewrite significant portions of the internal implementation of one particular DLL in order to add some new features, fix several bugs, and improve the overall performance of the DLL. The old API exposed from the DLL wasn’t able to take advantage of the new features, and it wasn’t designed all that well to begin with. Since nearly all of the use of that API was through a C++ template wrapper implemented in a header file, I just added a new set of API functions and changed the wrapper to use the new API.

I did some digging around in the code base (it’s a huge code base) to see what else was using the class wrapper around the DLL, and I found out that the component was more widely used than I had anticipated. There were components in all corners of the code base using it.

The component was still desperately in need of a new API, so what I did was write a new one and forward the old API to the new API. I changed the header file that declared the API functions to look like this:

I hoped that the comments would make clear that the use of the old API is discouraged, and that any clients should move to the new API. Likewise, in the implementation, I forwarded the old API functions to the new API functions:

In some cases, forwarding the old API to the new API meant that I had to make the new API implement the semantics of the old API whenever it was being called through the old API functions. For example, the old API had a nasty habit of writing sub-keys all over the company’s registry key under HKLM\SOFTWARE. I made the new API put these settings into a tidy sub-key that belonged to the API, like HKLM\SOFTWARE\CompanyName\Fictional. It would even hunt down the all of the old keys created by the old API and move them into the new sub-key. In a later story I’ll explain why I had to preserve that behavior in the old API, but for now just remember that I did.

Recently, a co-worker changed a customer-specific branch of the archive so that it would point to the newest implementation of this component. He was writing an application using some libraries that called this component’s API. He asked me to help with at a problem he was having, and when I went to look in the registry at some of the settings the API had written, I was dismayed to find that the settings were in the old location, all over the root of the company’s HKLM\SOFTWARE key. At first, I thought that he was still somehow using the old DLL, but when I checked the version information on the DLL it was, indeed, the new one. How could this be?

As I mentioned above, most clients use this DLL through a C++ wrapper, since most of the archive is now C++. Only a handful of older C modules call this API directly. The wrapper is a C++ template class, which means it is implemented in a header file. This customer archive was still pointing at the old version of the header that called the old API. As I mentioned above, the new API preserves the semantics of the old API when it is called through the old API, and that’s what we were seeing here.

My first suggestion, without really thinking about it, was to change the archive to point at the newer header. My co-worker pointed out that this would mean rebuilding all of the components in the archive that used the header, not just the one he was working on.

We left it alone, and everything is working as it should be. At some point in the future we’ll move all of the other components up to the newer version of the wrapper, and they’ll start seeing the behavior associated with the new API. In the meantime, the customer still benefits from a less buggy and more efficient component.



Leave a Reply

Your email address will not be published. Required fields are marked *