What is the value making main.go as small as possible? Whose dreams come true in this scenario?
How I write HTTP services in Go after 13 years
11–20 of 259 posts
Re: How I write HTTP services in Go after 13 years
#12What is the value making main.go as small as possible? Whose dreams come true in this scenario?
Re: How I write HTTP services in Go after 13 years
#13Re: How I write HTTP services in Go after 13 years
#14What is the value making main.go as small as possible? Whose dreams come true in this scenario?
For bespoke internal services, I like to keep main.go as flat as reasonable, like a "script". Handlers can have their own files but the bulk of the control flow and moving parts should be apparent from reading the main file. Abstracting things away from main makes it less readable and is general pointless for bespoke services that will be deployed in exactly one configuration.
Re: How I write HTTP services in Go after 13 years
#15> My handlers used to be methods hanging off a server struct, but I no longer do this. If a handler function wants a dependency, it can bloody well ask for it as an argument. No more surprise dependencies when you’re just trying to test a single handler.
For HTTP services in any language, your handlers will usually end up with a lot of business logic, logic which probably has many dependencies. I see single handlers using all of the following on a regular basis: DB, cache, blob storage, some kind of special authz thing specific to your endpoints, maybe some fancy licensing checker, a queue or two, a specialized logger, and specialized metrics client. Many of those (metrics, request/response logging) can live in middlewares most of the time, but in every code base there will be times where you need to do something custom with one or the other. As time passes, the more I wonder "why aren't these all just function parameters?"
Yes, that would be a lot of function parameters (9+ for a single handler, before even getting into the request or custom params themselves), and we all have many rules of thumb and linter rules which try to keep us from having lots of function parameters. But it's not like we're not writing code which depends on all those dependencies, instead we're just sticking them on the "server" class/struct and pretending that because the method signature is shorter, we have fewer dependencies!
As time passes, I find myself wishing more and more for code that takes all its dependencies in the function/method signature, even if there's 20 of them; at least then we wouldn't be lying about how complex the code's getting...
Re: How I write HTTP services in Go after 13 years
#16I never want to see another (esp. Python) Quick Start guide that treats dependencies as implicit/static/untestable.
Re: How I write HTTP services in Go after 13 years
#17Re: How I write HTTP services in Go after 13 years
#18I want to see a greater acceptance of this idea: > My handlers used to be methods hanging off a server struct, but I no longer do this. If a handler function wants a dependency, it can bloody well ask for it as an argument. No more surprise dependencies when you’re just trying to test a single handler. For HTTP services in any language, your handlers will usually end up with a lot of business logic, logic which proba…
Re: How I write HTTP services in Go after 13 years
#19The one weak spot for me is this aspect:
>NewServer is a big constructor that takes in all dependencies as arguments... In test cases that don’t need all of the dependencies, I pass in nil as a signal that it won’t be used.
This has always felt wrong to me, but I've never been able to figure out a better solution.
It means that a huge chunk of your code has a huge amount of unnecessary shared state.
I often end up writing HTTP handlers that only need access to a tiny amount of the shared state. Like the HTTP handler needs to check if the requesting user has access to a resource, and then it needs to call one function on the datastore.
I'd love to write tests where I only mock out those two methods, but I can't write simple tests because the handler is part of this giant glob where it has access to all of the datastore and every object the parent server has access to because it's all one giant object.
Nothing against Mat Ryer, as his pattern is the best I've found, but I still feel like there's some better solution out there.
Re: How I write HTTP services in Go after 13 years
#20is there a git repo with example code?
I think this is a pretty good example of a real-world implementation:
https://github.com/mtlynch/picoshare
Particularly these files:
https://github.com/mtlynch/picoshare/blob/2cd9979dab084ca781...
https://github.com/mtlynch/picoshare/blob/2cd9979dab084ca781...