Penetrability refers to the extent to which a developer must understand the underlying implementation details of an API and the extent to which the developer is able to understand those details. For example, an API that is basically a very thin wrapper over some lower level API probably requires that anyone using that API understands the low level implementation details before being able to use the API successfully.
Sometimes problems that users experience in an API are due to the extent to which they need to understand lower level details and the support that the API provides for gaining that understanding. Depending on the user, the necessary changes to an API in these cases may require changing the abstraction level by adding new higher level abstractions on top of the implementation details so that there is absolutely no need to understand those details. Or, at the other extreme, it may require reducing the abstraction level so that developers can more easily understand the implementation details.
In this posting, I’d like to try to explain how you might evaluate your own API to determine how much of the underlying implementation your users will need to know. Refer to the first article in this series for a description of how to do a task analysis of your API and then read on…
For each goal that the user might attempt to accomplish with the API, describe the extent to which the user needs to understand the implementation details of the API and the extent to which these details are exposed to the user.
For example, in the System.IO namespace, one user goal is to write code to append text to a file. In order to write code that uses the StreamWriter class effectively, the user needs to know:
- If the class is thread safe or not by default and the extent to which this can be configured.
- The character encoding that the class supports by default, and the extent to which this can be configured.
Users can discover from the documentation that the class is not thread safe and can find more details about the character encoding that the class supports. To create a thread safe wrapper around a StreamWriter instance the base class of StreamWriter, TextWriter is used. TextWriter exposes a Synchronize method that creates a thread safe wrapper around any class that derives from TextWriter. Users thus need to find out whether or not StreamWriter derives from TextWriter to determine whether or not they can use TextWriter.Synchronize to create a thread safe wrapper around StreamWriter.
Thus in this example, users need to know the context in which the class is defined and some of the working details of the class, particularly those details that are set by default.
There are other ways to expose API details to the user. For example, the API source code provides most of the detail that a user might need, but at the cost of having to parse and understand the code. Naming conventions used in the API can also help expose some of the details. For example, class, parameter, property, method and variable names might indicate to the user their purpose, scope, visibility etc. When inspecting these members in a debugger, such naming conventions can help users identify those members that they have direct access and control over from the internal working details of the classes they are inspecting in the debugger. It is important to figure out the details of the API that users need to know in order to be able to design the API in such a way that communicates these details clearly.
Penetrability can be defined in the following terms.
- If the API exposes only enough information to allow the user to distinguish between different methods and classes and if this is the only information that the user needs to attend to, the API provides and requires only minimal penetrability, or a snapshot view into the details of the API.
- If the API exposes enough information to allow the user to understand the context or scope of the particular part of the API that the user is working with, and if this is the only information that the user needs to attend to, the API provides and requires a context driven view of the details of the API.
- If the API exposes enough information to allow the user to understand the intricate working details of the API and if the user must attend to this information, the API provides and requires an expansive view of the details of the API.
Note that in the above definition penetrability is defined both in terms of the requirements from the perspective of the user working with the API and the extent to which the API provides support for gaining that perspective. In other words, describing penetrability from the perspective of the API describes the extent to which the developer must understand implementation details. Describing penetrability from the perspective of the developer describes the extent to which the developer demands to be able to understand the underlying implementation details. The developer might not require that amount of information in order to be able to use the API effectively, but they might want that amount of information in order to be able to feel comfortable using that API.
Having defined penetrability in the above terms, the System.IO namespace supports and requires a context driven view of the API.