About 5 years ago I was tasked to write a web service that allowed a single client of ours to send us bills and have them returned back with discounts applied in "real-time." At the time I had no idea what I was doing. Really, I was out of school a year at most and I was just doing something that I thought was cool. I never really debated what the API should look like. All I was concerned with was making the damn thing work.
I did manage to get something working, but by then the damage had been done. I created what I consider the worst looking API known to man. I'm exaggerating a bit; however, I really wish I had done some things differently. Today there are several clients using the very same API I had intended to be used by a single entity. Here's what I learned NOT to do.
Don't let your client's nomenclature creep into your API.
Sometimes this is a hard thing to distinguish. Everyone has their own way of describing things. Some of that terminology is industry specific and SHOULD be used in your API naming convention. Anything that's not industry specific jargon should follow your internal terminology.
Letting the original client's idea of a "site" into our API made sense at the time since this was the only client at the time. They have multiple locations from which bills can be submitted. Now that we have multiple clients using this, I'm finding that this element is rather confusing for most new consumers of our API.
Be consistent with how you name everything.
The names of every property, method, and class are the first thing that your client sees. Make sure it makes sense.
I made the mistake of using different terminology between the detail and master record level. In the master I had a field named "totalbilledcharges" that sums the detail field "linecharge". Today I would have omitted the bill level return value entirely. If it was a requirement to have both I would have named the master field "TotalCharges" and the detail level field "Charge".
You can add buckets, but you can't take them away.
The best thing you can do when defining an API is to accept the least amount of information possible to get the job done. I say this because it gives you less opportunity to screw it up before you even need it. If something is ever needed in the future, you can always add it. Once it's there, it's there forever. Taking something away now would be such a daunting task. I would have to approach each client and find out if the item in question was being used and how removing/changing it would impact their processes. People get pissed when something that was working suddenly breaks.
Even something as simple as changing the type might have some serious side effects. Say you had a status field that you defined as an integer. Changing that to a string would break the hell out of your client's code that was trying to convert a new status "ERROR" into an number. It just won't work. That's not to say that you should make everything a string, just make sure you plan ahead.
Everything going forward has to be opt-in.
You can't exactly tell a paying client that they have to change their code to accommodate the new "Super Foo" feature that you are adding to make something work for a different client. The client has other things to do than accommodate your desires to add a field that they don't need or care about. They've made the project work and have moved on to other ventures. Everyone has budgets for money and time that you have to be considerate of. I guarantee no one has budgeted to fix stuff you've broken because you felt like it.
Let's say you are adding a flag to turn off some default behavior. Being aware of default values will help you out here. If I know that a boolean defaults to false, then I can make this opt-out have a negative verb-age. "IgnoreBar" by default will be false which means that we'll use Bar feature unless someone goes out of their way to mark this true. That was the behavior all along and nothing will change for existing clients.
Beware of Bolt-Ons
A danger with being able to add but not take away is incrementallty adding elements which solve an immediate problem but don't take care of the larger issue.
One of the uglier parts of our API is surrounding how the client defines the hierarchy of who the bill is being submitted for. At present there is a "client id", "subclient id", "site" and "parent id" all in use in varying capacities by various clients. Everything but the "client id" was a bolt-on. Had we realized early on that there was a business need to define a large hierarchy, we would have just exposed an array of strings to fill in. You know what they say about hindsight...
I think that about covers all of the things I've managed to screw up along the way. I've learned this all the hard way. Maybe you won't have to do the same. 