Should idempotency be extended to include return codes?

A couple of weeks ago we had in interesting discussion at work. The topic was what a delete on a RESTful entity should return if an entity does not exist. First we had one camp arguing that if the entity does not exist then a DELETE should return 404 (Not Found) which is similar to what a GET would do in the same case. The upside is that a client knows if something was deleted or not based on the status code. The first argument against this approach was that the DELETE should be idempotent so it should return the same thing as if the entity existed. Especially since this would simplify logic in the client's retry logic. This in turn was countered by pointing out that idempotency applies to the state of the server and not the returned result and that it is good to know if an object was deleted or not. At this point nobody disagreed that it would be good to know if you're interested but we still did not agree on how this information was going to be returned. We read through the relevant RFCs and discussed some more and this is my personal take away from this discussion:

  • When an entity exists and is deleted then 200 (OK) should be returned and the body should either contain the deleted entity or a special status object indicating the object existed before deletion.
  • When an entity does not exist when deleted then 204 (No Content) should be returned or 200 with a status object saying nothing existed.
  • If it is important that an entity is only deleted if nobody have changed it since the client last saw the object, then 409 (Conflict) or 412 (Precondition Failed) should be returned if the DELETE is called with the wrong Etag/If-match header.

Two reasons I think 404 is a bad idea is that your typical managed request object will translate it into an exception while both 200 and 204 are success responses. Also if you look at other APIs, delete operations typically succeed even if the thing you try to delete does not exist since all APIs I'm aware of assume that you want the object gone and as such, if it is already gone you're happy. The use of Etag to make sure you only delete things not modified is similar to what you expect on a PUT operation. Retry logic for a PUT operation is hard since if the first attempt succeeds but the response is lost, the second attempt will return 409 or 412 unless the server can see that the second PUT is identical to the first one.

So back to the question in the title; should idempotency be extended to include return codes? When it comes to the exact response I'd say no, put I definitely think a good RESTful service should return success on multiple identical DELETE or PUT operations since it simplifies the logic of the client and its retry logic. Especially since, in my experience, a client rarely cares if the object existed or not before a DELETE or if it was already updated before a PUT; the client typically only cares about the state after the operation, not what it was before.