Earlier quoted context omitted.
Crazy that actually using your type system leads to better code. Stop passing everything around as `string`. Parse them, and type them.
There's a name for this anti-pattern: "Stringly typed"
How I write HTTP services in Go after 13 years
81–90 of 259 posts
Re: How I write HTTP services in Go after 13 years
#82Earlier quoted context omitted.
I mean, you may end up just wanting something like, type UsernameError struct { name string reason string } func (e *UsernameError) Error() string { return fmt.Errorf("invalid username %q: %s", e.name, e.reason) } And reason can be "username cannot be empty" or "username may not contain ' This is fine for lots of different cases, because it’s likely that your code wants to know how to handle “username is invalid”, bu…
I write mostly frontends. Sometimes the APIs I talk to give back beautiful English error messages - that I can't just show to the user, because they are using a different language most of the time. And I don't want to write logic that depends on that sentence, far too brittle.
It’s not perfect, but software evolves through many imperfect stages as it gets better, and this is one such imperfect stage that your software may evolve through.
Including a human-readable version of the error is useful because the developers / operators will want to read through the logs for it. Sometimes that is where you stop, because not all errors from all backends will need to be localized.
Re: How I write HTTP services in Go after 13 years
#83> The Valid method takes a context (which is optional but has been useful for me in the past) and returns a map. If there is a problem with a field, its name is used as the key, and a human-readable explanation of the issue is set as the value. I used to do this, but ever since reading Lexi Lambda's "Parse, Don't Validate," [0] I've found validators to be much more error-prone than leveraging Go's built-in type check…
Re: How I write HTTP services in Go after 13 years
#84I really like Mat Ryer's work, and I've applied most of the ideas in the 2018 version of this article to all of my Go projects since then. The 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 ab…
Can you explain that a little more?
Which chunk of code has what shared state, and why is it unnecessary?
Re: How I write HTTP services in Go after 13 years
#85Earlier quoted context omitted.
One of the major issues with a lot of the outdated concepts in programming is that we still teach them to young people. I work a side gig as an external examiner for CS students. Especially in the early years they are taught the same OOP content that I was taught some decades ago, stuff that I haven’t used (also) for some decades. Because while a lot of the concepts may work well in theory, they never work out in a w…
> It’s almost always better to repeat code. God no. Stop the copy pasta disease! It's horrible, mindless programming. When reviewing code, I'm astonished anything was accomplished by copy pasting so much old code (complete with bugs and comment typos). Incidentally, OOP encourages you to copy a lot. It's just an engine for generating code bloat. Want to serialize some objects? Here's your Object serializer and your o…
Re: How I write HTTP services in Go after 13 years
#86Earlier quoted context omitted.
One of the major issues with a lot of the outdated concepts in programming is that we still teach them to young people. I work a side gig as an external examiner for CS students. Especially in the early years they are taught the same OOP content that I was taught some decades ago, stuff that I haven’t used (also) for some decades. Because while a lot of the concepts may work well in theory, they never work out in a w…
> It’s almost always better to repeat code. God no. Stop the copy pasta disease! It's horrible, mindless programming. When reviewing code, I'm astonished anything was accomplished by copy pasting so much old code (complete with bugs and comment typos). Incidentally, OOP encourages you to copy a lot. It's just an engine for generating code bloat. Want to serialize some objects? Here's your Object serializer and your o…
Re: How I write HTTP services in Go after 13 years
#87Earlier quoted context omitted.
It would need a getter here. Probably good to keep it immutable, if you want guarantees that it will never be changed to something that violates the username rules.
> need a getter Yeah, thats what I figured. Im not sure if I want the tradeoff of calling .GetValue in multiple places just to save calling validate in maybe 2 or 3 places. Not to mention I cant easily marshal/unmarshal into it and next week valid username is a username that doesnt already exist in the database. Maybe this approach appeals to people and Im hesitant to say “that’s not how Go is supposed to be written”…
The tradeoff is not that you save calling validate, it’s that you avoid forgetting to call validate in the first place, because when you forget to validate, you get a type error.
IMO it’s a little more clear this way:
type Ticket struct {
requestor Username
assignee Username
}
It lets you write code that is little more obvious.Re: How I write HTTP services in Go after 13 years
#88I really like Mat Ryer's work, and I've applied most of the ideas in the 2018 version of this article to all of my Go projects since then. The 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 ab…
> It means that a huge chunk of your code has a huge amount of unnecessary shared state. Can you explain that a little more? Which chunk of code has what shared state, and why is it unnecessary?
While I may prefer a struct for this instead of separate arguments, I do agree it's useful to capture "the world" as the set of all dependencies, even if some handlers don't use them (yet).
Re: How I write HTTP services in Go after 13 years
#89Earlier quoted context omitted.
> need a getter Yeah, thats what I figured. Im not sure if I want the tradeoff of calling .GetValue in multiple places just to save calling validate in maybe 2 or 3 places. Not to mention I cant easily marshal/unmarshal into it and next week valid username is a username that doesnt already exist in the database. Maybe this approach appeals to people and Im hesitant to say “that’s not how Go is supposed to be written”…
> Yeah, thats what I figured. Im not sure if I want the tradeoff of calling .GetValue in multiple places just to save calling validate in maybe 2 or 3 places. The tradeoff is not that you save calling validate, it’s that you avoid forgetting to call validate in the first place, because when you forget to validate, you get a type error. IMO it’s a little more clear this way: type Ticket struct { requestor Username ass…
The original example was more “wrap a simple type in an object so it’s always validated when set” which looks beautiful when you don’t have the needed getters in the example nor show all the Get call sites opposed to the 1 or 2 New call sites. All in the name of “we don’t want to set the username without validation” but without private constructors Username{“invalid”} can be invoked, the validation circumvented and I’m not convinced the overhead we paid was worth it.
Re: How I write HTTP services in Go after 13 years
#90I've recently been playing with ogen: https://github.com/ogen-go/ogen Write openapi definition, it'll do routing, definition of structs, validation of JSON schemas, etc. All I need to do is implement the service. Validating an integer range for a querystring parameter is just too boring. And too easy to mistype when writing it manually. Anyways, so far only been playing, so haven't found the bad parts yet.