Recently I have been developing my own API and with that invested interest in API design I have been keenly interested how I can improve my API design.
One aspect that has come up a couple times is (not by users of my API but in my observing discussion about the topic): one should know just by looking at the code calling the API what it is doing.
For example see this discussion on GitHub for the discourse repo, it goes something like:
foo.update_pinned(true, true);
Just by looking at the code (without knowing the parameter names, documentation etc.) one cannot guess what it is going to do - what does the 2nd argument mean? The suggested improvement is to have something like:
foo.pin()
foo.unpin()
foo.pin_globally()
And that clears things up (the 2nd arg was whether to pin foo globally, I am guessing), and I agree in this case the later would certainly be an improvement.
However I believe there can be instances where methods to set different but logically related state would be better exposed as one method call rather than separate ones, even though you would not know what it is doing just by looking at the code. (So you would have to resort to looking at the parameter names and documentation to find out - which personally I would always do no matter what if I am unfamiliar with an API).
For example I expose one method SetVisibility(bool, string, bool)
on a FalconPeer and I acknowledge just looking at the line:
falconPeer.SetVisibility(true, "aerw3", true);
You would have no idea what it is doing. It is setting 3 different values that control the "visibility" of the falconPeer
in the logical sense: accept join requests, only with password and reply to discovery requests. Splitting this out into 3 method calls could lead to a user of the API to set one aspect of "visibility" forgetting to set others that I force them to think about by only exposing the one method to set all aspects of "visibility". Furthermore when the user wants to change one aspect they almost always will want to change another aspect and can now do so in one call.
update
method:foo.update(pinned=true, globally=true)
. Or:foo.update_pinned(true, globally=true)
. So, the answer to your question should take also the language features into account, because a good API for language X might not be good for language Y and viceversa.setSize(10, 20)
isn't as readable assetSize(width=10, height=20)
orrandom(distribution='gaussian', mean=0.5, deviation=1)
. In languages with enforced named parameters booleans can convey exactly the same amount of information as using enums/named constants, so they can be good in APIs.