Attributes and API usability revisited
I posted a query last week requesting feedback on the use of attributes in an API and their effect on the usability of that API (thanks for all the responses!). My query was driven by a study that I was running and that is now complete. I promised that I would describe the results that we obtained from the study with respect to attributes.
The API that we studied makes extensive use of attributes (I don't want to go into the details of the specific API so I'll just use finctional examples here to demonstrate the different points). To achieve any of the functionality of the API, developers must decorate their code with specific attributes. Nothing useful can be achieved in the API without doing so. For example, the API is used to create instances of Foo which can then be plugged in to some executable framework which calls methods on Foo. The API exposes a Foo base class that must be derived from, but also a Foo attribute that must be used to decorate the derived class with, like so:
[Foo(“Some property“,“Some other property“)]
class MyFoo : Foo
Many of the participants thought that the Foo attribute was overkill and was not required - they felt that deriving from Foo should be enough.
Deriving a class from Foo and decorating it with the Foo attribute is still not enough to achieve any useful functionality. You need to create some public members (properties or fields) and then decorate them with the Bar attribute. Only then will the execution context know which members of your class it should access. On top of this, you need to override one of three methods that the Foo class defines. In the example below, we've overridden Method1:
[Foo(“Some property“,“Some other property“)]
class MyFoo : Foo
{
[Bar(optionalparam1=false, optionalparam2=0)]
public string Message;
public void override Method1()
{...}
...
}
Note those optional parameters in the Bar attribute. These were pretty critical to the success of participants in the study. In order to implement some particular behavior, participants needed to alter the values of those optional parameters, and to set some other parameters. In many cases though, participants would not consider the attributes when they were thinking about what they needed to do in order to accomplish a given task. Instead, they would concentrate on their implementation of Method1 and think about what they needed to change there. They would end up spending a lot of time writing code that wouldn't help them accomplish their goal. In many cases, participants needed to be prompted to consider the attributes as a means to accomplish the goal.
The problem was made worse by more advanced tasks that required participants to decorate members with two attributes, and to set some optional parameters to specific values:
[Blah(“some string“)]
[Bar(optionalparam1=true, optionalparam2=0)]
public string Message;
Note how optionalparam1 is now true. Without being set to true, the attribute Blah has no effect. Blah on it's own also has no effect (if the user just used Blah and did not also decorate Message with the Bar attribute, nothing would happen). This caused some significant issues for participants in the study. In particular, the requirement to change the value of the optional parameter was difficult to see. The interaction between the two attributes just wasn't clear, since they both look as if they operate in isolation from one another.
These problems were not insurmountable. After a couple of hours, most participants started to grow accustomed to the way that the API worked. But it was clear that they struggled for a while to get to that level, making their initial experience with the API an unpleasant one. Furthermore, even after growing accustomed to the API, many participants commented that they felt that the attributes took a lot of control away from them and hid a lot of the details of how the API works. This made it much more difficult for them to form a conceptual model of how the API works. Thus resolving problems and bugs in their code was much more difficult for them than if they had been able to form an accurate conceptual model of the API.
The key take aways from the study with respect to the use of attributes are:
- Many developers will not expect core functionality of an API to be exposed through attributes only.
- Many developers will be uncomfortable with an API that exposes most, if not all, of it's functionality through attributes due to the perceived lack of control afforded by attributes.
- Combinatorial effects between different attributes should be avoided. If they cannot be avoided, the interaction between different attributes should be expressed through good naming.
- Many developers will be unlikely to consider modifying attributes in code to achieve particular functionality and will instead concentrate on their own (imperative) code.
Comments
- Anonymous
May 13, 2004
The comment has been removed - Anonymous
May 13, 2004
I think that at least some of the feedback that was obtained from the study is in agreement with your first point. Participants in the study did not appreciate or understand why they needed to both derive from some base class and use attributes on their class to achieve some functionality.
This was vital feedback to obtain - the team that designed the API had made certain assumptions that such a design was reasonable and would work well for their target customers. The feedback from the study suggested that this is not necessarily the case.
Regarding the rest of the results, I think we have good data to suggest that exposing APIs purely through attributes is not always perfectly fine. We most likely need to do follow up studies to determine the scenarios in which doing so makes more sense than the scenarios we considered in this study. Your example of exploiting security functionality through attributes could well be such a scenario in which it makes sense to use attributes. Clearly though, the data from this study indicates that there are also scenarios in which exposing APIs through attributes does not make sense. We need to collect more data so that we have a better understanding of the differences between these scenarios so that API designers know when it is reasonable to make extensive use of attributes and when it is unreasonable to do so. - Anonymous
May 13, 2004
Let me guess the design process.
Hmm, we don't want to design just another boring API, how would that show off our brilliance and intellect ? So what should we do ? I know, core functionality thru attributes ! - Anonymous
May 13, 2004
The comment has been removed - Anonymous
May 13, 2004
a follow up study into "best case" uses of attributes would be great to read about.... in fact, i would enjoy participating in that research. :)
There are many instances in which attribution/decoration provides, IMHO, much cleaner, easier to understand access to otherwise complex functionality. declarative transactions come to mind; so does declarative data mapping between biz object schema and database schema. instrumentation is also a great candidate for declarative, attribute based APIs; i.e., wrapping methods in [Trace],[LogEvent], [IncrementPerfCounter].
Moving towards more declarative, aspect-ish API's is a Good Thing. I can understand people having difficulties with complex composition, and impacts on readability and such; if we were to move foward with this, we should look into mechanisms to improve the experience from a dev/ui standpoint.