Oh, the IDE can only go so far. It helps, but once you have the relevant logic spread over 20 or more files, no IDE will let you grasp it all.
I tried following what "smarter people" created before me. Preparing for every possible eventual expansion of the system - adding new business logic algorithms, new types of input data, new variants of output, dynamic switch of dataset and algorithm mid-execution, massive parallelism, with a lot of cross-thread communication and clever automatic scheduling of tasks. They had been mucking around with it for 3 years, making a system that was very elegant - and completely useless.
It appeared the underlying system requires everything to be single thread, running under RTOS, because critical operations were not being completed on time and race conditions resulting from the underlying system design abound. And the deadline was in half a year.
All that fancy work had to be scrapped and written from scratch, in a much simplified form only sparsely utilizing scraps of old logic. The whole fancy broad class structure with deep inheritance trees and clever class switch-over mechanism was scrapped, replaced by a couple of classes with inheritance through composition. The smart scheduling was at the core of race conditions; replacing it with two trivial, rigid lists of jobs (realtime, and background) solved the issue.
And it's now some 6 years. The system works fine. Extra business logic algorithm had to be added once, it didn't take more work than it would before. Cosmetic changes of logic happen every couple of months, and need to be applied in three places instead of former one. The idea of business algorithm switch-over on the run appeared to run afoul of safety regulations, the on-the-fly change leading to transitions not allowed by law. The dataset switching, performed maybe once a year, requires extra two minutes of work versus what it was originally. Its structure was only ever expanded, which meant business logic also only was expanded occasionally.
Meanwhile, an area that lay fallow during the first version - interaction with external systems - underwent massive expansions. The fancy structure wouldn't help one bit with that. The range of systems that came up, what they did and what they needed was so wildly varied there's no way any preconceived structure to accept them would ever stand a chance. The lack of such system appeared a blessing, because adding them was straightforward; wherever they had to slice right into heart of the business logic in a completely new way, there was no struggle to break through extra abstraction layers; a single if() operating on easily accessible superglobal replacing twenty new methods to access areas previously isolated from the rest.
The bottom line is that you can't foresee every way the system may be modified or expanded, and making the system extendable in a way you guessed would be common may very well appear both completely useless (the system will never be extended that way) and thoroughly detrimental to expansion in a way that is required. Keeping the system SIMPLE from moment one, and instead of trying to account for every possibility, only doing what it needs to do in the simplest way it can do, is a much better approach to making it easy to expand. And - surprisingly, maybe - makes debug easier too.