How does EK9 validate dependency injection at compile time?
← Dependency Injection · Ref: Q227
EK9 validates ALL dependency injection at compile time. If your program compiles, every injection point is guaranteed to be satisfied. There are zero runtime DI failures.
FOUR COMPILE-TIME VALIDATIONS
The compiler performs four checks on every application definition:
1. Completeness: Every injection field ('!' suffix) must have a matching registration
2. Ordering: Dependencies must be registered before dependents
3. Cycle detection: Circular dependencies are rejected with error E08190
4. Field count: Components with excessive injection fields are flagged with E11040
CONTRAST WITH SPRING/GUICE
In Java Spring, a missing @Bean causes NoSuchBeanDefinitionException at runtime, sometimes minutes into startup. In Guice, a missing binding produces CreationException. In EK9, you find out during compilation, before any code runs.
WHY THIS MATTERS
Runtime DI failures are among the most frustrating bugs. They appear only when a specific code path is exercised, often in production. EK9 eliminates this entire category of defect by moving validation to compile time.
WORKING EXAMPLE
The code below demonstrates a correctly wired application. The compiler has verified that Logger is registered before Formatter, both are registered before MessageService (which injects them), and the program's injection point is satisfied.
See Q111 for component basics. See Q117 for singleton lifecycle. See Q228 for registration ordering. See Q229 for circular dependency detection. See Q230 for missing registration errors. See Q310 for compile-time quality philosophy.
See Q324 for @Autowired equivalent. See Q332 for runtime overhead comparison.
See Q666 for injection pure context. See Q673 for missing registration. See Q675 for transitive validation.
Example
defines module qa.di.compile.time defines component Logger as abstract log() as abstract -> message as String <- output as String? default operator ? ConsoleLogger is Logger override log() -> message as String <- output as String: "LOG: " + message default operator ? Formatter as abstract format() as abstract -> text as String <- result as String? default operator ? UpperFormatter is Formatter override format() -> text as String <- result as String: text.upperCase() default operator ? MessageService as abstract greet() as abstract -> name as String <- greeting as String? default operator ? SimpleMessageService is MessageService logger as Logger! formatter as Formatter! override greet() -> name as String <- greeting <- String() formatted <- formatter.format("Hello " + name) greeting: logger.log(formatted) default operator ? defines application ValidatedApp register ConsoleLogger() as Logger register UpperFormatter() as Formatter register SimpleMessageService() as MessageService defines program CompileTimeDiDemo() with application of ValidatedApp stdout <- Stdout() // === ALL INJECTION VALIDATED AT COMPILE TIME === service as MessageService! result <- service.greet("World") stdout.println(result) stdout.println("All injections verified by compiler")
Common mistakes
E08150 — Injection fields must use abstract types, not concrete implementations. Only abstract components can be injection targets. See ek9 -h E08150 for details.
Incorrect:
logger as ConsoleLogger!
Correct:
logger as Logger!
E50060 — String has no toUpperCase() method in EK9. Use upperCase() instead. See ek9 -h E50060 for details.
Incorrect:
result <- service.greet("World").toUpperCase()
Correct:
result <- service.greet("World")
E08210 — If the register statement for Logger were removed, the injection field 'logger as Logger!' in SimpleMessageService would have no matching registration and the compiler would reject it. See ek9 -h E08210 for details.
Incorrect:
register UpperFormatter() as Formatter
Correct:
register ConsoleLogger() as Logger
Other ways to ask this
- Does EK9 catch DI errors before runtime?
- How does EK9 prevent missing dependency errors at compile time?
- What DI validations does the EK9 compiler perform?
- How does inversion of control work in EK9?
Coming from another language?
Java Spring: NoSuchBeanDefinitionException at runtime, @Conditional can hide missing beans, circular dependency detection only at startup. Guice: CreationException at Injector creation, JIT bindings can mask issues. .NET: InvalidOperationException during service resolution, runtime-only. Python: no compile-time DI validation at all. Go: no built-in DI. Rust: no built-in DI. EK9: ALL DI validated at compile time, zero runtime failures possible.
Keywords: migrate, inject, injection, circular, inversion, guarantee, ioc, compile, safety, completeness, validate, ordering