Man looking taken aback and affronted

3 Simple Techniques to Make APIs Easier to Use and Understand

How many times have you tried to use an API only to find that you had to fill in some ridiculous number of parameters with values that you had no idea about?

If you’ve ever done Windows programming and had to call into some of the Win32 APIs, I’m sure you’ve experienced this pain. Do I even need this parameter? What the heck is this value for?

Even if you aren’t writing an external API, it makes a lot of sense to make it as easy as possible to use your API without having to specify a huge list of parameters and it is also a good idea to restrict the options for those parameters as much as possible.

In this post, I’m going to show you three ways you can take a complicated API and make it much more simple and easy to use.

Our offensive API method

Let’s start off by taking a look at an API method that could use some cleaning up. (Examples are in C#, but the idea applies to many languages.)

offensive 3 Simple Techniques to Make APIs Easier to Use and Understand

Granted, this method signature doesn’t look so bad, but it is not the easiest to use. You have to fill in 4 different parameters and you have to know what customerType is and have to make a decision about the numberOfTries value and what that even means.

It is pretty common to see methods like these throughout a code base. While they seem innocent enough, they end up introducing complexity, because they require the caller to do more work than is necessary to call the method and to understand more of the system than they might need to know.

In this scenario, I’m purposely not explaining what customerType or numberOfTries is, because you, as a developer trying to use this code, wouldn’t know either. You would have to go and figure it out.

Let’s see if we can improve this method signature a little bit.

1. Reducing parameter counts by creating objects

The first thing we can do to make this method signature a little simpler is to reduce the number of parameters it has.

The only problem is, we need all those parameters otherwise we wouldn’t have them, right?

That is probably true, so what can we actually do?

Well, one really common refactoring step–which probably won’t be much of a surprise to you is to reduce parameters by combining them into an object.

(BTW, if you are looking for a classic, but great book on refactoring, check out: Refactoring Improving The Design Of Existing Code by Martin Fowler)

Now, most developers do this the wrong way. They right click and select the refactor option in their IDE and they take all the parameters and make them into a single class that gets passed in.

This is wrong, because it is simply hiding the problem and now requiring the caller of the code to first create an object that has a constructor that takes those same parameters. If you do things this way, you are just kicking the can up the stream.

Instead, you should focus on grouping related parameters together, parameters that would always be right next to each other.

In our case the parameters that make sense would be userName and password. Together those parameters make up a user’s login. So, it makes sense to create a LoginInfo class that would contain those two parameters. We might even find that class has some additional functionality of its own or contains more data. But, for now we can simply refactor to make this method signature simpler.

It now requires more code to call our method, but the code is simplified and easier to understand. When someone tries to call the method and they see they need a LoginInfo object, they can very easily see that the LoginInfo object requires a userName and password and it is clear what the purpose of it is.

We’ve only slightly reduced the complexity by reducing the parameter count by one, but we’ve made the API more purposeful and clear.

2. Using enumerations to limit choices

We aren’t done yet though. We still can do quite a bit more to improve this simple API.

The next big issue with the API is that the customerType parameter is very vague. We can’t immediately deduce what it means and what value should be put in there. Is there a restricted list of values? Is it free form? Does it even matter? Or can I just put a blank string there?

We can solve this issue by restricting the possible values using an enumeration. In this example, we’ll assume the customerType can be one of 3 different values. We were requiring the caller to type the string representation of those values manually–and not make a mistake–but, we can improve the experience–and reduce the error rate–by limiting the choices to just what is valid.

Again, a fairly simple change, but a powerful one, because it really has reduced the complexity for the caller. Now, the caller doesn’t have to figure out what should go in that string parameter for customerType and can instead select from one of three possible options. By making this change, we’ve also enabled the caller to utilize the auto-complete feature of the IDE to assist in making a choice and we’ve greatly reduced the chance of error from a typo, since we are now validating the values at compile time instead of run time.

For this reason, anytime you can restrict choices for a parameter in a method, you should. Enumerations are the easiest way, but you can utilize other techniques as well.

For example, suppose you had a method that took a parameter maximumAge. You could make this an int, but, in most cases you would be better served by creating an Age class that had its own validation to make sure the actual age was an integer between 0 and say, 130. You wouldn’t catch errors at compile time, but you’d be greatly restricting the possible values and you’d make the intent of the parameter even more clear through the name of its type.

3. Using default values to reduce required parameters

In many cases there are reasonable defaults that can be provided for method parameters in an API. Whenever possible, it makes sense to use default values and optional parameters to further reduce complexity and only require the caller of an API to worry about what is important to them.

If your API is mostly used in one way or there is a reasonable default, provide the default and make the parameters optional. Callers who need extra functionality can always override the defaults.

In our example, we can get rid of the need to specify a customerType and the numberOfTries by providing a reasonable default for both.

Now, calling our method is dead simple for most cases. We can just provide the loginInfo and if we need more control, we can override the customerType or the numberOfTries.

It’s not rocket science

What we did here was pretty simple, but don’t let the simplicity fool you. We have made small–and arguably obvious–changes, but we’ve greatly reduced the mental burden for the caller of this method.

happy 3 Simple Techniques to Make APIs Easier to Use and Understand

We would see a much larger cumulative benefit from applying these techniques to an entire API or code base. When you start restricting choices and grouping related data into objects instead of raw parameters, you end up with a synergistic effect in your code, because you are able to know more about the data that is being passed around.

For example, changing our customerType to an enumeration made it easier to call the method, but it also makes our logic inside the method simpler, because we don’t have to check for invalid values. Anytime we can rely on data being within certain bounds, we can simplify the logic for handling that data.

If you found these tips useful…

…sign up to never miss a post again.

I’ll deliver you all the Simple Programmer posts to your email every week and include some additional content that I don’t publish on the site. Over 4000 developers have already subscribed.

 

  • john nystrom

    I have been looking for a good way to expose possible enum values to a client, but have not found a great way. Not giving the client the knowledge of what the possible values are, and taking in an integer for this case seems dirty to me. Let me know what your ideas are around this. Thanks

    • jsonmez

      I would just expose the enum values as public so they have access to them. I suppose if this API were over the web though, you’d have a little more difficulty. I don’t think there is going to be a better way than just giving them the enum values.

      • john nystrom

        Now by “as public” you mean just expose the values via help docs, I assume?

        • jsonmez

          Yeah. Or if they are writing code directly against your API, actually making the type pubic.

          • john nystrom

            We are talking about HTTP services, right? Sending back JSON or XML? If so what do you mean by “make the type public”?

          • jsonmez

            I’m not necessarily talking about HTTP. This article could apply to an API over HTTP, but in general it is targeted at an API that someone would write code directly against and compile against.

          • john nystrom

            Gotcha..thanks.

  • Brandon

    Hmm…It would be interesting to see how the customer type enum is used throughout the rest of the code base. Any time you’re making decisions in code based on “type” enums like this, consider using actual types (polymorphism) instead.

    • jsonmez

      That’s a good point. I would depend on how the logic is handled and how many different attributes were passed in about a customer. In this case, it is pretty easy to argue that the customerType is the main attribute of the customer, so it could be a class.

    • http://www.coder-mike.com Michael Hunter

      I think the choice between enums and polymorphism should be decided based on how the code is expected to expand and change in future. Using enums makes the code closed to adding new options (enum values) without breaking open all the methods that switch on the enum, but open to adding new methods that can operate on all those enum values. Using polymorphism makes the code open to adding new options (new classes), but closed to adding new methods of each class without breaking open all the classes.

  • Pingback: Dew Drop – May 20, 2014 (#1780) | Morning Dew()

  • http://www.rudeshko.com/ Anton Rudeshko

    Hey there, John! Offtopic here.
    Just want to notice that JS-based code snippets are usually having hard time showing in popular readers like feedly or pocket =(
    May be you’ll want to consider to do some kind of fallback. Thanks

    • jsonmez

      This was my first time embedding Gists in the post. I will have to think about how to handle this. Thanks for letting me know though.

  • http://PauloMorgado.NET/ Paulo Morgado

    Are you aware that optional arguments (like static fields) are captured at compile-time and, if you change the defaults in the called code, cannot be changed without compiling the caller code?
    In your example, customer type should be a part of login info.
    If you use optional arguments as part of a public API, you’re asking for trouble. It’s like running with scissors. It might not arm you most of the times, but when it does…

    • jsonmez

      Yes. I still would utilize optional arguments as much as possible, because it greatly simplifies the code. Good point though. Worth thinking about whether those defaults are likely to change the inconvenience of recompliing client code or side effects if it isn’t.
      Customer type could be part of login info. Depends on how it is being used. I left that part out on purpose.

    • David Rael

      overloading to handle the optional arguments would be an alternative that wouldn’t suffer from this problem. sometimes the old way of doing something isn’t just old – it’s better.

      • http://PauloMorgado.NET/ Paulo Morgado

        It’s not old nor better. Ir’s the correct object oriented way. :)

  • David Rael

    There are certainly cases where collapsing parameters into objects makes sense. I don’t think this is one of them. It is intuitive that a login method requires credentials and the parameter names make it clear what is coming in. There is nothing wrong with the method taking those as individual parameters and I think the indirection of having to create another object just makes the code less readable here. In a situation with a more complex object that would have more options and maybe even a fluent builder object for with constructor parameters for required values and methods for setting optional values with reasonable defaults, it would probably make more sense. There’s no question the principle here makes sense, I just don’t think it’s the best example.

  • Pingback: No Structure Services | Form Follows Function()