It sounds like the original poster has already effectively, but informally deprecated their API (anything that is referred to as 'old API'). However, until it is announced and users are notified that an API is deprecated, it is not formally deprecated.
Deprecated API is an interim, inactive stage of code. It is the last rites. This is the period that allows adopters/consumers to reconfigure their apps for a newer API and bid a fond farewell, making peace with the API. Some APIs may linger longer than others, but at this point we know their time isn't long.
Deleted API is a code funeral. There is nothing more that it can do, but properly disposed and appropriately memorialized.
Many API and service developers opt for code funerals rather than performing last rites; however, I think that is somewhat risky. If there was any kind of service or support promise made when the API/service was either initially adopted or through renewal, you may want want to honor that committment for a reasonable period of time before performing the funeral.
For non service libraries, I think one major release version, regardless of the period of time, is probably a more than acceptable and fair period of guaranteed backwards compatibility. Beyond that it depends on the influence and lobbying by users to extend it's life beyond that period. And don't be suprised if from time to time there are objections due to irreplacable 3rd party dependancies being stuck in limbo, and tied to certain versions of certain platforms.
For services, I suspect you might want to look at either a six month or year period, simply because of the variance in by who and by how a service can be consumed, and the corresponding development cycle variance from consuming project to consuming project -- many projects that might be consuming your service could still big up-front design, and may schedule a release cycle of longer than a year. Most developer opinions from the outside would suggest that those with long schedules are responsible for meeting your cycle times, and those long cycle consuming projects should adopt a quicker release cycle, and it may be true. But ultimately the date of deletion is something that you have to negotiate with users.
A good but not bulletproof strategy for deprecation might be when annoucing deprecation, highlight the timeframe for intent to delete, along with a request for comment or objection in a survey format of the API sections in question. If you don't have a contact list of users because your service operates with [semi] anonymous access, you might consider looking at logs for frequent and active users and dispatch the notification to the host or domain administrator to forward as they see fit.
It would make more sense to me to organize the API around resources and use the verbs (GET, PUT, POST, DELETE) that come with the http protocol, in short: Make it more RESTful, that is surely considered good practice.
This is a good succinct talk on API design that also analyzes popular APIs such as facebook's or twitter's.
I am lost on the difference between your /getX
and /getXForId
so I can't really give you an example of how I would change your API.
Edit
I tried to model the API in Sinatra. It is pretty self explanatory and straight forward. (I saw you are using axis but it's been a while since I did serious Java). If you are serious about RESTful then you probably want to drop the approach with the four ids. However the approach works:
require "sinatra"
require "sinatra/reloader"
def all_ids_given?(ids_given, ids_needed)
ids_needed.reduce(true){|memo, id| memo &= ids_given.include? id}
end
#Your old /getXForId
get '/X/:combined_id' do |id|
"Getting X with combined id #{id}\r\n"
end
#Your old /createX
post '/X' do
post_params = request.body.read
"Creating a new X with #{post_params} \r\n"
end
#Your old /updateXForId
put '/X/:combined_id' do |id|
put_params = request.body.read
"Updating X with combined id #{id}: New values are #{put_params}\r\n"
end
#Your old /removeXForId
delete '/X/:combined_id' do |id|
"Deleting X with combined id #{id}\r\n"
end
#Your old /getX
get '/X' do
if all_ids_given?(params.keys, ["id1","id2","id3","id4"])
"Getting X with the ids #{params}\r\n"
else
"Error: Need arguments for id1, id2, id3 and id4\r\n"
end
end
#Your old /updateX
put '/X' do
if all_ids_given?(params.keys, ["id1","id2","id3","id4"])
put_params = request.body.read
"Updating X with the ids #{params}: New values are #{put_params}\r\n"
else
"Error: Need arguments for id1, id2, id3 and id4\r\n"
end
end
#Your old /removeX
delete '/X' do
if all_ids_given?(params.keys, ["id1","id2","id3","id4"])
"Deleting X with combined id #{params}\r\n"
else
"Error: Need arguments for id1, id2, id3 and id4\r\n"
end
end
#Extensibility
get '/X/:combined_id/Y' do |id|
"Getting all Ys for the X with combined id #{id}\r\n"
end
Testing the API with curl:
base_url="http://localhost:4567/"
echo "Getting a X with a combined id"
echo -n "-> "; curl ${base_url}X/id1234
echo "Creating a new X with a list of properties"
echo -n "-> "; curl --data "property_1=test" ${base_url}X
echo "Updating an existing X with a combined id"
echo -n "-> "; curl -X PUT --data "property_1=test&property_2=another_test" ${base_url}X/id1234
echo "Deleting an existing X with a combined id"
echo -n "-> "; curl -X DELETE ${base_url}X/id1234
echo "Getting a X with 4 ids"
echo -n "-> "; curl ${base_url}"X?id1=1&id2=2&id3=3&id4=4"
echo "Getting a X with 4 ids, but supplying only 3"
echo -n "-> "; curl ${base_url}"X?id1=1&id3=3&id4=4"
echo "Updating a X with 4 ids"
echo -n "-> "; curl -X PUT --data "property_1=test&property_2=another_test" ${base_url}"X?id1=1&id2=2&id3=3&id4=4"
echo "Updating a X with 4 ids"
echo -n "-> "; curl -X DELETE ${base_url}"X?id1=1&id2=2&id3=3&id4=4"
echo "\r\nTesting the extensibility"
echo "Getting all Ys for a single X (Unrelated example: /post/2/comments would give all comments to a single post with the id 2)"
echo -n "-> "; curl ${base_url}X/id1234/Y
echo "This won't work for the 4 ids"
echo -n "-> "; curl ${base_url}"X?id1=1&id2=2&id3=3&id4=4/Y"
The script returns the following:
Getting a X with a combined id
-> Getting X with combined id id1234
Creating a new X with a list of properties
-> Creating a new X with property_1=test
Updating an existing X with a combined id
-> Updating X with combined id id1234: New values are property_1=test&property_2=another_test
Deleting an existing X with a combined id
-> Deleting X with combined id id1234
Getting a X with 4 ids
-> Getting X with the ids {"id1"=>"1", "id2"=>"2", "id3"=>"3", "id4"=>"4"}
Getting a X with 4 ids, but supplying only 3
-> Error: Need arguments for id1, id2, id3 and id4
Updating a X with 4 ids
-> Updating X with the ids {"id1"=>"1", "id2"=>"2", "id3"=>"3", "id4"=>"4", "property_1"=>"test", "property_2"=>"another_test"}: New values are property_1=test&property_2=another_test
Updating a X with 4 ids
-> Deleting X with combined id {"id1"=>"1", "id2"=>"2", "id3"=>"3", "id4"=>"4"}
Testing the extensibility
Getting all Ys for a single X (Unrelated example: /post/2/comments would give all comments to a single post with the id 2)
-> Getting all Ys for the X with combined id id1234
This won't work for the 4 ids
-> Getting X with the ids {"id1"=>"1", "id2"=>"2", "id3"=>"3", "id4"=>"4/Y"}
What you can see is that nesting resources and the four parameter approach does not work well together. Maybe one could model it differently and do something like /X/:id1/:id2/:id3/:id4
.
Best Answer
It depends on how many fields you're talking about, and how they are used. Concrete is preferable for highly structured queries with only a few fields, but if the querying tends to be very free form, then the concrete approach quickly becomes unwieldy with more than three or four fields.
On the other hand, it's very difficult to keep a generic API pure. If you do a simple name search in a lot of places, eventually someone is going to tire of repeating the same five lines of code, and they're going to wrap it in a function. Such an API invariably evolves into a hybrid of a generic query together with concrete wrappers for the most commonly used queries. And I don't see anything wrong with that. It gives you the best of both worlds.