API Design Kata
For our fortnightly coding dojo I recently suggested to focus on API design instead of implementation – at least for one session. The idea was that APIs live much longer than API implementations and that consequently flaws in the API design hurt much more than flaws in the actual algorithms. And because developers code much more often than they design APIs, the need for practice should be expected to be much more urgent.
Our goal was to design a generic caching API. Some use cases were given:
- Lookup a value from the cache.
- Compute a value that is not currently cached.
- Let a value computation register dependencies. A dependency is a representation of a mutable entity.
- Invalidate a dependency. All values whose computation registered that dependency must be removed from the cache.
- Configure a maximum cache size.
- Let one cache be responsible for fundamentally different classes of value at the same time.
The approach was to write the API, only, and to provide test cases simply to evaluate how a client would use the API. No implementation of the actual API was allowed, just implementation of callback interfaces that are normally provided by clients of the cache.
Under this assumption the tests would not run, but the test code was using the API and had to look natural and understandable. Of course, the crucial aspect was to make the API convenient for clients. API documentation snippets were written only as far as absolutely necessary.
We decided to work on a single laptop connected to a beamer to allow all participants to comment and implement improvements alternatingly. It turned out that it is surprisingly difficult to build an API without building the implementation. There is the temptation to let the intended internal data structure shine through in the API (“But how are dependencies stored after all?”) when the client of the API couldn’t care less.
There is also the tendency to skip the ‘test-driven’ design and write down the cache interface immediately when in fact the tests given you a good feeling which information has to be provided to the cache somehow.
It was observed that some upfront drawing would have helped a lot. It wouldn’t have to be proper UML, but an overview of the entities involved and their relationships would have given us a quicker start. Caching is more complex than the above use cases might suggest.
Java generics were a recurring topic. While we are all used to instantiating generic classes, actually defining the right type parameters for an interface is different matter.
We talked a bit about code style. The
@Nonnull annotation sparked the most intense discussion.
If you decide to repeat the kata, also think (after the API is done) about possible performance implications of the design choices. Look for further missing features. On the other hand, look for redundant features that only make the API harder to understand.