Alex is a Software Engineer working on Android development tools, especially Android Studio, at Google. His interests include Java, API design, OOP, IDEs and testing. Alex spends some of his spare time working on Open Source, blogging, writing technical articles, and speaking at international conferences. The opinions expressed here represent his own and not those of his employer. Alex is a DZone MVB and is not an employee of DZone and has posted 49 posts at DZone. You can read more from them at their website. View Full User Profile

A Couple of Lessons I Learned About API Design

07.14.2009
| 8262 views |
  • submit to reddit

One of the reasons I work on open source is to become a better programmer. Learning opportunities come from different sources, being practice the more obvious one. In the last couple of weeks I learned two important lessons about API design not by practicing my craft, but from listening to our users.

What I learned may appear as common sense by most of you. But I’d like to share anyway. I found out that common sense is not always common :)

Lesson #1: Do not sacrifice flexibility and extensibility by overprotecting your users

It started with this thread, in which Szczepan Faber, creator of Mockito, mentions that he recently found FEST’s Assertions module and it “seems very interesting.” He suggested that, in order to make the library more extensible, the class Assertions and the classes implementing assertion methods could be made non-final.

I admit I’ve been adamant to open up those classes. I rejected previous requests due to the following reasons:

  1. The class Assertions contains only static methods: a bunch of overloaded assertThat methods. I thought it is a “best practice” to access static methods from the declaring class only (the Eclipse compiler even has a setting to enforce this.) I honestly didn’t see the point of subclassing Assertions. I thought that if somebody needed to extend it, he or she could use composition instead.
  2. Classes containing actual assertion methods are also final (e.g. StringAssert) due to the lack of self-types in Java, the language. I’ll explain with an example. The assertion methods in StringAssert always return this, to facilitate method chaining

    public StringAssert isNotNull() {
    assertNotNull();
    return this;
    }
    so we can write something like

    assertThat(someString).isNotNull()
    .isEqualTo("Hello World");

    If we need to subclass StringAssert, we need to override its methods to return the subtype, otherwise, when chaining methods, we will not see the new methods in the new subclass

    @Override public MyStringAssert isNotNull() {
    super.isNotNull();
    return this;
    }

     

    Once again, I thought that by using composition users may have better chances of not falling into this trap.

Szczepan responded:

Hmmmm, I see point now. I would still recommend to un-finalize the assert classes… Internally, you can probably figure out some way of verifying if you didn’t forget to override (like extensive functional test suite, PMD rule or JUnit test that reflects the methods, etc.) Externally, I’d like to have an easy way to extend assertions on already handled types. Extension via custom conditions doesn’t work for me because it’s too Hamcresty and I’m doing FEST here, right? :)

Szczepan provides a use case for FEST’s API I never thought about before. Not only that, he also provides a pragmatic solution to the “problem” I was afraid of!

Soon after reading his response I realized I’ve been over-worrying about users not following certain “good practice” or forgetting to return the correct type. The worst-case scenarios are not as bad as I thought, and users of FEST’s API can easily recover from a mistake. I realized I’ve been limiting extensibility and flexibility of the API by over-protecting users.

Lesson #2: Too many options may cause confusion

In the same thread, Szczepan suggests to add the methods is and has as “humanized” aliases for satisfies, to make the API more compact and readable. The following example is taken from FEST’s wiki:

assertThat("hello").as("Greeting").satisfies(isUppercase());
// using the "is" alias:
assertThat("hello").as("Greeting").is(uppercase());
Although it makes a lot of sense (and I actually filed tasks to have this aliases in our next release,) Ansgar Konermann, another FEST user, brings up a very interesting point:

I like FEST assertions for its simple API. I think it is a significant plus to attract new users. We should take care not to complicate the API unnecessarily. When I was new to the FEST API, I even considered it strange to have both as() and describedAs(), which do exactly the same thing. I later understood this is a technical necessity to allow usage of FEST in Groovy. I’d love to see FEST keep its API as simple as possible.

Think about http://c2.com/xp/OnceAndOnlyOnce.html

I’d agree that satisfies/doesNotSatisfy sounds like math, not like a check of domain conditions. Nevertheless, I feel that this basic concept of a condition/predicate is absolutely valid to use when defining tests. We also kind of agree that assertThat is fine for all tests, however your business analyst would probably express this a bit different (e.g. makeSureThat, itMustAlwaysBeTheCase). If someone asked me, I’d opt not to include each and every variant which a natural language might have developed for the same meaning over time, but to convey this meaning using exactly one, carefully chosen word.

Ansgar has a valid point. Aliases can add more flexibility to an API, but it also may increase confusion to its users. Finally he adds:

In our team, the expectation of FEST’s API is: type assertThat(actual), press CTRL+Space and instantly _know_ which single method to call for the check you want to perform. No further thinking about which method to choose. Given a test intention, it should be totally obvious which method to call. Aliasing makes this harder. Don’t make me think – at least not about the methods which I need to call. When writing tests, I instead want to focus my thinking on the conditions which should be checked.

Pretty clear message. Nothing else to add.

In Conclusion

What I like the most about these two lessons I learned is that they seem to pull in opposite directions. Finding the balance between these two may help create an API that is both flexible, extensible and at the same time, does not offer too many choices that may cause confusion.

As I mentioned earlier, working on open source is a great experience. It helps us stay humble, since we are always going to find somebody with better ideas than ours.

Thanks Szczepan and Ansgar!

From http://alexruiz.developerblogs.com/

Published at DZone with permission of Alex Ruiz, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Mast Ermnd replied on Tue, 2009/07/14 - 12:31pm

Great article, but I always fall into trap #1 about overprotection of users. Would you care to elaborate on the part where you say "Not only that, he also provides a pragmatic solution to the “problem” I was afraid of!".

Thanks!

Alex Ruiz replied on Tue, 2009/07/14 - 2:10pm in response to: Mast Ermnd

I was referring to the PMD checking and/or tests to verify that the correct type is returned from the assertion methods.

Cheers,
-Alex

Jakob Jenkov replied on Wed, 2009/07/15 - 4:02am

I agree with #1, but I am not sure I agree with #2

Yes, too many options can be confusing, but it depends on who you are developing the API for. A noob would perhaps be confused by too many choices, but as the noob becomes a veteran, she may learn to cherish the many options. What's confusing today is simple tomorrow. I'd rather  stick to #1 and go with more flexibility, and just document my way out of the many options.

Alex Ruiz replied on Wed, 2009/07/15 - 12:03pm in response to: Jakob Jenkov

Hi Jakob,

I completely agree with you. I didn't mean that we should avoid aliases or options, but to try to find a balance, depending on the target audience. What I learned from this exercise is to make design decisions having these two lessons in mind :)

Cheers,

-Alex

 

Jakob Jenkov replied on Thu, 2009/07/16 - 1:16am

Hi Alex,

 Yes, I kinda got that from your posts too. That you are not following these rules as cut-in-stone religious commandments, but rather as guidelines.

 

BTW I wrote my own API design guidelines down in this 12 text series:

http://tutorials.jenkov.com/api-design/index.html

 

That series contains a lot of my experiences gained during 5-6 years of open source development of Butterfly Components among other API's. 

Bruno Vernay replied on Thu, 2009/07/16 - 10:01am

Both links are broken.

Alex Ruiz replied on Thu, 2009/07/16 - 12:51pm

Thanks Jakob :)

BTW, the links you shared don't work :(

Cheers!
-Alex

Jakob Jenkov replied on Thu, 2009/07/16 - 1:11pm

My web server had crashed. It's back up now, so the links work again.

Bruno Vernay replied on Thu, 2009/07/23 - 4:09am in response to: Jakob Jenkov

I read your API Guide and the exception tutorial: good works.

Maybe you could host it on more "social/wiki" place: this would allow comments, more robust service and maybe more printer friendly format.

 Google has plenty of services: blogspot, Sites,Knol ...

 By the way you could add a reference section (like you did for the Exception tutorial). Feel free to get a starting point from my own.

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.