"Code against interfaces." Generally speaking that's a good idea, but let's be clear about one thing. But there's a problem with that sentence. What does interface mean? There are a couple of options
It could be the dictionary definition of an interface, which is "a point where two systems, subjects, organizations, etc. meet and interact" or, in computer terms, the public API. It could be a class, a service, a website, or something else. That means every C++/Java/<insert language> class is an interface. And that's certainly not what the intent is.
Or, it could mean the keyword interface, but C++ doesn't have an *interface* (unless you're talking about the MSVC extension). So that's probably not what interface means either.
What it really means is code against the abstraction of the model. The platonic ideal as it were. The things that boil a model down to its essence. The capabilities of a thing, or it's verbs.
Consider a logger. It has one verb, write. It takes a LogLevel and a String, and puts them somewhere for later access. When you're using a logger that's all you care about. You might write some convenience functions to help hide the definition of level, or to build the string from a template, but that's about it. And that's what you code against. In C++ it's an abstract class with only
pure virtual functions. In Java its an
interface. In Golang it's a type of
Constructing one can be as specific and complicated as you need, or even provided via dependency injection, but when used you only know/care about the abstraction.
So, you're already coding against interfaces. In fact you can't help but do that. What you want to do is code against abstractions.