A part of this article covers the issues arising from designing C/C++ interfaces, and assumes basic knowledge of those languages.
This is a fifth article in the SDK Design Goal series. Please see the introduction article “How to present the licensed technology the right way?”.
The technology you have developed works fine on a 32-bit Microsoft Windows. This is where it has been developed, this is where it has been used internally, and this is the platform it supports. Your marketing team sees no needs to support anything else, because your product already runs on both 32-bit and 64-bit Windows. Developing a separate 64-bit product would require significantly more effort, duplicate QA, and the expected performance gain for a 64-bit version is less than 1%.
Considering this, you decide to limit your SDK to a 32-bit Windows, and the API you created for it cements that. You happily using wchar_t Unicode strings all over the API, and you expect your SetIntValue function to accept pointers as well, because they’re the same size as the integer type on this platform. Your first licensees are also using 32-bit only, and don’t care about it. Everything goes well.
Suddenly one of your existing customers – one which brings you significant revenue – wants to develop a separate 64-bit Windows version. There may be many reasons for that. Maybe Microsoft Store only shows their 32-bit application for the users who run 32-bit Windows, and their numbers keep shrinking. Maybe they integrate another SDK which uses a driver, which in turn requires a communication library – and both must be 64-bit. Or maybe they just want to.
If your API is designed for future portability from the beginning, all you need to do now is to port your software to 64-bit and recompile it. However if you used anything specific to the architecture, such as an assumption that pointer sizes are the same as integer size, now you have to change the API. And from a previous design goal we know that API changes which require the partners to change and recompile the code are bad (if you didn’t read the previous article and wonder why is it the case, or why can’t we just create a separate 64-bit version of API, please take a break and check it now. I will wait).
Now, the next Big Customer comes in, but they do not use Windows. They want a Mac version, and they would like a Linux version too. Those platforms support wchar_t Unicode strings, but almost nobody uses them. Instead the majority of developers use, and the majority of system calls accept Unicode strings as UTF-8 char strings. Now you need to get rid of wchar_t as well.
Now you need to deal with Unicode strings in UTF-8, so again you need to change your API. Not only this really annoys your existing customers because they have to recompile their code again, but they also are angry because they could care less about them – they did not need Mac/Linux support in the first place, so in their opinion you’re wasting your time because you didn’t do your homework, and did not plan for the future portability in advance!
The opposite may happen too – you have the API which works fine on Linux and Mac, which uses stdio and accepts the files through FILE structure (which is more convenient than a file name, because the user can open the file themselves and see/handle the error, if any). You might even think nothing needs to be changed when you port it to Windows – after all, it has stdio too, right? Unfortunately you provide your API via a static library, and this might make your API incompatible depending on link options (due to different stdio used by you and the partner.
You might ask, what about languages which do not have such portability issues, such as Java? Even there such issues might happen. For example, you have created a Java API for Android, and since you only planned to support Android, you used some Android classes there – for example, your API requires to, and uses various Android contexts. So when you decide to offer your Java API to be used on TomCat applications on Linux, you have to modify it, and those modifications would require Android users to rebuild their application as well.
Of course you already understood what needs to be done: Design your SDK for Future Portability. To do so:
- Have the right mindset from the beginning. Expect and anticipate that if your library is successful, you will have to port it to different platforms, architectures and operating systems.
- Mark everything which is part of your API and depends on the platform you use. Common type sizes, string formats, byte order, stdio objects are prime suspects. Replace them either by a more generic types (you know what always has a size of a pointer, no matter what a platform is? A void pointer), or replace them by the macros which you can #define depending on configuration.
- Isolate the parts which depend on the environment. For example, if your API needs a pointer to a Windows API function, do not make it part of mandatory initialization structure. Add a separate SetFuncPointer function – then, when you decide to support other platforms, you can #ifdef it, or move it into a separate header.
Finally, you might ask, what happens if I decide not to support those platforms? In this case your competitors would quickly add necessary support, and start taking your customers. For a licensee it is much easier to use the same API on all platforms from all vendors. So a vendor which covers all existing platforms will win the deal even if their API is not as good as yours – if yours only cover one of the needed platforms.