It appears to me that many API's of in-house projects just happen. Instead of design meetings to discuss what a subroutine should return in each relevant case (success, failure, error), it's instead left to the developer to pick whatever they think is appropriate at the time. Unfortunately the developer can occasionally get it wrong.
Now I admit that it can be hard work to come up with a good interface. I'm certainly not perfect at it myself. Often I go over old code of mine and cringe at the decisions I made. Usually this happens when I realise that had I written it better, I could probably be reusing some of that code for my latest project.
Some bad user interface issues can be worked around. There's the classic case where you fix (and rename) the original and create a wrapper that has the old behaviour. Or alternately, you break up the original so that you now have a handy subroutine which does the small but important bit you'd otherwise have to duplicate.
Some times the bad user interface issues can't be fixed. Perhaps there's a freeze on the API, or too much code depends on things being exactly the way they are (weird side effects and all). Some times there are "political issues" with such changes and other times the issue is with the required review and re-review process.
Fortunately, as I'm discovering, most of the horrible issues with solving interfaces can be happily avoided at the start, with test cases. Surely you'll test for success, failure and error? That will help you define right then and there what should happen in each case. Test cases are great at providing use cases at the function level. And of course at each level above that.
Now I just have to finish reading Ian and chromatic's book and start using them properly.