Most active commenters
  • gf000(8)
  • roncesvalles(5)
  • Cthulhu_(4)
  • throwaway2037(3)
  • akoboldfrying(3)
  • fweimer(3)
  • unscaled(3)

←back to thread

257 points pmig | 84 comments | | HN request time: 0.002s | source | bottom
1. 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 #
2. MrDarcy ◴[] No.43096604[source]
I hit this point in tfa and had the same comment. Please don’t pass things around in a Comtext. Maybe stash a slog logger in there, but that’s about it.

I made the switch to Go a few years ago. For those who are on a similar journey as the author, or the author himself, I suggest spending time with the Go standard library and tools written by Rob Pike and Russ Cox to get a handle on idiomatic Go.

It’s clear the author still thinks in Java, not go. Saying Context ctx for example instead of ctx context.Context. Also DI, which is arguably not necessary at all in Go given how elegantly interfaces work.

I spent quite a lot of time using wire for DI in go only to really study the code it was generating and realizing it truly is code I would normally just write myself.

Edit:

Regarding stack traces, it turns out you don’t need them. I strongly suggest a top level error handler in Go combined with a custom error struct that records the file and line the error was first seen in your code. Then wrap the error as many times as you want to annotate additional lines as the error is handled up to the top level, but only that first point in our own code is what actually matters nearly all of the time.

replies(14): >>43096842 #>>43098162 #>>43098528 #>>43099235 #>>43099768 #>>43099821 #>>43100046 #>>43100754 #>>43100817 #>>43101015 #>>43101030 #>>43101721 #>>43101893 #>>43103377 #
3. cflewis ◴[] No.43096796[source]
FWIW the internal Google style guide says to not pass anything via Context unless you _really_ know what you're doing and why. Things that make sense: security tokens and tracing. Things that don't make sense: almost everything else.
replies(2): >>43098194 #>>43099440 #
4. arnath ◴[] No.43096842[source]
This comment is about a very minor part of what you said, but isn’t the whole point of a DI framework to write code you’d have written anyway to save you time?
replies(2): >>43098306 #>>43098889 #
5. Ferret7446 ◴[] No.43096956[source]
Context is just thread local storage aka dynamic scoping aka global variables.

It is useful for some things, particularly middleware that needs to cross API boundaries.

https://www.felesatra.moe/blog/2019/12/01/transiting-apis

6. nine_k ◴[] No.43097757[source]
Implicit shared state? Exactly the thing to enjoy in a highly concurrent environment!

At least I hope that a context can be immutable throughout. I only see one variable in the documentation of the context package. Extending it with mutable fields, and mutating them to pass data between functions, would be something I'd never approve in a code review.

replies(1): >>43099837 #
7. mukunda_johnson ◴[] No.43098162[source]
I prefer stack traces in errors. It's gives so much more automatically so you don't have to worry about manual annotation. Stack traces and debug logs are the way to go. I like to use panics for exceptional conditions just for the convenient escape with the stack trace.
replies(1): >>43099348 #
8. throwaway2037 ◴[] No.43098179[source]

    > 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
This is not possible in Java or C#? Both of those languages are so simple to grasp.
replies(4): >>43098215 #>>43099187 #>>43099570 #>>43100840 #
9. throwaway2037 ◴[] No.43098194[source]

    > internal Google style guide
Can we read this somewhere?
replies(2): >>43098893 #>>43099040 #
10. rendaw ◴[] No.43098205[source]
How are interfaces a replacement or improvement to context? Is it just that they're type safe?
11. zdragnar ◴[] No.43098215[source]
The languages aren't, strictly speaking, so much the problem as are the massive frameworks configured via distant files with lots of DI and reflection magic involved.

Go has some large frameworks, but the community frequently suggests they aren't needed and that the standard library is enough.

replies(2): >>43098856 #>>43101802 #
12. MrDarcy ◴[] No.43098306{3}[source]
I was writing code similar to how the popular int13 kubelogin kubectl plugin works, which also uses wire for DI and is organized as a clean architecture repo. In that particular case I found both the clean architecture and the wire DI to add more layers of abstraction, which took more time to comprehend, write, and maintain than jettisoning both and doing it with idiomatic Go.
13. nine_k ◴[] No.43098528[source]
The exact stack trace may not be very necessary, but tracing the chain of calls, especially async, can be hugely helpful in troubleshooting, performance tracking, etc.

In Node, I remember wrapping Promisesromises into objects that had a stack for pushing messages onto them, so that the creator of a Promise could mark the calling site, and creating another Promise within that promise would pick up the chain of call site names and append to it, etc. Logging that chain when a Promise fails proved to be very useful.

replies(1): >>43099165 #
14. demi56 ◴[] No.43098856{3}[source]
So the community influences the language, not the language that influence the community that explains everything
replies(1): >>43099710 #
15. bcrosby95 ◴[] No.43098889{3}[source]
DI frameworks save you from writing trivial code, and it masks dependency insanity. This is why I don't use it even in Java. If the codebase gets to the point where a DI framework is really useful then you've fucked yourself over.
replies(1): >>43101250 #
16. Saser ◴[] No.43098893{3}[source]
https://google.github.io/styleguide/go/ is the public version of it. Having read both I'd say the differences are small. The main thing missing, though, are the GoTip episodes that haven't been made public, and which are excellent.
replies(1): >>43099698 #
17. konart ◴[] No.43099040{3}[source]
I'm not sure about Google style guild but Go's context package docs state:

"Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions."

https://pkg.go.dev/context

Using context as a some sort of a mule is an antipattern.

replies(1): >>43109870 #
18. lelanthran ◴[] No.43099165{3}[source]
> In Node, I remember wrapping Promisesromises into objects that had a stack for pushing messages onto them, so that the creator of a Promise could mark the calling site, and creating another Promise within that promise would pick up the chain of call site names and append to it, etc. Logging that chain when a Promise fails proved to be very useful.

Sounds complicated. I don't use Node much (nor recently, for that matter), but when I write f/end JS I capture the chain of function calls by creating a new exception (or using whatever exception was thrown), and sending the `.stack` field to a globally-scope function which can then do whatever it wants with it.

Will that not work in Node?

replies(1): >>43099834 #
19. lelanthran ◴[] No.43099187[source]
> This is not possible in Java or C#? Both of those languages are so simple to grasp.

Not really, no.

Apart from the cognitive burden of knowing the test framework in use, the DI tooling in use, the Mocking framework in use, plus all the annotations that each framework may want/need, almost none of which applies to the Go projects I've seen, the languages themselves have drifted away from simplicity.

I used to program in C# but left it for a long time and only recently looked at it again. C# is approaching C++ levels of syntax indecipherability.

replies(4): >>43099933 #>>43100780 #>>43101322 #>>43101876 #
20. nesarkvechnep ◴[] No.43099235[source]
I’m yet to see a former Java developer who uses the idioms of the language they currently use. They all just write Java in a different language.
replies(2): >>43099695 #>>43100282 #
21. ignoramous ◴[] No.43099348{3}[source]
> I like to use panics for exceptional conditions just for the convenient escape with the stack trace.

One can debug.PrintStack(), instead.

https://pkg.go.dev/runtime/debug#PrintStack

22. rapsey ◴[] No.43099570[source]
Programs written in those languages tend to have many layers of abstractions, on top of abstraction heavy frameworks.
23. 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 #
24. geitir ◴[] No.43099625[source]
Also https://github.com/uber-go/fx
25. 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 #
26. roncesvalles ◴[] No.43099689{3}[source]
I disagree with the premise of that whole project. It shouldn't exist.
27. vram22 ◴[] No.43099695{3}[source]
That is a function of the developer, not of the language, i.e. f(dev), not f(lang) ;)

Replace Java with star and that statement still holds true (for some people).

Hence the statement that you can write FORTRAN in any language.

https://blog.codinghorror.com/you-can-write-fortran-in-any-l...

replies(1): >>43099969 #
28. starquake ◴[] No.43099698{4}[source]
I think the Google Go style guide is really nice and pragmatic. There are some references to the GoTips for some advanced subjects. I hope they release it someday.

I made an issue for it: https://github.com/google/styleguide/issues/881

29. someothherguyy ◴[] No.43099699[source]
So, write python in go instead, gotcha.
replies(1): >>43099736 #
30. 65a ◴[] No.43099702{3}[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.
31. rob74 ◴[] No.43099710{4}[source]
Well, it's a bit of both. Java is of course very influential, and because it uses a framework for web applications, many languages/communities started imitating that (Ruby/Rails, PHP/Laravel, JS/[Framework_of_the_day] etc.). Then Go came along with its back-to-basics approach and its standard library which is "batteries included" but definitely not a framework, and for some this is a breath of fresh air, while for others it's apparently unbearably alien and backwards...
replies(2): >>43100819 #>>43101703 #
32. roncesvalles ◴[] No.43099736{3}[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 #
33. 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 #
34. akoboldfrying ◴[] No.43099768[source]
> Regarding stack traces, it turns out you don’t need them.

goes on to suggest rolling your own buggy, slow, informally specified implementation of half of a stack trace printer

35. roncesvalles ◴[] No.43099791{3}[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 #
36. andreasmetsala ◴[] No.43099821[source]
> Also DI, which is arguably not necessary at all in Go given how elegantly interfaces work. > I spent quite a lot of time using wire for DI in go only to really study the code it was generating and realizing it truly is code I would normally just write myself.

DI is the idea that you should create your dependencies outside of the module / class / function that uses it and pass it in. This makes it easy to swap implementations.

DI does not require any framework and I would argue you can’t write modular code without it. Most likely you are doing DI even in your manually written code.

replies(1): >>43099943 #
37. galaxyLogic ◴[] No.43099834{4}[source]
I believe it does. Creating a new Error-instance to learn what the current stack is a bit hacky, but it can be useful, even when there is no error. The code can reflect on who is calling it.

Sometimes a function or method is called very many times so trying to log them all is useless. But at the same time it can be the case that there are multiple callers of the said function. Then looking at a stack we can log just the case of some specific call-chain calling that function.

38. cyberax ◴[] No.43099837[source]
The context itself is immutable, it's essentially a linked list (or actually a tree). If you need to "mutate" it, you create a new segment that links to a previous segment.
39. 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 #
40. zigzag312 ◴[] No.43099933{3}[source]
> C# is approaching C++ levels of syntax indecipherability.

Can you post an example of such new syntax? In my experience C#'s syntax is getting cleaner.

41. RussianCow ◴[] No.43099943{3}[source]
If you're doing "dependency injection" by just passing arguments to functions/modules, you're not really doing dependency injection—you're doing "dependencies" without the "injection" part. I'm not saying that DI necessitates a ton of magic, but you need at least a small framework for specifying dependencies and injecting them into your modules dynamically.
replies(5): >>43100207 #>>43100273 #>>43100588 #>>43100709 #>>43101162 #
42. eximius ◴[] No.43100046[source]
> spent quite a lot of time using wire for DI in go only to really study the code it was generating and realizing it truly is code I would normally just write myself.

Yes, but the point is 1) you don't have to write it yourself and 2) it does 'the right thing' even if your junior dev straight out of BS CS wouldn't know how to write it.

There are, of course, caveats and not all frameworks are created equal and any tool can be misused. But I find DI very useful. Personally I'd recommend Uber's Fx framework and underlying dig library.

43. com2kid ◴[] No.43100207{4}[source]
Design patterns are independent of the implementation technology.

OO and virtual functions can be implemented in C by looking up function pointers in a table.

Reference counting can be done by manually incrementing and decrementing references.

At the end of the day everything is compiled to assembly and the CPU doesn't care what ideology was in the programmer's head, except however much a given paradigm abstracts too far away from the underlying machine.

44. cryptos ◴[] No.43100273{4}[source]
But that "framework" could be a simple factory function.
45. mavelikara ◴[] No.43100282{3}[source]
This isn’t anything special about Java. A determined programmer can write Fortran in any language.
46. tpm ◴[] No.43100420{3}[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 #
47. lucumo ◴[] No.43100588{4}[source]
No, that's wrong.

DI requires that the deps come from outside, not that it's dynamically created. The opposite is that dependencies are created inside the unit. DI is about which part of the code owns the dependency. With DI it's some parent component, without DI it's the component itself.

DI with magic can simplify the management of component lifecycles, but it's entirely possible to do it without.

48. gf000 ◴[] No.43100709{4}[source]
No, you can pass (inject) the necessary dependencies to the constructor of an object at creation time, and then simply use that object instance everywhere. The only thing frameworks do is "solve" the dependency graph and instantiate stuff in the correct order.

This is also the most common/preferred way Spring et alia implements their "framework-aided" DI, so that you can write unit tests easily without bootstrapping a Spring context (and it is just well-designed vanilla Java code).

49. gf000 ◴[] No.43100727{4}[source]
Sounds like a no true Scotsman fallacy.
50. fweimer ◴[] No.43100729{4}[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 #
51. gf000 ◴[] No.43100732{4}[source]
So global mutable state? How wonderfully modern!
52. fweimer ◴[] No.43100754[source]
How does Go avoid getting bogged down by middleware-oriented programing in practice? I think most large-scale programming in organizations tend to converge on that because it sort of works.

Is it that people who like this kind of stuff write microservices for deployment on Kubernetes clusters?

replies(1): >>43101748 #
53. gf000 ◴[] No.43100759{5}[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 #
54. gf000 ◴[] No.43100780{3}[source]
C# tends to accumulate a lot of new features, but Java has a bog-standard syntax and even new features are very carefully added and easily guessable even if you haven't seen them before (e.g. switch expressions vs statements).

There is absolutely nothing more readable in Go than in Java, it's just classis familiarity (referencing the simple vs easy talk), I would even wager that too little expressivity will actively hinder code understanding.

(It's much easier to understand the intent of a complex stream API "pipe" than 4 nested for loops with ifs, with a bunch of dumb if errs pretending to be error handling).

Also, you are comparing a complex framework that solves 70% of your problem domain (at the price of a bit of a learning curve) with a vanilla Go project that only calls libraries. The exact same is more than possible with Java, it just doesn't make much sense in case of CRUD apps because better ways exist.

55. nprateem ◴[] No.43100817[source]
> Regarding stack traces, it turns out you don’t need them

Then you go on to explain how to recreate them by hand.

56. gf000 ◴[] No.43100819{5}[source]
I'm fairly sure that most of these frameworks are not direct imitations of what Java did, and there was a back-and-fort co-evolution where standard CRUD web applications can be written in a very very productive way (for the small price of learning a framework).

Sure, reinventing the wheel is fun, but if I can finish a whole project while the equivalent go code can finally return "Hello world", then I'm not sure it's a worthwhile tradeoff. Java is not anemic when it comes to its standard library, people moved to frameworks because a good deal of CRUD apps have to solve some of the same problems. Don't forget, the only silver bullet is reusing existing code.

57. nprateem ◴[] No.43100840[source]
Try understanding what spring boot is doing. Annotation soup. It's practically impossible for a beginner to Spring to debug.
replies(2): >>43101149 #>>43101788 #
58. saturn_vk ◴[] No.43101015[source]
> Maybe stash a slog logger in there, but that’s about it.

Please don't do this either. Read the stuff you want to log as additional attributes in your slog handler from the context, which you ultimately pass to `slog.*Context`

59. saturn_vk ◴[] No.43101030[source]
> Also DI, which is arguably not necessary at all in Go given how elegantly interfaces work.

DI is necessary in every language that doesn't rely solely on global singletons. Passing dependencies as arguments to a function is DI.

What may not be necessary, are IOC containers automatically create objects and satisfy their dependencies.

replies(1): >>43101199 #
60. gf000 ◴[] No.43101149{3}[source]
I mean, would you be able to pilot an airplane at first try? Does it mean that it is badly designed?

Frameworks reverse who calls who, it is your code that will be called at certain points, not the other way around. Obviously, one has to learn a bit about the framework before touching one, blindly copy-pasting from stackoverflow/chatgpt is not software develolment.

61. simiones ◴[] No.43101162{4}[source]
"Injection" in "dependency injection" simply refers to getting your dependencies from outside instead of building them yourself.

Let's take the case of an object which represents a simple CRUD web service that needs to talk to a database to get some data. Here is what it looks like without dependency injection:

  func NewService(databaseHostname, databaseLoginSecret string) (WebService, error) {
    databaseConn, err := databse.NewConn(databaseHostname, databaseLoginSecret)
    if err != nil {
        return WebService{}, fmt.Errorf("Failed to create database conn for WebService: %w", err)
    }
    return WebService{
        databaseConn: databaseConn
    }, nil
  }
And here is what it looks like with dependncy injection:

  func NewService(databaseConn DatabaseConn) WebService {
    return WebService{
        databaseConn: databaseConn
    }
  }
This is the only concept: don't build your own dependencies, get them from outside.

Ideally then there is a place in your application, possibly in `main()`, where all of the base services are initialized, in the required order, and references are passed between them as needed. This can include "factory" style objects if some of these need to be initialized on-demand.

62. unscaled ◴[] No.43101199{3}[source]
Too many people confuse the concept of DI with a DI framework. You don't even need a DI framework to write straightforward programs in Java. After all, Java also has interfaces!

One of the reason people needed a DI framework in Java is crazy "enterprise" configurability requirements and Java EE-based standards that required you to implement a class with a default no-argument constructor. If you're using a web framework like Jooby, Http4k, Ktor or Vert.x, you do not need a DI framework (source: we've written many modern Kotlin applications without a DI framework and we've had zero issues with that).

Of course, all of our non-toy Go applications are using dependency injection as well. Unless the code reviewer messes up, we won't let anyone configure behavior through globals and singletons.

replies(1): >>43103293 #
63. unscaled ◴[] No.43101250{4}[source]
To be fair, traditional Java EE apps often required a DI framework, because you couldn't control the main entry point of the program, and the entry point to your code was a class with a default no-argument constructor.

This is still insanity, but the insanity comes from Java EE rather than the apps themselves.

64. CharlieDigital ◴[] No.43101322{3}[source]
Kinda disagree on C#

It's actually converging with JS and TS

Small repo: https://github.com/CharlieDigital/js-ts-csharp

Edit: but I don't want to dismiss either because C# does cover a large gamut of use cases (desktop, 3D/gaming, COM interop, etc.) and it is true that in some use cases, you may see a wider range of keyword soup compared to just building web APIs (where I think it really shines; this is a .NET web API in 4 lines: https://imgur.com/a/simple-net-api-with-route-param-validati...).

65. unscaled ◴[] No.43101395{4}[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 #
66. fweimer ◴[] No.43101585{6}[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.

67. demi56 ◴[] No.43101703{5}[source]
I would say the leaders(could be the creators) of the language plays the most important role in its infancy the decisions they make defines what decisions the community are gonna make and the was what Go made right, Java been a business oriented decisions made by the community will also be business oriented
68. roncesvalles ◴[] No.43101709{5}[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.
69. Cthulhu_ ◴[] No.43101721[source]
> Then wrap the error as many times as you want to annotate additional lines as the error is handled up to the top level

I'd add that this is a last resort; errors should be handled and resolved as close to where they occur as possible. Having them bubble up to a central error handler implies you don't really want to do anything with it.

replies(1): >>43102360 #
70. Cthulhu_ ◴[] No.43101748{3}[source]
Go itself doesn't do anything about that, nor does Java or JS dictate anything about using middleware.

I can't speak for the ecosystem / developers though; there are plenty of examples where e.g. HTTP request handlers are wrapped in several onion layers of middleware itself. But, there's also an emphasis on keeping things simple and lightweight.

What kind of middleware are you thinking of when you mention things getting bogged down?

71. Cthulhu_ ◴[] No.43101782{3}[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.
72. mattgreenrocks ◴[] No.43101788{3}[source]
IntelliJ seems able to locate all potential implementations at injection points just fine.
73. Cthulhu_ ◴[] No.43101802{3}[source]
Exactly; if you ask about for example a test assertion or mocking library like in Java, you get told to not use it. These libraries often add a DSL of sorts, meaning you have to relearn them.

Granted, Go's testing library / setup is also a bit of a jump from regular Go code (especially things like table tests). But the assertions are just `if`s.

74. spicyusername ◴[] No.43101876{3}[source]
Gotta disagree for C#.

It definitely has more language features than Go, but by no means is indecipherable.

I find C# syntax to be quite crisp.

75. the_gipsy ◴[] No.43101885[source]
> Test using monkey-patching.

Can you elaborate? What library do you use?

76. jbreckmckye ◴[] No.43101893[source]
> Regarding stack traces, it turns out you don’t need them. I strongly suggest a top level error handler in Go combined with a custom error struct that records the file and line the error was first seen in your code. Then wrap the error as many times as you want

So instead of a stacktrace, you are - tracing the stack? Am I understanding correctly?

Because it just sounds like a manual version of stacktraces

replies(1): >>43107247 #
77. gf000 ◴[] No.43102360{3}[source]
I would argue that in the majority of the cases that's the only reasonable behavior. I do agree that errors should be handled as close as possible to where they occured, but not any closer -- e.g. there is not much you can do within a library's functions if an unexpected error occured inside. It should be handled at whatever business code happens to call it. And in the worst case, they should bubble up to a central handler, that either outputs an 500 error, or an error dialog.

Exceptions pretty much make this the sane/default behavior, with rust-like ADTs with syntax sugar being close.

78. akoboldfrying ◴[] No.43102630{4}[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...)

79. mexicocitinluez ◴[] No.43103293{4}[source]
> One of the reason people needed a DI framework in Java is crazy "enterprise" configurability requirements a

No, it's so that you can have something else manage the lifetime and disposal of your services instead of doing this yourself. You don't have to be writing crazy enterprisey code to have the need for this.

I agree DI is simple, but 100% disagree that you can achieve this through a hand-rolled library without sinking a ton of wasted time.

80. mexicocitinluez ◴[] No.43103377[source]
> I spent quite a lot of time using wire for DI in go only to really study the code it was generating and realizing it truly is code I would normally just write myself.

This is like saying "I won't use source generators because it generates code I would normally write myself"

Like, yea no shit. THAT'S the point.

81. Capricorn2481 ◴[] No.43107247{3}[source]
> Because it just sounds like a manual version of stacktraces

Because it is. I don't understand it either.

82. throwaway2037 ◴[] No.43109870{4}[source]

    > Using context as a some sort of a mule
I never saw the term "mule" used in this way. Very succinct!
replies(1): >>43109914 #
83. bdangubic ◴[] No.43109914{5}[source]
https://www.mulesoft.com/platform/soa/mule-esb-open-source-e...

:)

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