Building a Robust Service - PHP Web Services (2013)

PHP Web Services (2013)

Chapter 11. Building a Robust Service

A robust service is one that feels secure and reliable to its users. Something that behaves unpredictably, sometimes gives incorrect results, and occasionally doesn’t respond at all, is not what a consumer wants to integrate into her own applications. This chapter will look at what makes a robust service, and some techniques for making services as reliable and useful as they can be, both when things are going well and when they are not.

The best services exhibit consistent, predictable behaviors. This approach of having as much “sameness” as possible works well for consumers, who start to feel at home. As they use the service, they become familiar with how it will work, and will be able to find their way around and deal with any errors they encounter more easily. Most importantly, those consumers will be able to achieve their goals, which should give both consumer and provider a warm, fuzzy feeling.

Consistency Is Key

As PHP developers, we know only too well how difficult it is to use an interface that is inconsistent. The number of manual entries that use the words “needle” and “haystack” with very little correlation between which one should come first in any given situation (and one function where they can be passed in either order!) is our reminder of how painful this can be!

In our own applications, we can do better, but it is important to pay attention to the bigger picture and the existing elements of an API while working on building more features. In particular, consideration should be given to how things are named, how the parameters are passed in and returned, and what the expected behavior should be when something unexpected happens.

Consistent and Meaningful Naming

I recently worked with a system that had a function in it called isSiteAdmin(). Guess what it returned? Wrong! It actually returned the username of the current user, or false. There are plenty of examples of badly-named functions in the world, but please protect us from having any more to add to the list. Function names should be meaningful, and they should also be alike. So if there is something called getCategories() available, try to avoid adding a function called fetchPosts() or getAllTags() unless there’s a good reason for the differences. Instead, fit in with the existing convention and call the functions getPosts() and getTags().

The same applies to RESTful services, as well as those that contain function names, although it is slightly less of an issue when the clients are following hypermedia links. Look out for consistency in whether collection names are plural or not, for example.

NOTE

Case-sensitive or not, make sure your service is absolutely case-consistent throughout.

The naming of parameters is also an area full of traps that are all too easy to fall into—and will annoy your users forever (or at least until you figure out how to release the next version without breaking their existing applications). The way that you name your parameters can give users a clue as to what they should be passing in. For example, a parameter called user is rather ambiguous but either user_id or username would help the user to send more accurate data through to your API.

Naming your parameters with “Hungarian notation” is probably a step too far, but aiming more at the verbose than the terse is probably in everyone’s interests. If there’s a field called desc then people will probably guess the correct meaning of the abbreviation from the context, but it is clearer to call the parameter description or descending or whatever it really means.

Common Validation Rules

The benefits of consistency were discussed already, but it is very easy to end up with slightly different validation rules for similar parameters in different settings; for example, whether extra address lines are optional or required between shipping and billing addresses. Also, try to avoid the irritatingly common situation of allowing a particular format of date/time information or telephone number in one place in your API, but not in another.

Make sure that incoming data is validated in the same way for the same kinds of data every time. An easy way to do this is to always use functionality that is built in, such as whatever your framework offers, or the fabulous Filter extension in PHP. Alternatively, and for types that are specific to your application, you can create a utility class that holds all the validations. In this way, you can add functions that check for particular kinds of data, and then reuse them across your application to ensure consistency.

Just like with the “needle” and “haystack” problems that are found in PHP, parameter ordering is important for RPC services. Figure out a plan to keep your service looking the same everywhere; does the API key need to be the first parameter, for example? Often it isn’t obvious which parameters should be in which order, and in those cases it is best to simply pick something and then stick to it.

Predictable Structures

Structure of data is a key characteristic of a service, and a good API design will have it in mind when accepting requests, building responses, and also in the event of any error. APIs that return an array of results should always return an array of results. If there’s one result, it still needs to be in an array. If there are no results, an empty array should convey this information. Suddenly returning false, or showing an item one level up from where it usually lives, is confusing; so take care to avoid it.

In most situations, the order in which parameters are provided, either as URL parameters or as part of body content, should not matter. Whether the parameter names or their values are case-sensitive can be made clear in the documentation; it is a challenge to keep these small details correct, particularly across a large API, but it is key and does greatly improve your system.

If an error should occur, it may well be the fault of the user. That said, the API ideally should help the user understand what went wrong and how the user can be better in their use of the API (because otherwise they will log a support ticket that you will have to fix). The entire next chapter is devoted to error handling, but at this point it seems important to mention that error responses should always be in a format, that is consistent across the API. If a user sees not-success in the status code that is returned with his response, he should immediately know how to get the information he needs about what went wrong, in a predictable format.

Predictability isn’t just about data formats. Take care to follow patterns throughout an API regarding what happens when something is created, deleted, or not found.

Making Design Decisions for Robustness

Robustness is basically a measure of reassurance; how does the API behave both in good and bad situations? It can be tricky to know which design patterns are the best ones to follow, especially if you are new to APIs. In that situation, good advice would be to stick to the existing standards. These are well-known and understood, and will make it easier for people to integrate with your API or web service. Writing great documentation (see Chapter 13) is key to creating a great API; in general, anything without documentation will not be a good experience for anyone using it. Finally, always consider what should happen in the event that something goes wrong. Keep reading, as the next chapter is all about how to handle errors.