I think the major problem is that base classes are really two different interfaces (public and protected) combined with a default implementation, all exposed as a public symbol that anyone can reference.

So if you have a Vehicle base class with Car and Truck that inherit from it people will naturally externally do things like pass around List<Vehicle> and will extend functionality with Lorry : Vehicle and start using it. This creates a problem because you cannot change the base class of a Car or else it will no longer fit in a List<Vehicle> but if you modify Vehicle you may break people who inherit from Vehicle.

If you wrote it out explicitly without using inheritence though you'd have something a bit more sane and split up into the three different concerns:

class Car : IPublicVehicle {
IProtectedVehicle _vehicle = new Vehicle();

public void Drive() {
_vehicle.Drive()
}
}

Now you have a public interface where you'd create List<IPublicVehicle>'s and toss them around, but you would be free to write a VehicleV2 class that could be injected into _vehicle (you could even do it dynamically and go nuts with dependency injection). Then other people using Vehicle() wouldn't have their behavior changed and everyone would still live happily inside of List<IPublicVehicles>'s next to each other.

At the same time by having to write down the interface IProtectedVehicle (not shown) you'd be more likely to do actual design about the interfaces between your subclasses and your base class. This is the real problem which is that people do lazy shit design over the base class interface and just use it to shove shared code into the base and don't consider it a private interface and then go and break behavior as they mess around with more crap in there. And the "DRY" principle pushes software devs to ALWAYS push shit into their base classes, without even thinking beyond "because DRY", which is guaranteed to be bad design. With good design you may have to tolerate some level of necessary repetition in your base classes. Then you wind up realizing that you need derived classes that do not share behavior (e.g. consider adding a Boat subclass of Vehicle when previously you had taken the SinksInWater() implementation of Car(), Truck() and Lorry() and pushed that into the Vehicle() baseclass. Oops. Now your boats all SinksInWater() unless you override that. And if you override it you may be heading down the road of violating Liskov.

So that's the source of the objection that OO programmers will just tell you to write interfaces everywhere for loose coupling and only depend on interfaces not types (not abstract/virtual base classes).

But so far I don't know what Go or rust programmers do that is any different. And Go has a really fairly slick system for delegating interfaces to contained structs by composition which is just the same model I outlined above but where the two interfaces are the same. I'm still searching for where other programming languages produce a fundamentally better model. And as far as I've been able to ascertain Go's model is to just replace inheritance with interfaces and delegation -- which you can do in any OO model just by not using inheritance and writing interfaces and delegating. But then the complaint against OO is that proponents will just tell you to write interfaces, and for some reason that is bad. IDK if I'm missing something here...