I've never seen a "true" OOP project that wasn't quite a mess and couldn't have been written much cleaner in a plain old procedural style:
Use freestanding functions, the most successful abstraction to date.
Stop with that singleton bullshit. Most things that need to be managed exist precisely once in a program (talk to the sound card, to the printer, to the network, to the graphics card, to the file system, allocate memory...). Making classes first and instanciating once (or how many times, how can I know by looking at that handle?) is just plain silly, overly verbose, and confusing to the consumer of the API.
Don't couple allocation and initialization. It's a stupid idea. It leads to pointless, inefficient, and (in some languages) error-prone one-by-one allocations.
Flat fixed structs for data organization. By default expose struct size for massive decrease in memory allocation. Expose almost all fields (except for truly platform / implementation-defined ones) and stop with that silly getter/setter boilerplate.
Mostly use data tables (with direct integer indexing mostly) like in a relational database, for data-driven architectures. (CS/programming technology TODO: How can we switch between AOS/SOA more seamlessly? Maybe we can get inspiration from Graphics APIs?)
Don't use silly flexible-schema XML/object hierarchies to "compensate" for having no idea what's in the data. It doesn't help.
Make interfaces ("vtables" if you will) only sometimes where they are needed, not by default. Don't call this inheritance. Bullshit. It's an interface, not more, not less. If you think interface descriptions must typically be bundled with each data item, think harder. They are independent data.
We don't need no friggin "doer" objects for every simple thing. It doesn't help a bit, but only makes things less readable and more complex. Just do what needs to be done!
- getters/setters everywhere (but not in favour of public fields, which just as much introduce tight coupling, but 'tell don't ask' style which allow to localise functionalities prone to change)
- introducing interfaces upfront - it goes against Reused Abstraction Principle or Rule of Three (discover rather than design abstractions, apply interface only when you have at least 3 classes that would adhere to it)
Both OOD and functional programming try to reach loose coupling and composability by different means. All these 'patterns' have usually some more sane general architectural concern standing behind.
It would be interesting to know whether old school procedural approach allows you to achieve all those architectural benefits in large scale applications.