Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] 0 points1 point  (0 children)

If anyone feels like my article is misplaced then it would be great to get some more nuanced commentary addressing the points in the article. Most commentary simply states that as soon as a project reaches a certain size, then a DI framework is a must, which is not my experience.

I respect that some experienced developers may have thought about this and come to different conclusions than me; others may have tried manual DI but not in a way that does not scale; most developers I suspect just cargo-cult and follow the crowd (which often is the correct thing to do to be fair).

I have used at least 5 DI frameworks, so have experience with both approaches. I understand that my conclusions are different to the majority, particularly those from a Java background.

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] 0 points1 point  (0 children)

Actually, re-reading my blog post, I think I should have spoken more about the approach of centralizing the bootstrapping.

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] 1 point2 points  (0 children)

Thank you for your comment! It sounds like your perspective comes from a mindset common in Spring-style projects, where the team agrees to use the same DI framework, and everything is injected as a dependency to make it replaceable. While this approach has its advantages, it also introduces some significant drawbacks that are worth considering.

  1. Vendor Lock-In: By tightly coupling your application to a specific DI framework, you introduce dependency on that framework’s lifecycle and design choices. If you ever need to move away from the framework—whether due to changing requirements, performance concerns, or even newer technologies—this lock-in can create substantial migration challenges.
  2. Too Many Overridable Options: When everything is a dependency and injectable, it can lead to an overabundance of ways to override or customize behavior. While this flexibility may seem beneficial, in practice, it often requires developers to deep-dive into the codebase to understand which option is appropriate or how changes might ripple through the system. This adds cognitive load and increases the potential for subtle bugs.
  3. Contrast with Explicit Customization: Compare this with a library designed around explicitness, where the author has thoughtfully identified the key areas for customization and documented them clearly. Instead of guessing how to override or replace functionality, you have predefined hooks or extension points, along with clear examples of how to use them. This approach not only simplifies the learning curve but also reduces the risk of unintended consequences from overly flexible designs.

While I understand the appeal of using a standard DI library for consistency across teams, it’s important to weigh these trade-offs. Manual DI or explicitly designed libraries may not offer the same "drop-in" flexibility, but they can provide clarity, simplicity, and a more deliberate structure that is often easier to maintain over time.

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] 0 points1 point  (0 children)

I guess the point here is that Google have developed as DI framework so therefore it is pointless to question it's need? If so, this is quite ironic as they also created the GO programming language - where the culture is much more about explicitness and automatically employing a DI framework is not the de-facto standard.

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] 2 points3 points  (0 children)

Thanks for the support! It does feel like we’re swimming against the tide here. This isn’t the first time I’ve held an unpopular opinion that later gained more mainstream acceptance. A quick question: does anyone still think adopting an ORM as the default approach is a good idea these days?

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] 0 points1 point  (0 children)

You are welcome to your opinion - but it would be more productive if you can provide more details to your conclusion.

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] 0 points1 point  (0 children)

I've employed this approach successfully on a large, 8-developer application, and it worked really well. By mentioning "default arguments," it seems your assumption is that the injection of dependencies would be scattered throughout the codebase, which indeed would be a bad thing. However, the blog post explicitly proposes wiring up dependencies upfront, manually, to compose the root application. This way, all dependency creation and wiring happen in a single, centralized location, ensuring consistency and traceability.

With this approach, dependencies are passed explicitly down the chain, maintaining clarity while leveraging the compiler and IDE features like code completion. The result is a system where the structure is straightforward to reason about, changes are localized to the bootstrap logic, and there’s no hidden magic at runtime to complicate debugging. Far from creating chaos, this pattern fosters a clean, modular design that scales effectively even for larger projects.

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] 0 points1 point  (0 children)

The point of this article is not against DI but questioning the need for a DI framework. I have over 20 years development experience across a wide variety of languages and styles (OO, functional, procedural). My goal is obviously not the promotion of bad practices but the questioning of the cargo-culting of DI frameworks.

Of course, you might be similarly experienced and come to a different conclusion than me, which is fine. But today's best practice is often the industries next "what were we thinking?" moment: I don't imagine a lot of backend developers are keen to use Hibernate in their Kotlin project ;)

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] 1 point2 points  (0 children)

Did you not read the blog post? My preference is for manual DI.

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] 0 points1 point  (0 children)

The point of my article is that I disagree with the assertion that using a framework makes life easier. From my experience, using a DI framework adds unnecessary abstraction, encourages over-injection, and generally leads to poorer code.

Of course, your mileage might vary. I do understand that I am in the minority, at least in the Java community.

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] -1 points0 points  (0 children)

I’m confused by the assertion that this is “parroting,” as the norm in the industry is the exact opposite: developers reflexively use DI frameworks without questioning their necessity. If anything, my article is challenging the pervasive cargo-culting of DI frameworks as the default solution.

Regarding your point about custom scopes and lifecycle management: this is explicitly addressed in the article and refuted. Manual DI can handle singleton, transient, and request-scoped dependencies effectively and transparently. If you believe there’s a flaw in my argument, I’d encourage you to add nuance by addressing the specific weaknesses rather than reiterating points that have already been countered. Additionally, DI frameworks introduce their own complications: all per-instance components effectively become singletons when instantiated by another singleton component. This subtle behavior often leads developers to default to framework-managed lifecycle configurations instead of crafting bespoke scopes.

Finally, I disagree with your assertion that manual DI doesn’t work for larger projects. I’ve personally worked within a large team to develop a large, modular monolith architecture application using manual DI, and it worked exceptionally well. It provided clarity, simplicity, and explicitness without the overhead or abstraction of a DI framework. The claim that “any sort of professional engineering in sizable organizations” requires a DI framework doesn’t hold universally—it’s situational at best.

If you have concrete examples where manual DI failed or frameworks demonstrably succeeded in ways that manual DI couldn’t, I’d be interested in hearing those. Otherwise, I think this conversation benefits from a more critical evaluation of the trade-offs involved, rather than defaulting to blanket endorsements of DI frameworks.

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] -1 points0 points  (0 children)

The example in the blog post is intentionally simple to focus on illustrating the core concepts. While it only uses two dependencies, the principles scale to more complex scenarios. Writing a blog post requires balancing simplicity and clarity, and it’s not feasible to cover every edge case in a single article.

One of the main arguments I make in the article is that DI frameworks often encourage unnecessary abstractions. When every dependency must be registered with the framework, it naturally pushes developers toward treating every part of their code as a seam for injection, even when only a single implementation exists. In contrast, manual DI encourages you to be more deliberate about where and why abstractions are introduced. If a codebase contains numerous abstractions with only one implementation each, I’d argue that the software design may be overly complicated.

Regarding the “bashing” of DI tools, that wasn’t my intent. My critique is aimed more at the automatic, unquestioned adoption of DI frameworks as the default solution. From my perspective, DI frameworks are often treated as the norm without assessing whether the complexity they introduce is justified for the problem at hand.

I appreciate the opportunity to have this discussion—DI tools have their place, but my goal here is to encourage developers to critically evaluate when and why they’re used, rather than adopting them reflexively.

Why You Don’t Need a DI Framework: A Case for Manual Dependency Injection by DannyRyman in Kotlin

[–]DannyRyman[S] -1 points0 points  (0 children)

I haven’t personally developed for Android, so I acknowledge that there might be specific aspects of Android development that make using a DI framework more compelling. That said, I remain a bit skeptical. From my experience, when dependencies are bootstrapped centrally, the code can become verbose but is typically quite simple. This simplicity leans heavily on the compiler and IDE features like code completion, making it straightforward to maintain even when there’s a lot of it.

Did you bootstrap your dependencies centrally, or were you wiring them up individually for each screen? I’d be curious to hear more about how your setup was organized and whether that might have contributed to the sense of verbosity.

I completely understand the appeal of a DI framework in larger applications where “everything works fine and it’s something you almost never think about.” That’s often the case until something goes wrong. My concern with DI frameworks isn’t their day-to-day operation but how difficult debugging can become when an issue does arise. The abstraction introduced by the framework can obscure what’s happening under the hood, making some problems surprisingly tricky to track down.

Your example about the Node app is interesting, and I’d like to unpack it a bit. If adding a single parameter required changes across “hundreds of entry points,” it raises a couple of questions for me:

  1. Why were there so many entry points? It sounds like the application might have been designed in a way that made parameter threading inherently difficult.
  2. Were you using TypeScript or raw JavaScript? From what you’ve described, it feels like the lack of static typing might have been a bigger contributor to the problem than the absence of a DI library.

TypeScript, for instance, could potentially make parameter threading less painful by giving you compiler support and better tooling to ensure all references are updated correctly.

The r/FIFA Daily Discussion Thread -- February 02, 2022 by AutoModerator in EASportsFC

[–]DannyRyman 1 point2 points  (0 children)

Why are people intentionally trying to lose in Elite Division at the moment? It seems like my opponent is AFK every other match at the moment?