Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
High quality software can be changed and improved without introducing instability into existing features and workflows. In order to maintain the stability of a project, a mixture of coding practices and release management practices need to be applied. On the release management front, things like maintaining good notes about what has changed and communicating those changes through a semantic versioning policy can go far. When it comes to the code itself, I've recently found that the ideas put forth by Mike Burns about designing unobtrusive APIs to come in handy.
While Mike's original article gave me a lot of inspiration, it lacked explicit examples. This caused me to write Practicing Ruby 2.7, which contains a bunch of case studies that illustrate what the idea of "Unobtrusive Ruby" means to me. You can read the full article for details, but examples related to API stability include:
Implementing dependency injection by passing in instances of objects rather passing a class object and having the method create an instance for you.
Using duck typing and explicit registering for a plugin system rather than inheritance hooks, preventing plugins from having to inherit from a common base class.
Building an API which uses blocks to register callbacks rather than expecting the client objects to implement specific methods.
Writing high level APIs which provide simple 'data in, data out' operations that don't require complex setup and teardown while reducing the number of classes the user needs to interact with.
The thing that all of these examples have in common is that they keep the number of publicly visible moving parts to a minimum, which increases API stability. Because many bugs in client code are a result in changes to upstream libraries or a misunderstanding of how those libraries are meant to work, we can increase stability at the application level by improving the way we write our libraries.
While I've focused here on how to improve stability through design, it goes without saying that a solid testing strategy is also important. Even for those that don't do strict test-driven development should at the very least have some solid integration tests and regression tests. For more on that topic, see Practicing Ruby 2.5.
Turn the page if you're taking the linear tour, or feel free to jump around via the sidebar.