Distance Debugging Logo

API Taxes

Taxes serve two purposes: the first is to collect revenue, and the second is to help incentivize or disincentivize certain activities. For instance, we (supposedly) keep raising cigarette taxes to encourage people to quit, and we offer mortgage deductions to encourage home ownership. I got to thinking about taxes when tackling some recurring problems in the development of an API intended for broad consumption including:

  • You have methods that perform low-level operations that have to be made available for certain uses, but should generally be avoided.
  • You have methods that have serious performance implications, or serious performance implications depending on the arguments passed in.
  • You have methods that you want to discourage use of for whatever reason; maybe you are thinking about removing it but haven't yet gone to deprecated; maybe you think it is buggy and unreliable for many inputs; maybe there's a better way of doing it that people often overlook.

All these problems boil down to one thing: there's no consistent way to indicate to API users what methods they ought to be calling under what circumstances. You can add comments all day long and people are unlikely to change their behavior. Once they find something that works, they will stick with it, and then complain when performance is terrible or they corrupt a data-structure. So I thought of a simple solution: API taxes. It would be easy enough to implement in Java with annotations and/or aspects. Basically, every method can be assigned a tax (the default being zero) annotation. Methods that you want to encourage the use of have negative taxes, and methods that you want to discourage have positive taxes. The annotation could specify a base cost to call, or different costs for different values of inputs, or even different costs for calling in different contexts (for instance, calling a low-level method from a wrapper API is zero-cost, but calling it directly is high). Then you have a "Tax Collector" aspect that is called for every method annotated with a tax, to keep track of what you owe. As the API designer, I can then give out guidelines for what a reasonable cost is different usage patterns. If you are exceeding that cost, you need to revisit your calling patterns to see where you are incurring that unnecessary cost. Perhaps you are sending in incorrect inputs, or just failing to cache results that should be cached. Overall, besides the added burden of coming up with tax values during API creation, it seems like an easy way to catch problems early on, when you only have small data sets and test cases to work with.