The Julia compilation model is fairly complex, due to the Julia user's wish for dynamic capabilities combined with common features in statically typed languages.
Although it may appear so, it was not low hanging fruit - it took a lot of effort building incrementally over several releases by a number of contributors.
In retrospect, how essential was it that Julia went the dynamic route in the first place (as opposed to taking a Haskell-like approach to inferred static typing)?
It's a pretty major difference. Julia lets you write code that the type system doesn't understand, while in a static functional languages you generally have to explain a decent portion of category theory before someone can start writing code that passes around functions. (For a simple example, what is the type of `+`, and does that type allow you to make Int+Float=Float?)
To elaborate on the sibling comment: Julia has (1) multimethods / multiple dispatch capabilities, (2) you can dynamically define new types and methods at runtime, (3) everything is aggressively devirtualized/compiled to machine code when possible. That requires the capability of discarding and recompiling code that has already been compiled and cached when a new method is defined at runtime. Worse even, just importing a new library might invalidate some of the compiled cached code. For me, capabilities 1,2,and 3 make the cost of a complicated compilation model worth it. But it also means it takes a lot of time to improve the compiler (this type of better caching was not at all a low hanging fruit).
Although it may appear so, it was not low hanging fruit - it took a lot of effort building incrementally over several releases by a number of contributors.