6

In my opinion, one of the hallmarks of a good API is that it is self-consistent. It establishes easy-to-remember conventions and then follows them.

As I'm trying to adopt the 2.0 API, I'm finding a whole bunch of inconsistencies in the API that are making it really frustrating to work with. Here are some of the things that are really bothering me:

  • The value for the sort query parameter should always correspond to a field in the object of the returned JSON. It usually doesn't. For example, if I want to sort badges by their badge_type, I have to pass in sort=type. It should be sort=badge_type. (another example, for tags I do sort=popular even though it really should be sort=count)

  • I should not be able to sort by something that does not correspond to a field in the object returned. For example, I can sort Tags by "activity" (sort=activity), but there's nothing in the returned Tag objects that indicates what that activity is. If we really want to be able to sort Tags by the time they were last used, then Tags should have a last_used_date field that contains the timestamp of the last time the tag was added to a Question, and then I should do sort=last_used_date)

  • I find it really annoying that some object fields don't make sense in all contexts. For example, the Badge user field is only relevant if I'm requesting awarded badges (i.e. via /badges/recipients or /badges/{ids}/recipients). If the field is not always relevant, it should not be there. Instead, the */recipients endpoints should return "Awarded Badge" objects that have award_count, user, and badge fields.

  • Tags, Badges, and Users all have name (or display_name) fields. So why can't I do inname=foo when requesting Badges? If you're going to offer searching by name on some things, you should offer searching by name on all things.

There are more, but these are ones that I remember right now. As I come across more I'll also post those.

2
  • 1
    +1 for the anti popular/activity, they are too ambiguous. Although award_count should be on all badges, as when not associated with a user it means the number of times the badge has been awarded site-wide. (separate fields, e.g. total_award_count and user_award_count, might be better)
    – Jonathan.
    Commented Jan 5, 2012 at 0:03
  • 1
    @Jonathan. good point on award_count, but I definitely think total_award_count would be a better name. Making the semantic meaning of a field dependent on the context is a bad idea. Badges, IMO, shouldn't contain any user-specific information, because there's nothing inherent about a badge that has to do with a User. Commented Jan 5, 2012 at 0:29

1 Answer 1

0

Hmm, I agree with a bit of this... but not all of it.

sorts descend conceptually from the sorts exposed on the sites, you'll note this most strongly from the question sorts (hot, week, month, activity, and votes). popular on tags, is another example of something that came more or less directly from the site. It would be wrong to not expose sorts like hot in the API, but there really isn't a meaningful field to be returned there (internally it's a completely opaque decimal number).

Related, I disagree with the idea of sorts mapping to fields explicitly. This creates the false expectation (that would be essentially impossible to meet, for performance reasons) that you can sort on all returned fields. For overlapping conceptual sorts (which often have overlapping field names), like "when was this object created", we do share sort names (in the example case, creation). I will, however, look into adding tag.last_activity_date.

The only example of a field I can think of off hand that's never set is badge.user when fetched from /badges, /badges/{ids}, /badges/name, or /badges/tags (the other /badges/* and */badges methods can and generally do set the user field). I can see where you're coming from, it is a little odd. However, I think the cure is worse than the disease. We'd have a type that differs from another by a single field (which means some annoying boiler plate in statically typed consumers) and we'd have split the concept of a badge in two (which would mean more configuration when building a filter that's concerned with the overarching concept of a badge). It's hardly like this is the only case where a field is almost always not set, consider timed_penalty_date on /me for example*. All things considered, I believe the cure for this blemish is worse than the disease.

As something of an aside, we don't expose lots of text searching** because it's expensive. This is why, for example, /search exists rather than having intitle and nottagged on /questions (and also why it returns much more heavily cached data, and less of it at that). the descent of inname parameters is also from the sites, you'll notice that we have search boxes on Users and Tags, but not Badges. All that being said, I'll looking into what adding that parameter would do to performance; I suspect it can be added and may be of some use (at least, in the tag-based badge case).

*It being rather hard to authenticate when your account is suspended, rarity of suspension not withstanding.

**Note that some tag searching tasks devolve to text search or equivalent performance wise.


tag.last_activity_date and inname on various badges methods have been added as a consquence of this discussion.

2
  • 3
    (Comments in order) I'm willing to let things like hot, week, and month slide as sort values on questions, because there's no way for clients to duplicate the algorithm used to determine the "hotness" of a question. But I maintain that consuming the API is not the same thing as using the site. The API is for us developers, and thus it should be consistent. If I'm sorting by the score of a question, then don't call it votes, call it score. If I'm sorting by when an object was creation, don't call it creation, call it creation_date. The sort key should match the object field. Commented Jan 5, 2012 at 4:54
  • 3
    Badge and User have a many-to-many relationship. Proper decomposition mandates that the way to model that correctly with a -to-one relationship on one side (which is what JSON can handle) is with another object, the Awarded Badge. I understand that text searching is expensive. It's just frustrating to think "ok, this had inname, that had inname, this should to. Wait, it doesn't? wtf?". Commented Jan 5, 2012 at 4:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .