dependency injection in Golang

A dependency injection container helps you to avoid manual wiring the dependencies. Yet still, there’s a debate whether containers are a natural fit to Go, as it provides an idiom that lets you completely reach the same goals in your own codebase. And now there’s Wire, a new DIC from the Go team. Go engineer Márk Sági-Kazár discusses the topic.

Márk Sági-Kazár, working in Budapest for Banzai Cloud, recently discussed the use of dependency injection containers in a blog that was one of the best-reads in Maarten Bezemer’s Golang Ninjas newsletter.

It was a trigger to ask Márk for a Q&A conversation in the #live-interviews channel that is part of the Gophers Slack Community (34.000+ members). Read the full conversation, initially starting in this screenshot below.

Dependency injection interview in Slack

Márk Sági-Kazár:

About me: I develop in Go for about four years now. I learnt that Go became a very divisive language: someone either hates it or loves it. I immediately fell in love with it.

Banzai Cloud’s mission is simple: we want to make cloud native infrastructure simple and available to everyone. Our primary product is called Pipeline which lets you create Kubernetes clusters on all kinds of cloud providers with features like autoscaling, monitoring, log collection out of the box. On top of that, we provide a simple CI/CD solution to build your application and deploy it easily to your cluster. This way developers does not need to know the infrastructure to its full extent, only the parts they have to work with. Without having a full operations team.

Sebastiaan (interviewer):

That indeed can be a big burden, the infrastructure. But very important to be able to scale up. Could you maybe give some impressions about how dev teams use this in their daily life? What are the steps to make and what is the UX?

Márk Sági-Kazár:

Well, it is usually a company-wide decision which cloud provider to use. We provide a unified interface that covers all cloud providers. In case of GKE this might not add as much value, since GKE’s interface is quite clean and easy to use, but imagine you have to start cluster on Amazon EKS. It takes a lot of time to configure it. With our platform, it’s just a few clicks to start a new cluster on any of the cloud providers, so it’s just one, simple platform that you have to learn.

To start using the platform now you basically have two steps to do:
1. Sign up for our beta at: https://banzaicloud.com/
2. Once you are accepted for the beta (currently we accept like all the applications) you can go ahead and start creating your first cluster. The cluster creation wizard is quite straightforward with decent defaults. It also tells you how much the cluster will cost you. So no surprises at the end of the month. Another thing that cloud providers don’t really “provide”.

Sebastiaan (interviewer):

Do you have a screenshot of the in-platform environment?

Márk Sági-Kazár:

Banzaicloud UI

banzaicloud user interface

banzaicloud ux

Sebastiaan (interviewer):

Great, thanks.
Now there are of course a couple of straight-forward reasons why backend teams want to apply Golang. But there are always some reasons that have more weight for one team compared to the other. What were the two most important reasons for you guys to use Golang?

Márk Sági-Kazár:

For one: all cloud native stuff is written in Golang, so in order to integrate with them, we were kinda forced to use it, but not in a bad meaning of course. :slightly_smiling_face:

Secondly, but this is kinda the same reason, Golang is currently the best language to write cloud native stuff. Easy to write, easy to build and deploy and fast.

About dependency injection:

Sebastiaan (interviewer):

So your recent blog article discusses dependency injection in Go, referring to the recent implementation of Wire. For those who are not familiar with Wire yet: it takes a different approach from Facebook’s inject and Uber’s dig. It doesn’t use reflection, but generates the container.

Does your own Go team apply (wire) dependency injections? Or any other (container) dependency injections? Why?

Márk Sági-Kazár:

It’s really important to separate the concept of dependency injection from dependency injection containers and frameworks. The first does not require the second.

Yes, we use dependency injection. It makes testing the code easier, it takes us one step closer to SOLID.

But we don’t use any DI containers or frameworks. Mainly because there is no need for them in Go, at least not in our case.

Sebastiaan (interviewer):

In your article you state the following:

> Wire is not the first of its kind in the Go ecosystem, but, as a product of the Go team, it quickly went viral and started a conversation within the community about whether dependency injection containers are “idiomatic” in Go. While this has not yet grown as heated as the “tabs vs spaces” debate, there are lovers and haters with valid arguments on both sides.

Which are the valid arguments on both sides? Can you outline them?

Márk Sági-Kazár:

The most obvious argument is personal preference. Coming from a language where a team used DI frameworks, it’s easy to reason that they want to use one in Go. I know it’s not a technical argument, but devs often forget that a team can only work with that they have.

As I explained in the article, my theory about why DI frameworks spread is the appearance of large, complex, general purpose frameworks. When the dependency graph grows so big that it’s simply impossible to handle it in one place manually, that’s when DI frameworks and modular service registration methods come in handy. But that’s simply not the case in Go. We don’t have large, general purpose frameworks. We have frameworks, but they are minimalistic, web frameworks. For that reason, many people (and I) simply don’t see why DI frameworks are necessary in Go. They make reading the bootstrapping code harder as you don’t actually see what gets what.

I think these are the main arguments, of course one could easily find some more technicalities.

Sebastiaan (interviewer):

Then you state the following:

We don’t have far to go in order to meet types of dependency injection in Go. In fact, we can find most of them in the net/http package. Specifically, the http.Server implementation provides two serviceable examples.
One is the so-called property injection:

1 server := &http.Server{
2 Handler: http.NewServeMux(),
3 }

In this case, the dependency is the HTTP handler, which is passed to a struct member field (“property”). In a language like Go, this is usually not the safest dependency injection, because there is nothing that prevents concurrent access or subsequent writes, both of which can lead to unexpected behavior

Could you explain better why method injections are a safer option than struct member field like HTTP handler?

Márk Sági-Kazár:

Well, as quoted above, nothing prevents concurrent access to a property. While it may not be common to modify a property value after it’s set, it can be done accidentally which leads to unexpected behaviour.

With method injection you can protect yourself by using locks, but that makes the code bloated. So the safest and cleanest type of injection is “constructor injection”. I use quotes, because there are no constructors in Go, only a concept of using New* methods.

Sebastiaan (interviewer):

Dependency injection containers are another phenonemon, as outlined earlier in an article of Drew Olson. You write that based on what we’ve seen so far, they don’t add much value. The clearest and most concise reason why they are probably still used, are frameworks, you say.

What kind of frameworks would these be in relation to Go?

Márk Sági-Kazár:

Well, I mean frameworks in general, not really in Go. Go (fortunately or not) doesn’t have these complex, general purpose frameworks, like Java (Spring) or PHP (Symfony) have.

The reason why we “need” DI containers is that the dependency graphs of applications built on these frameworks grew so big, that handling them manually was just a pain.

But as I said, Go doesn’t have these frameworks (yet?). Given what we (should) use Go for, I don’t think there will be any in the future.

Thus DI containers will never really be a thing in Go.

Sebastiaan (interviewer):

In your eyes, do you think it was wise to introduce Wire?

Márk Sági-Kazár:

I’m glad you asked that.

Because Wire doesn’t use reflection, but generates the container, it is in my opinion fundamentally better. Because otherwise you can get compile time errors if something is missing.

On the other hand, it will not spare you too much code, makes your application dependencies less readable, and adds an extra build step, so I don’t really see the added value.

Personally I think it’s quite a risky thing that the Go Cloud team introduced Wire. Go is a very opinionated language and having a Go team releasing a DI framework might send the message that this is the idiomatic way to handle dependencies in Go.

Sebastiaan (interviewer):

Thanks! And thanks for the interview. In the meantime, happy to see @jjuliano also formulating a question, guessing as I see him typing. For people that are interested to ask something, jump in right now please.

Márk Sági-Kazár:

Thank you! It was quite interesting!

Rodrigo Valentim:

Hi Márk,
I would like to know which framework can you recommend to use with Wire and which are been used in Banzai Cloud, if any?

Márk Sági-Kazár:

Hi Rodrigo!

We use a framework called Gin. It’s quite minimalistic and easy to use.

As to what to use with Wire: you can use which you see fit to your application requirements. Since Go frameworks are mostly minimalistic web frameworks, there isn’t much connection between the web and the DI framework you use, unlike in large frameworks, where the whole application is wired into the DI container.

Rodrigo Valentim:

I’ll take a look at Gin.
Thanks.

Joel Bryan Juliano:

I have been following the DCI (Data, Context and Interaction) architecture since it was introduced by Trygve Reenskaug back in 2009, and in his paper (https://klevas.mif.vu.lt/~donatas/Vadovavimas/Temos/DCI/2009%20The%20DCI%20Architecture%20-%20A%20New%20Vision%20of%20OOP.pdf), he intended it to be used for OOP in general. I know that Go is not an OOP language, but the interesting thing about DCI is it also introduce the concept of *injection*, here’s a quore of a citation in the wikipedia page (see: https://en.wikipedia.org/wiki/Data,_context_and_interaction)

> Dependency injection is a longstanding approach to change the functionality of an object at run time by allowing it to “outsource” some of its execution to an external object that can be re-bound at will. Most implementations[which?] of dependency injection lead to the self schizophrenia problem,[citation needed] which implementations of DCI address properly. Systems such as Elmo use this approach, which brings additional complexity to resolve method ambiguity and duplicate data member names.[13][full citation needed]

And in the past, I remember there were several implementations of it was tried on different languages, where it injects the role into the context of the object.

Do you think Go’s DIC is also somewhat influenced by DCI?

Márk Sági-Kazár:

Well, although Go is not an object oriented language, many object oriented paradigms can easily be implemented in Go. I even say they should be. For example Dave Cheney talked about achieving SOLID with Go:

But unfortunately I don’t know if Wire, or any of the dependency injection containers in Go were influenced by DCI or not. If I have to guess, I would say they were rather influenced by DIC’s in frameworks of other languages, like Java or PHP.

Dependency injection Golang interview end

Do you write great blogs about Go too? Good chance they are included in Maarten Bezemer’s Golang Ninjas newsletter. You can subscribe below.






Also published on Medium.

Tags: , ,

Categorised in: ,

This post was written by Bas van Essen

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.