Indeed. The problem is trying to apply the principles of "Clean Code" or more generally of OOP everywhere. There are surely cases where having an interface as an abstraction and multiple implementations makes sense. There are case where it doesn't, and if you take as a dogma "everything shall be the implementation of an interface" you get more complex code and performance penalty for nothing.
There is nothing wrong with having procedural code with a switch case, as there is nothing wrong in having global variables, in having even goto, depends on how you use it.
The principle of clean code includes KISS, therefore complex code for nothing isn't clean code
> There are surely cases where having an interface as an abstraction and multiple implementations makes sense.
A tried and true way solve problems is by adding more layers of indirection, starting with an interface makes it trivial to swap things out. I just did a rewrite of some old sound tool that was hard coded to OSS and Alsa. Now i wanted Pulse and Pipewire, this ended up requiring basically a rewrite because there was a lack of a good interface and assumptions everywhere. Instead now I have some good interfaces and adding whatever the next Linux audio stack comes in - it likely won't be a problem.
> There are surely cases where having an interface as an abstraction and multiple implementations makes sense.
I think most people aren't aware of the alternative, which is: A function that can call different implementations based on some other variable.
E.g. instead of having RealDB and MockDB type have a createUser() (method), you have a createUser() (function) that switches part of it's logic based on what DB is selected.
That's the prodecural way of achieving the same thing without needing a concept for virtual functions.
Casey explains this in the long discussion with Uncle Bob.