Typically, application development is carried out using specific software tools, such as frameworks and software libraries. The connection between applications and tools is made with a set of special commands known as classes and functions. The implementation of these connections is generically called the Application Programming Interface (API).
Our C3D Toolkit is a software development kit (SDK), and so all of its components offer application programming interfaces to access their functions in engineering applications. Like any other software vendor, we are constantly updating our C3D Toolkit with new functions, which directly affect the APIs of the Toolkit’s components. As APIs are adjusted, we need to at the same time keep them working, as well as remaining completely predictable for users.
In this note, we tell you about the considerations we take into account when developing new APIs: how we ensure backward compatibility, and maintain stability and quality.
API Development Features
First, let’s look in greater detail at the function of APIs. Application programming interfaces are created by developers for use by other developers to write their own software products. This allows us to assert the following:
- APIs solve a variety of problems under different scenarios based on the data they receive
- APIs are used in a variety of environments, such as with different operating system, compiler brands, and compiler versions
- Products developed with APIs have their own life cycles, development plans, and release plans
Given the above, programming interfaces must meet the following basic requirements:
- APIs should be simple, intuitive, and consistent
- APIs must support different development platforms
- The design of APIs should allow them to be developed further without compromising stability
- APIs should provide stability and backward compatibility
Here it is worth dwelling in greater detail on two of the concepts listed above -- stability and backward compatibility -- and what these mean for APIs.
Stability means that APIs should not vary much over the longest possible period of time. At the same time, if there is a need to modify an API, then there is a clear procedure for making changes, aimed primarily at maintaining the users’ ease of use of the API.
Backward Compatibility means that users’ code written with a previous version of an API works the same with the new version of the API.
Development Principles for New C3D Kernel APIs
When we develop the C3D Modeler geometric kernel API, we strive to maintain the following important principles:
1. Adding a new interface is never difficult. Removing an existing one that has, for example, become redundant can irritate our API users. Therefore, we have an entire procedure for when we remove an interface, including informing users about the need to switch to the new interface and replacing the old one. To minimize problems, we provide a minimum set of interfaces by implementing only those functions needed for the API to work at the moment.
2. Developing an API means extending the programming interfaces, and we should carry it out in a way in which there is no need to make any change to the existing API. At the same time, the extended interfaces should not adversely affect their stability, nor violate their compatibility with previous versions of the API.
There will be the rules we use when extending our API below.
3. When developing the structure of parameters of each specific interface function, we work to avoid long lists of arguments, because large numbers are more difficult to control and can lead to the interface operating incorrectly. Our solution is to single out semantic blocks of arguments and pack them into separate structures.
4. Ownership of the objects created by an interface must be unambiguous. To comply with this principle, it is good practice to use smart pointers.
5. The implementation of new interfaces should, if possible, be accompanied by the use of existing ones. This is known as “implicit testing” when a new API function uses other interfaces.
6. Finally, we note the importance of documentation. Any programming interface that users see should be well documented. Even when an API is well-designed, it is difficult for external users to understand how it operates without detailed descriptions of all key points.
Example of Developing A New C3D Kernel API
Let’s look at an example that uses our principles in developing a new API function for our geometric kernel. Below, we list several deprecated functions that were formerly used to construct fillet bodies in different ways. The three interfaces used many arguments!
Here is the replacement interface for the three previous ones. Notice that all parameters used as arguments in the old functions are encapsulated in a single structure, the MbShellFilletValues class:
Let’s take a closer look at the MbShellFilletValues class.
While developing the MbShellFilletValues class, we followed the principles described above. The class provides a special constructor for each construction method (principle 1) and employs smart pointers (principle 4). As well, it provides the ability to extend the fillet constructing function without violating backward compatibility of the interface (principle 2). For instance, you can add a new build method by defining a constructor, or extend the function of an existing interface by adding different build parameters and a method to initialize them. However, users can continue to use this interface, because nothing in terms of compatibility changes for them.
Ensuring Stability and Backward Compatibility In C3D’s API
So far we’ve covered what API stability and backward compatibility are, and emphasized their importance for well-designed programming interfaces. Let’s find out how we are ensuring API stability and compatibility at C3D Labs.
First, let me clarify that within one major version (C3D Toolkit 2022, for example) no large changes are made. Updating the API in a revision does not violate the compatibility of users’ products, as massive API changes are only allowed when a major version is released. When we declare an interface obsolete, we accompany it with an indication of its validity period; following the specified period, the obsolete API can be deleted at any time and ceases to be stable. Throughout this, we ensure the stability of the kernel interface’s behavior.
Speaking of the stability and compatibility of programming interfaces, it is impossible not to say something about their development and extensions. At C3D, we adhere to the following rules when changing the kernel API. The rules apply to all interfaces: functions, classes, enumerations, and public constants.
- An interface cannot be moved or renamed without providing backward compatible aliases.
- We cannot change the calling convention, the number of parameters, or their type in the interface function; the sole exception is that we can add a new parameter to the end of the existing function’s parameters list, with an obligatory indication of the default value. (A similar rule applies to methods of interface classes.)
- We are not allowed to delete, move, or change public fields in interface structures, with one exception: When adding a new field to a structure, we can add it only to the end of the fields list. When adding a new value to an enum, it also can only be added to the end of the values list.
- If it is necessary to make changes to the interface class method, a new method should be added, with the existing one declared obsolete. The new method should not violate or change the purpose or behavior of the existing one. (The same applies to the interface function.) There are, however, exceptions here that limit the maintenance of stability and compatibility: an interface can be backwards compatible without being deprecated if a fatal error makes it unusable; interfaces in development are not required to comply with stability requirements.
For the convenience of users, we provide information about API changes by describing them in the changes.txt file of the corresponding C3D release; marking as “experimental” in comments the interfaces that are under development (unstable); and marking the interface to be deleted as “obsolete” and stating the interface that replaces it. The timing of deletion of the obsolete interface is indicated in the message during compilation; see below.
warning C4996: ’FilletSolid’:This is a deprecated API that will be removed in version 2022! Use FilletSolid with ’MbShellFilletValues’ parameter instead.
In these ways we strive to provide our users with components for developing engineering applications that have intuitive and convenient programming interfaces, with stability and compatibility in which users can rest assured.