Most active commenters
  • roncesvalles(5)
  • gf000(3)

←back to thread

257 points pmig | 21 comments | | HN request time: 0.248s | source | bottom
Show context
bryancoxwell ◴[] No.43096481[source]
> But there are obviously work around solutions in the Go ecosystem. It uses the Context ctx, which we pass around functions in order to juggle data around in the application.

Man. This works. The context API allows/enables it. But I’d really recommend against passing data to functions via context. The biggest selling point of Go to me is that I can usually just look at anyone’s code and know what it’s doing, but this breaks down when data is hidden inside a context. Dependency injection is entirely possible without using the context package at all, interfaces are great for it.

replies(8): >>43096604 #>>43096796 #>>43096956 #>>43097757 #>>43098179 #>>43098205 #>>43099616 #>>43099625 #
1. roncesvalles ◴[] No.43099616[source]
IoC DI in Go is a massive antipattern and absolutely should not be done. Do NOT write Java/.NET style controllers in Go i.e. initializing an instance of a "controller" type with some instances of a "dependency" such as a store.

Just use the dependent package directly. Initialize the package once using init() or Init(). Rely on the built-in package dependency resolver system in Go, which will catch cyclic dependencies, call init() in topo order, and other such things.

Test using monkey-patching. Stop using interfaces just to be able to swap a real thing with a mock implementation. These are all symptoms of writing Java in Go.

replies(6): >>43099670 #>>43099699 #>>43099767 #>>43099845 #>>43101885 #>>43194806 #
2. 3uler ◴[] No.43099670[source]
And yet Uber wrote fx[1] to support DI in their golang services. It’s clearly a useful pattern when working on large services.

[1]: https://github.com/uber-go/fx

replies(3): >>43099689 #>>43099702 #>>43101782 #
3. roncesvalles ◴[] No.43099689[source]
I disagree with the premise of that whole project. It shouldn't exist.
4. someothherguyy ◴[] No.43099699[source]
So, write python in go instead, gotcha.
replies(1): >>43099736 #
5. 65a ◴[] No.43099702[source]
I really cannot say Uber's use of Go is particularly idiomatic to me, having started writing Go more than a decade ago now. It just strikes me as overwrought, and I've worked on big services.
6. roncesvalles ◴[] No.43099736[source]
The idiomatic way to write Go is as naively as possible after you fully understand how it works. Otherwise it'll just feel like Java with shitty ergonomics.

If you're ever writing Go and wish you had real classes instead of this deconstructed "mess" with struct types, methods, and interfaces, you're writing Go totally wrong.

replies(2): >>43100727 #>>43101395 #
7. stpedgwdgfhgdd ◴[] No.43099767[source]
I don't get the part on package. Often you want to use structs. (Instance vs static)

How would you construct an instance in a test that needs mock implementations?

replies(1): >>43099791 #
8. roncesvalles ◴[] No.43099791[source]
Such an instance would just be a variable somewhere that would be initialized in some init() call when the program starts. Every user of that instance will use it directly (as opposed to storing a reference to it locally DI style).
replies(1): >>43100732 #
9. akoboldfrying ◴[] No.43099845[source]
> Test using monkey-patching.

TIL Go has monkey-patching.

But since it has monkey-patching, how is it statically typed? Or does Go monkey-patching amount to creating an instance of a defined-on-the-fly subclass?

The latter would be interesting, because Java lets you do this too -- conveniently "new up" an instance of a subclass that you define inline of a given class or interface (this used to be the easiest way to define callbacks, before lambdas came along), so this testing strategy is available to Java too, but seems not to be preferred.

Separate question: IIUC, monkey-patching is convenient if your test code directly needs to create a TestX instead of a real X, but what if you need the test double "deeper down" -- that is, you need an X that creates a Y that creates a TestZ instead of a real Z?

replies(1): >>43100420 #
10. tpm ◴[] No.43100420[source]
Java doesn't really enable monkey-patching in the style of Javascript, Perl etc. AFAIK, or am I missing something? That you can create anonymous subclasses during runtime is different to e.g. editing methods of existing objects/classes during runtime.
replies(2): >>43100729 #>>43102630 #
11. gf000 ◴[] No.43100727{3}[source]
Sounds like a no true Scotsman fallacy.
12. fweimer ◴[] No.43100729{3}[source]
JVMTI allows method patching: https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.h... The capabilities are somewhat limited, and an enhancement JEP was withdrawn: https://openjdk.org/jeps/159

On the other other, there is a movement to remove such late binding/rebinding features from the Java platform. However, I think JVMTI is expected to remain supported if the agents are loaded ahead of time. Only on-demand loading of agents will be removed from OpenJDK.

replies(1): >>43100759 #
13. gf000 ◴[] No.43100732{3}[source]
So global mutable state? How wonderfully modern!
14. gf000 ◴[] No.43100759{4}[source]
I wouldn't say that there is a movement to remove such late binding features -- the "movement's" primary goal is simply to properly encapsulate and mark any such part of the code (e.g. make it explicit in the module that that should be supported).

That way AOT compilation becomes possible/efficient and dependencies can't hack into other packages in an unmaintainable way without explicit permission from the user, so that updating the JVM and dependencies will be even more streamlined.

replies(1): >>43101585 #
15. unscaled ◴[] No.43101395{3}[source]
That is certainly one way to write Go, but I wouldn't call it "idiomatic" when evil the Google Best Practices guide recommends against it[1][2]. I'll wager you'll probably find it in most other Go style guides. This is how the Go standard library itself works. While you can use http.Get() if you want to use the default HTTP client for simple apps, the library provides you the http.Client struct, where you can also override the Transport (RoundTripper interface) and Jar (CookieJar interfaces). The transport interface lets you further override the Dialer and Proxy function.

[1] https://google.github.io/styleguide/go/best-practices#global... [2] https://google.github.io/styleguide/go/best-practices#provid...

replies(1): >>43101709 #
16. fweimer ◴[] No.43101585{5}[source]
As far as I know, there is no tooling support to propagate these marks to the launcher program even in cases where they can be statically discovered. (This could be similar how dynamic linker agents (audit modules) are handled by the Solaris linker, via DT_AUDIT and DT_DEPAUDIT markers.) Mainly because OpenJDK does not provide a tool (beyond jar) that creates launchers.

This doesn't concern features that have been traditionally abused (such as reflection on core OpenJDK classes), but also harmless (from an encapsulation perspective) uses of JNI, and access to future features such as FFI/Panama. Justification for restricting those as well is not so much OpenJDK updates, I think, but that it could cause crashes that might be blamed on OpenJDK.

17. roncesvalles ◴[] No.43101709{4}[source]
The example they've chosen is somewhat contrived in that the dependency is maintaining a collection of plugins. In such a case it makes sense to go with their approach. I'm not actually arguing against having multiple instances of some package's functionality; indeed such instances may be created and stored per controller. I'm more so arguing against IoC style Dependency Injection, i.e. central instantiation of the controller and its dependencies. My point is just, as far as possible, it's better to rely on init() and package import relationships. You can simply call dep.New() in any number of inits.
18. Cthulhu_ ◴[] No.43101782[source]
Just because a known company uses it doesn't mean it's authoritative; there's likewise functional programming libraries built by big companies, but FP should also be avoided in Go because it's not a functional language and not optimized for it.
19. the_gipsy ◴[] No.43101885[source]
> Test using monkey-patching.

Can you elaborate? What library do you use?

20. akoboldfrying ◴[] No.43102630{3}[source]
I agree -- I was just trying to guess what OP might have meant by "monkey-patching" when talking about Go (which is TTBOMK statically typed, which precludes what I'd personally call "monkey-patching" at the language level).

Java does have "agents", which enable arbitrary control of bytecode at class loading time -- bytecode in existing .class files can be transformed, or completely new bytecode generated on-the-fly. But generating bytecode just to replace a class with a test double would be insane.

From another comment I learned there's also a separate "Tool Interface" that can do this bytecode-replacement job, but again, it's a huge amount of tricksiness and bother for something more easily and clearly accomplished in source code with a boring old anonymous subclass. (Though it won't work if the class is sealed, or final, or your code uses reflection to check the class name, or...)

21. captain_coffee ◴[] No.43194806[source]
Hard disagree to literally every thing that you have said in your reply above.