I think a good way to handle this convergence would be to move away from repeated invocations of the compiler. Instead, your build system&#x2F;IDE could keep an active instance of the compiler running that keeps the last generated ASG&#x2F;intermediate representation&#x2F;machine code in memory, along with bookkeeping information that remembers the relations between these. When a file changes&#x2F;a rebuild is requested, this lets the build system to not only correctly identify dependencies, but also allows far more granular incremental compilation than is possible now. For example, if you really only change one function, then only that function&#x27;s code (and possibly any code which relies on optimizations based on that function&#x27;s form) need to be changed. You could even add more to this by tracking dependencies in unit tests and automatically re-running them when the result may have changed, or using it to facilitate some sort of live-coding.<p>This sounds like it would need a huge amount of memory, but IDEs already do this to the ASG level, and much memory and computation is wasted on the compiler re-generating the ASG in parallel when the IDE has a similar one already analyzed. The main disadvantage is it would restrict how build systems could be structured, as to pull this off the build system would need to have much more ability to reason about the build (call command X if Y has changed after Z won&#x27;t cut it). Macro systems would also need to be more predictable.<p>As far as keeping things non-monolithic, you could still have plenty of separation between each phase of the compilation process, the only extra interface you would need between passes is the more granular dependency tracking.<p>edit: grammar