I've tried object-oriented programming and understand concepts like classes, objects, inheritance, encapsulation, and polymorphism, but I still find large object-oriented codebases difficult to navigate. Deep layers of classes and dependencies can turn into a "banana-gorilla-jungle" situation where finding one piece of behavior means searching through an entire forest of abstractions. Functional and procedural code feels clearer and easier for me to maintain. Should I avoid object-oriented programming where possible, or is there a better way to use it without creating that kind of complexity?
5 Answers
For small personal projects, object-oriented structure can feel like unnecessary overhead. Larger systems often benefit from grouping state and behavior or modeling important business concepts explicitly, but that does not mean every detail needs its own class. Start with the simplest design that works, then introduce abstractions when repeated change or complexity gives you a reason.
A lot of code described as object-oriented is really procedural code spread across classes, with excessive inheritance and too many layers. That kind of design can be awful, but it is not the only way to use objects. Learning to keep boundaries shallow, favor composition, and avoid abstractions without a clear purpose may make the style much less frustrating.
Try not to treat programming paradigms as competing ideologies. Use the approach that fits the problem. Functional, procedural, and object-oriented techniques can all work well, and many real systems combine them. The important skill is recognizing when an abstraction is helping and when it is just adding layers.
You may dislike deep inheritance hierarchies more than object orientation itself. Objects can be kept fairly simple, and composition is often easier to understand than inheritance. In a well-designed system, classes should have focused responsibilities and predictable relationships rather than becoming a maze of wrappers and base classes.
Functional and procedural code can absolutely be used for business logic and unit tests. Polymorphism is one possible testing technique, not a requirement for every test. You can test functions directly, pass dependencies as arguments, or use interfaces when substitution is actually useful. Also, polymorphism and object orientation are related but not identical concepts.

That’s probably closer to what I mean. A small class or object is fine; the trouble starts when every action requires tracing through several layers of indirection.