
Test-Driven Development of Go Web Applications with Gin - Liriel
https://semaphoreci.com/community/tutorials/test-driven-development-of-go-web-applications-with-gin
======
zalmoxes
I wish Go examples/tutorials would stop showing code that uses or implies
global state. Many of the beginner tutorials do, and as a result a lot of
libraries and projects out there are also full of global state.

~~~
oldmanjay
Would it be fair to say that the go culture optimizes for productivity more
than any other characteristic? I'm not really immersed but my outsider
observations seem to point that way.

~~~
zalmoxes
I don't think it would be fair to say that the Go community optimizes for
productivity over good software development practices. In fact, there's been a
good push in the community lately to talk about application design and what it
means for code to be good/not good.

* [https://github.com/marcusolsson/gouk16-slides/blob/master/bu...](https://github.com/marcusolsson/gouk16-slides/blob/master/building-an-enterprise-service-in-go.pdf)

* [https://peter.bourgon.org/go-best-practices-2016/](https://peter.bourgon.org/go-best-practices-2016/)

* [http://dave.cheney.net/2016/08/20/solid-go-design](http://dave.cheney.net/2016/08/20/solid-go-design)

However, as a Go developer I'm finding that a lot of examples/tutorials and
github projects are taking shortcuts when it comes to program design and how
dependencies(loggers, config, db handlers) are being passed around.
[https://peter.bourgon.org/go-best-practices-2016/#program-
de...](https://peter.bourgon.org/go-best-practices-2016/#program-design) IMO
it's a sign of Go still being a relatively new language. Despite the language
existing for ~6 years now, we're only discovering some best practices today.

~~~
infogulch
I've actually built out a concept to address this:
[https://github.com/infogulch/inject](https://github.com/infogulch/inject)

Call `di := inject.New(db, logger, tmpl, ...)` then you can call
`di.Inject(myfunction)` which just calls the function with the values passed
to New and returns the result.

"YAWN"

Here's the key: `myfunction` shouldn't just execute, it should return a
closure over the injected values that can be called later. E.g.

    
    
        func home(t *template.Template, db *sql.DB) http.HandlerFunc {
        	return func(w http.ResponseWriter, req *http.Request) {
        		var tm string
        		db.QueryRow(`select datetime('now')`).Scan(&tm)
        		t.ExecuteTemplate(w, "home.html", tm)
        	}
        }
    

Now with `fn := di.Inject(home)`, fn is a plain old http.HandlerFunc, it has
access to its' dependencies (and _just_ those dependencies) without global
state, and there is no reflection in the hot path.

Of course, you could use this technique manually: `fn := home(tmpl, db)` but
inject can be integrated with your framework and you don't have to make any
changes to code. It's also designed to not require the framework to depend on
the reflect package, it can just define and accept the Inject interface.

~~~
zalmoxes
:(

There are much better ways of doing it.

~~~
infogulch
Methods on giant structs that contain all the dependencies isn't any better.
What would you suggest?

