but all of these key-value stores implement different features and functionality, and what's easy in one is next to impossible in another. Why would you limit yourself to just using the common subset of each?
like always, software development is tradeoffs. For this product, where I have a hosted thing, which uses riak (which has a very limited set of functionality), but may eventually offer an enterprise thing that uses a traditional sql database (just as a k/v store though). Furthermore, the ability to compose e.g. caching on top of storage has been super useful
if your needs are simple, why reach for a higher layer of abstraction, rather than just rewrite the simple layer in the first place? You will quickly find that your abstraction library doesn't handle everything you were thinking of post-migration anyway.
That doesn't help if I want to support both kinds of installs using the same codebase. I'm pretty confident with this set of abstractions at this point - I've implemented maybe 15 different storage backends or storage wrappers, most of which were super easy, and I've never changed app code based on storage changing out from underneath it.