Creating an SDK is as simple as creating a MySDK.lib file and including a header, right? Wrong. The keys to any successful SDK are quite a lot of work to create. At the very least, the keys to successfully creating an SDK include the following:
- fault tolerance
- error reporting
- debugability (not a word I realize)
- documentation with cross links
- fully defined enumerations
- No mysterious function names or variables. All function names should be obvious.
- consistent naming convention
- cross-platform compatibility
- code samples in the documentation
- all documentation must list possible values for all function parameters.
- accounting for the fact that users may not initialize your library properly or shut it down properly.
- standards compliance. Is there a standard that must be used (h.264, IEEE, etc).
- carefully controlled resources.
Some other things that are usually helpful, which we'll call niceties, include:
- namespaces. Keeping things uniques to your SDK can help termendously with upgrades and inclusion in end-user products.
- default parameters. These clue users in to proper usage.
- testability. Unit tests and internal testing keep customers coming back. Very few users like to be beta testers.
- naming scheme. This means thinking hard about the name of your libraries and what they may look like to users.
- organization. Is everything in a single header, a series of libraries, how are they included in the project and so on.
- whether they are C compatible or cross language compatible. A hunk of code written in C++ could be compatible with Pascal or C with a bit of work. In fact, it could even be compatible with Python or PHP. Do you need this? Who would use it? Is this an untapped market for your SDK?
- scalability. You may think that this is a requirement and for many SDKs, it is. But a lot of SDKs are simple like the string functions in string.h or maybe the os lib in Python.
- extendability. Sometimes called extensibility, this is as simple as not painting yourself into a corner. Many MS functions have an extra parameter which is unused. You may have a versioning plan. Maybe, you have all of your future functions already defined in a header, but not explicitly working yet.
Now on to the details....
Fault tolerance means that your libraries and SDK can never crash. The basics of this are so easy to test, it's silly. Simply call all of your methods without initializing you libraries. If you crash, then you have work to do. Then start passing random variables to all of your methods. If you crash, you still have work to do. Lastly, play with the order of the function calls to test SDK state. It is very easy to put a library in a bad state and crash it with a poorly placed function. Crashes are impossible to debug and must be avoided.
The harder cases are hardware failures. You must deal with all of the possible cases where a file is not found, a disk drive disappears, you run out of memory, or the network cable becomes unplugged. This is very hard to test and you probably want a test crew for this level of testing.
Error reporting means defining a set of error codes and how they will be reported. There are a few ways to do this and all should be considered. One way is to have all methods of your SDK return an error code where an enumeration of 0 means no error occurred. If your OS supports it, then throwing exceptions is a good thing to do. Logging errors to a log window or to the OS's logging system are also good ways to report errors. Many applications report errors to a command-line or ComPort (RS-232). In any case, users cannot debug their applications without excellent error catching and reporting. Always provide example code of proper error reporting and usage in the documentation.
Debugability means making sure that you can run in a debugger. While this sounds simple, this can be very tricky. You don't want users of your SDK to ever have access to your internals and giving them structs/classes that allow them access means that you never really have control over state and error reporting goes out the window. Hide all classes that users shouldn't see which include window handles, file data, the current thread, memory allocations, and so on. Users should only see a minimum subset of data and they should only see integers or void* to data that you are controlling internally. Don't even give users a real pointer in case they try to reverse engineer it by casting hese pointers to other types: all pointers or handles should be virtual types that do not directly access any memory. All of this helps to make your application more debugable.
Documentation with cross links means cross-referencing. This should never be thought of as direct links all of the time because this can clutter the help files to the point to becoming noise. However, to a certain extent, you can say that the more cross links you provide, the better. If you are talking about file management, then links to OS file systems, file seeking, and memory management are all appropriate. You might want to talk about virtual file systems too. Think about related topics and provide talking points about those too.
Fully defined enumerations sounds like an implementation detail, right? Not really. Take a look at how carefully all of the enumeration name are chosen in OpenGL. They aren't wordy names or strange contexts. They all make sense in the context of graphics. Choose the wy that you choose to do enumerations very carefully. I suggest putting them into a namespace, choosing some sort of prefix, make sure that you choose a consistent caps logic Camel case, pascal case, all caps and so on), and that the names are logical and readable by your grandmother. This is where the detailed work that you put into your SDK really shines. Even if the rest of your SDK is imperfect, get this part right.
No mysterious function names or variables. This one is so obvious and yet so few developers get this right. Never name a file close function "FC" or "CloseThatFile" which both show a lack of care and/or craftsmanship. This is your time to shine so demonstrate your ability to create high-quality that everyone can read and use. Also, parameter names should give the user of your function a clue what is happening. E.g.
int fileRead (FileHandle* handle, BYTE* bufferPtr, int NumBytesToRead);
Consistent naming convention means that all of your functions that have related functionality have similar names. If you have a fileOpen, you must have a fileClose, fileRead, and fileWrite. Consistency, convention, and easy-to-learn are keys to widespread use of your API. This includes avoiding any spelling mistakes which are difficult to fix later.
Cross-platform compatibility means that for people to accept and begin using your SDK, you must also provide some cross-platform support. This can be as easy as making sure that your project compiles under GCC as well as Visual Studio. It can also mean a whole lot more like device drivers designed to provide OpenGL to a wide variety of hardware platforms. Users of your SDK are likely to expect some level of cross platform compatibility.
Code samples in the documentation means putting runnable code directly in the documentation that has a lot of different uses so that users can copy and paste it into their console app as a good starting point. Under previous versions of VisStudio (5 and 6) Microsoft had this formula right and their docs were filled with example code. Often, the best sample code available was in the MS Docs. They seem to have messed that formula up with more recent versions of VisStudio, but this concept is very helpful to newer users of your SDK and sometimes veterans.
All documentation must list possible values for all function parameters. While this may seem like overkill, this is a lifesaver. Users can get used to docs if the docs actually tell them something and this tidbit is just the thing to prevent a support call. Users do not want to call your help desk and listing all possible values into your functions (ranges are acceptable, obviously) creates a sense of trust with your end user as to overall quality, thorough docs, and it adds n air of "fully functional" API to see this level-of-detail in the docs.
Accounting for the fact that users may not initialize your library properly or shut it down properly. You must fail gracefully at all times with error codes, logs, or similar means to tell what went wrong. Once users understand your intent, the better off you'll be: fewer 'emergency' support calls.
Standards compliance. Most of the time, you must meet some standards. These can be TCP, H.264, UART, XBoxLive, or whatever. In fact, unless you are manufacturing the hardware yourself, it is likely that you must comply with some standards and the level of compliance should be noted and documented in your SDK docs.
Carefully controlled resources. Your library must release threads properly, close all file handles when done, and report memory allocation errors. Any place where the user is in control and must release a handle of some sort should be noted in your docs. Better yet, do not give users direct access to handles and give them ID numbers or GUIDs that they can use to identify the resource, but you'll be the one to manage the resource.
No comments:
Post a Comment