News for hackers. Stuff that matters.
Re:Dependency injection in Go with Uber-go/fx by BobbyJo on Sunday February 27, 2022 @08:27AM (#30488196)
I've used dependency injection heavily in Java in previous jobs, and, later, spent a few years doing Golang at another job. I never missed it.

What does dependency injection give you that a simple combination of Singletons, Constructors, and Factories doesn't? I feel like the only thing you get is the ability to combine multiple independent dependency trees without having to make a sane structure. Kind of like, what redux does for state in JavaScript. It make things easier because it takes away some responsibility, but, in my eyes, that responsibility is an important one, and ignoring it makes code very hard to reason about.

Re:Dependency injection in Go with Uber-go/fx by bluefirebrand on Sunday February 27, 2022 @08:48AM (#30488377)
This is actually something I've been trying to talk about with a new hire at my job. We are running a nodejs stack, and he comes from a strict OOP C# background.

I've never worked in a strict OOP paradigm, coming from Python and JavaScript mostly myself. I've been puzzled by his insistence that DI is important. He wants us to implement DI containers and take a "code to interfaces not implementations" approach. To me theres not really any value in this approach in JavaScript.

I don't know Go, but I get the impression this stuff is also not as valuable there.

My question is always "what does this get me and what does it cost me?". Outside of strict OOP paradigms like C# and Java, it's pretty unclear what the benefits are to me.

Re:Dependency injection in Go with Uber-go/fx by cle on Sunday February 27, 2022 @08:55AM (#30488449)
It's not. It saves a few lines of code, with the tradeoff that you are handing some critical functionality--the wiring of dependencies--over to a black box. When things don't behave as you expect (which you might not initially notice), there is no code path to follow, you have to figure out what kind of magic is happening in the black box. It's just not worth the one-time cost of writing a few extra lines of code. This is also congruent with the aesthetic of Go in general.
Re:Dependency injection in Go with Uber-go/fx by danmur on Sunday February 27, 2022 @08:53AM (#30488425)
I've found DI pretty useful when building frameworks, it gives you a sort of generic plugin system. Plus you can hide it from consumers if that makes sense. If you're just building applications I'm not sure it's worthwhile.
Re:Dependency injection in Go with Uber-go/fx by bluefirebrand on Sunday February 27, 2022 @09:09AM (#30488551)
The argument I always hear is DI makes your code more testable.

Which is probably true in some cases, but I find most of my code is pretty testable without it. Haven't run into any tests I've wanted to build where I couldn't.

Re:Dependency injection in Go with Uber-go/fx by danmur on Sunday February 27, 2022 @09:20AM (#30488655)
I think that's a side-effect of how code must be written to work with dependency injection, to make the injection actually possible, IMO it's not the DI itself that makes it testable.
Re:Dependency injection in Go with Uber-go/fx by isbvhodnvemrwvn on Sunday February 27, 2022 @09:36AM (#30488815)
Are you sure you are not mistaking dependency injection with dependency injection containers?
Re:Dependency injection in Go with Uber-go/fx by mdpye on Sunday February 27, 2022 @09:14AM (#30488591)
How do you test the code which determines when to fire the torpedoes, without actually firing the torpedoes every time you run the tests?
Re:Dependency injection in Go with Uber-go/fx by danmur on Sunday February 27, 2022 @09:21AM (#30488660)
Just fire them and do a ROLLBACK
Re:Dependency injection in Go with Uber-go/fx by bluefirebrand on Sunday February 27, 2022 @09:19AM (#30488647)
By using a mock?
Re:Dependency injection in Go with Uber-go/fx by mdpye on Sunday February 27, 2022 @09:23AM (#30488692)
How do you convince the when-to-fire-logic to act on a mock, if the torpedo implementation is not provided from outside it?

I'm wondering if I have the terminology understood differently to other people, because to me, DI == IoC. DI Frameworks are build on top of the concept that dependencies will be injected, but doing it explicitly in your start-up code is the same thing to me.

Re:Dependency injection in Go with Uber-go/fx by bluefirebrand on Sunday February 27, 2022 @10:01AM (#30489031)
No, you understand perfectly well!

This is the same conversation I had with the new hire I mentioned. And as the other comment here mentioned, it's JavaScript. You simply overwrite the object method with the mock at runtime of the test, then restore it after the test finishes running.

Most JS testing frameworks do this under the hood I believe, without any change in how you write your code. It's a fundamental difference in how JavaScript handles things versus something stricter like C#.

It's a dangerous footgun if you use it carelessly, of course. But a useful language feature when applied carefully.

Re:Dependency injection in Go with Uber-go/fx by mdpye on Sunday February 27, 2022 @10:41AM (#30489415)
Right. Facepalm. I kind of missed the bit where we shifted paradigms.

My personal preference would still be to be explicit, to echew secret mutable state even in the tests, but that's just me. My personal baggage makes phrases like "then restore it after the test finishes" bring me out in a cold sweat ;)

I guess I sympathise with your colleague, but I agree it's not idiomatic, and not being idiomatic is not helpful in a team.

Re:Dependency injection in Go with Uber-go/fx by ByteJockey on Sunday February 27, 2022 @12:59PM (#30490719)
In languages like JS/Python, you can mock imports. Actually, sometimes you can mock things defined in the same file if you set things up right.

Basically, the logic not being inlined into the function is enough to provide this behavior in certain languages.

This is not true for other languages, which is why DI frameworks tend to be more useful there.

In go specifically, I just always throw the dependencies behind an interface and shove them into a struct. Then you just create a struct of mocks for your testing.

Re:Dependency injection in Go with Uber-go/fx by isbvhodnvemrwvn on Sunday February 27, 2022 @09:47AM (#30488911)
JS allows you to override global objects, pretty much nothing is constant or immutable in JS. You can (more or less) replace arbitrary functions in any place, at any time.
Re:Dependency injection in Go with Uber-go/fx by mkdirp on Sunday February 27, 2022 @09:11AM (#30488560)
> What does dependency injection give you that a simple combination of Singletons, Constructors, and Factories doesn't?

Easy refactoring of an interface/constructor? I've been using go for a while, and I decided to follow the advise of not using a DI for my project. Every time I refactor a constructor, it is a fun hunt to make the same changes everywhere I'm using that constructor.

Re:Dependency injection in Go with Uber-go/fx by bbkane on Sunday February 27, 2022 @11:05AM (#30489614)
Doesn't the compiler tell you exactly what needs to change?
Re:Dependency injection in Go with Uber-go/fx by mkdirp on Sunday February 27, 2022 @06:26PM (#30493940)
It does, but having to go through every single error to fix the issues isn't that easy depending on the number of dependants.
Re:Dependency injection in Go with Uber-go/fx by marvinblum on Sunday February 27, 2022 @12:14PM (#30490297)
Or the IDE. GoLand from JetBrains is very helpful there.
Re:Dependency injection in Go with Uber-go/fx by tadfisher on Sunday February 27, 2022 @06:17PM (#30493862)
That's exactly what a Factory is for; you defer construction and pass around an object that knows how to create an instance. Then you're hunting at the root of your call tree to swap out a Factory instead of throughout your codebase to change a constructor call.

This is also what DI amounts to in practice. Frameworks abstract over it in the name of DRY, but at the same time introduce all the downsides of frameworks.

Re:Dependency injection in Go with Uber-go/fx by mkdirp on Sunday February 27, 2022 @06:28PM (#30493964)
A service still needs its own dependencies, thus forcing you to change its factories where needed. With a DI you re-define it in a single place.
Re:Dependency injection in Go with Uber-go/fx by BobbyJo on Sunday February 27, 2022 @01:36PM (#30491118)
That's true. However, the decoupling that occurs when refactoring one and not the other creates two different code structures, often with different levels of abstraction. I saw the same thing happening with a combination of react and redux, an it made debugging weird behaviors really, really, hard.
Re:Dependency injection in Go with Uber-go/fx by twodave on Sunday February 27, 2022 @08:50AM (#30488399)
I came here to ask a similar question. I’ve spent most of my career writing C# where DI is prevalent. Over the Christmas holiday I picked up golang a bit. When I went to learn about DI for golang it seemed very counter to the principles of the language so I simply moved on. What am I missing out on?
Re:Dependency injection in Go with Uber-go/fx by mdpye on Sunday February 27, 2022 @09:21AM (#30488659)
Do you distinguish between dependency injection (frameworks) and general inversion of control?

Because what I think of dependency injection is extremely common in golang - they made interfaces satisfy structurally rather than nominally so that consumers could specify interfaces which anyone is free to satisfy. That the consumer owns the interface (packages shouldn't export interfaces for concretes they implement) is a pretty core tenet, and goes hand in hand with good dependency injection (IoC).

DI frameworks are extremely rare (and IME even more painful to use), because injecting your dependencies explicitly is straightforward. But injected they should be.

Re:Dependency injection in Go with Uber-go/fx by azth on Sunday February 27, 2022 @02:43PM (#30491789)
Because golang doesn't have constructors, you're forced to implement functions that mimic them yourself. And the language still doesn't prevent you from directly instantiating the struct yourself, meaning it is always possible to bypass the "constructor functions". This is quite terrible and opens up your code to errors.

Furthermore, DI frameworks also usually have lifecycle management, which is quite handy in many cases.

Re:Dependency injection in Go with Uber-go/fx by twodave on Sunday February 27, 2022 @05:19PM (#30493280)
Fine, but that applies to even data structures and other domain types, which even in a more classically-OO language you wouldn’t pipe through a DI framework. I think DI as a code organization concept is great, but if I have to write a factory for every struct anyway then it’s not any harder to simply use those as a rule and define module boundaries sensibly as a way to determine which code should be using new vs the factory methods.

You have a good point wrt lifecycle management, but I feel like that’s actually a separate class of problem.

Re:Dependency injection in Go with Uber-go/fx by gefhfffh on Sunday February 27, 2022 @09:22AM (#30488673)
Please don't confuse DI with DI containers. With DI you still have all the responsibility.
Re:Dependency injection in Go with Uber-go/fx by cfors on Sunday February 27, 2022 @08:55AM (#30488442)
Agree. I really can only imagine this being useful for an organization that publishes dozens of different libraries that a "product team" needs to use such as logging, database access, secret managers, etc. At that point, speaking the common language of a DI framework might be easier.

For example, rather than have to read the GoDoc for every single constructor i'm supposed to use, I can just see how to use my DI framework to set up this client library and move on with my life.

That being said, DI always strikes me as a layer of abstraction that I don't need in my day to day. But I don't work somewhere at Google/Uber scale so YMMV.

Re:Dependency injection in Go with Uber-go/fx by cle on Sunday February 27, 2022 @09:09AM (#30488552)
There's nothing special about DI frameworks there. If a client library has some sane defaults preferred by the devs, then call a constructor that sets the defaults for you. You don't need DI for that.

IME at tech megacorps, DI just obfuscates and distracts, shifting the focus onto understanding the accidental complexity it introduces instead of dealing with the intrinsic complexity of the dependency relationships.

Re:Dependency injection in Go with Uber-go/fx by catlifeonmars on Monday February 28, 2022 @09:22AM (#30500067)
I mostly agree. I think there are two cases where using a DI framework make sense:

1. Complex lifecycle management, maybe. For example say you need to restart a set of go routines after loading a new configuration, without killing the process. I’m on the fence about this one.

2. When there is a combinatorially large number of components that need to be combined arbitrarily at runtime. The only examples I’ve seen for this in the wild are games and simulations using an ECS (entity-component-system) and queries to discover components that fit a certain criteria.

Re:Dependency injection in Go with Uber-go/fx by akshayshah on Sunday February 27, 2022 @12:20PM (#30490362)
Fx author (in part) here - replied in a separate comment. I mostly agree with you, but DI offers some ecosystem-wide benefits that may be useful in some environments.
Re:Dependency injection in Go with Uber-go/fx by awill88 on Sunday February 27, 2022 @06:33PM (#30494016)
I want to say thanks to your team for open sourcing this library so the community can discuss. It’s ambitious and to me feels like a right size approach to the problem, for all the heartache around DI, it seems like it’s different strokes for different folks but that doesn’t mean we should all be taking a collective shit on the engineers who are trying their best to provide a way to organize service complexity strategically. Well done.
Re:Dependency injection in Go with Uber-go/fx by likeabbas on Sunday February 27, 2022 @12:42PM (#30490577)
Such as?
Re:Dependency injection in Go with Uber-go/fx by tonfreed on Sunday February 27, 2022 @05:05PM (#30493131)
I feel the same way. IoC is a great pattern, but DI frameworks that try to magically sort out dependencies are just overkill especially in go imho
Re:Dependency injection in Go with Uber-go/fx by lr4444lr on Sunday February 27, 2022 @10:06AM (#30489074)
Just come out and say it: DI is an anti-pattern. No implementation of it that I'd ever use was doable without a good config file schema, and at that point I have to ask: why not just have a declarative build system in the first place as the modern generation of Devops tools does? DI just burdens and litters code unnecessarily with awareness of build files.
Re:Dependency injection in Go with Uber-go/fx by isbvhodnvemrwvn on Sunday February 27, 2022 @10:15AM (#30489146)
You, as many people here, are again mistaking dependency injection with dependency injection containers. You don't need containers for dependency injection, most codebases can just wire everything by hand without any problem.
Re:Dependency injection in Go with Uber-go/fx by lr4444lr on Thursday March 10, 2022 @09:09AM (#30627029)
What's an example of what you're talking about? Because the delegator pattern is not CI, and that's the only thing that's coming to mind that you might mean.
Re:Dependency injection in Go with Uber-go/fx by saturn_vk on Sunday February 27, 2022 @09:46AM (#30488909)
I think you are confusing dependency injecting with frameworks that facilitate it. You are probably using the former with constructors
Re:Dependency injection in Go with Uber-go/fx by BobbyJo on Sunday February 27, 2022 @01:32PM (#30491064)
What's the difference? Isn't the 'injection' part the part where you don't need to connect point A and point B?
Re:Dependency injection in Go with Uber-go/fx by taeric on Sunday February 27, 2022 @01:54PM (#30491324)
Sorta. The pattern is having standard method of injecting dependencies. Having a framework just saves you having to do the actual injecting, manually.

This is easy to see in frameworks like dagger, which compile time generate the boilerplate you could manually do.

And if everything is a singleton with no lifetime management, the framework doesn't buy you too much. The pattern, though, is kind of nice. I rarely have to question how a dependent section of code is linked to the one I'm at. (Contrast to python, where I don't know what is going to happen if I add that import...)

Re:Dependency injection in Go with Uber-go/fx by BobbyJo on Monday February 28, 2022 @03:21PM (#30504619)
> (Contrast to python, where I don't know what is going to happen if I add that import...)

I felt that in my bones.

Re:Dependency injection in Go with Uber-go/fx by tasubotadas on Sunday February 27, 2022 @09:01AM (#30488484)
>What does dependency injection give you that a simple combination of Singletons, Constructors, and Factories doesn't?

You don't need to create a "simple combination" of Singletons, Constructors, and Factories.

Re:Dependency injection in Go with Uber-go/fx by BobbyJo on Sunday February 27, 2022 @01:28PM (#30491025)
Don't you? In Java I still had to create the Singleton/Constructor/Factory etc. and register it with the framework as a provider of that type.