Change is the only constant
Philip Van Hoof (also known as my
evil twin, not so long ago, thought his Tinymail API was frozen, but
ended up having to change it anyway.
It's been said many times, but there it is again: change is the only constant.
When I tell people about
XPLC, they often think that it's about dynamically loading plug-ins, extending applications at runtime. But it's not really that. What it is about is
interfaces.
It just so happens that interfaces are a requisite for plug-ins, but really, they are everywhere. An API is an interface, as is an ABI. It's just that the first obvious use for an interface is plug-ins, extensions, or whatever dynamically loaded piece of code.
Some project are very aware of their interfaces, glibc or libpng for example. The former uses all sorts of ELF tricks (like sonames and symbol versioning) so that code linked against an earlier version can still work correctly with a newer version (this is called backward compatibility). The latter is careful to never remove or change the semantic of existing functions, only adding new functions while providing a way of knowing whether the functions are available for your use. Some, like GTK+, have taken different approach, where they made sure that incompatible versions can be installed in parallel (the downside being that a bug fix in the newer, still supported version, will not fix applications still using the older version).
What does XPLC bring to the game? It helps manage all of these things, in a portable way. Those ELF tricks I mentioned, they do not work on platforms that do not use ELF, like Mac OS X or Windows. Also, the technique used for defining interfaces not only allows backward compatibility, but also forward compatibility, where an application that only uses a subset of an interface can easily use an older version of the interface (that is still supported by the library implementing it, of course) on purpose, so that its runtime requirements are lesser. This is not possible at all with the ELF symbol versioning, for example.
A library providing its interfaces using XPLC can even make incompatible changes in its interface in a new version (such as changing the semantics or replacing a function by another), while still maintaining backward compatibility, admittedly at the cost of providing adapter glue. Some people seem to think that you
have to provide this glue, but it is left at the discretion of the library's implementers (consider it's also possible to stack adapters, making things work, but likely an extra cost in runtime efficiency). If we think back to the GTK+ approach of parallel installation, this means that fixes to the library will fix the users of the old interface just as much as those of the new one. There is a downside, of course, in that this adapter glue is more code, and that more code brings "opportunity" for more bugs, but still, the glue can also be fixed in a newer version, without breaking the interfaces.
When ones considers the possibility of security bugs in older, unmaintained versions of a library, the only two ways would be to update all the applications to use the new version of the interface (which is impractical, for clear reasons, anyone needing a reminder can only look at
XMMS, still widely used, despite many attempts at killing it) or to keep on updating the older version. With XPLC, the adapter glue can be fixed, and since the applications are, in fact, using the new version of the library, they can actually migrate to the newer interface incrementally, so it can be done without major disruption or the heavy burden of conversion work.
There are also sometimes optional modules in a library, that are controlled by compile-time options (to the "configure" script, for example). XPLC interfaces are
discoverable, meaning that you get the best out of both strong typing and dynamic typing: you can explore the interfaces an object implements dynamically, and when it tells you that it is indeed supported, it is a strong, binding contract.
So, in summary, with XPLC interfaces, one can get:
- A stable ABI.
- Backward compatibility, even through "incompatible" changes.
- Forward compatibility, allowing application writers to easily target older version.
- Opportunity for incremental migration from older to newer APIs.
- Discover optional modules at runtime, without requiring extra shared objects.
All of this is in the simple case of a single implementation of a given interface, but as Philip is demonstrating, it only starts there. He uses interfaces to let users of
Tinymail provide their own alternative bits and pieces, mixing and matching as the task at need requires. Not to mention unit testing, language neutrality (including scripting), and so on...
Syndicated 2006-11-29 21:22:14 (Updated 2006-11-29 21:28:05) from Pierre Phaneuf