Live data from Hacker News

How I write HTTP services in Go after 13 years

grafana.com

131–140 of 259 posts

Re: How I write HTTP services in Go after 13 years

#131

Earlier quoted context omitted.

Then the zero value is their problem, not yours. You have no reason to be worried about that any more than you are worried about them not getting enough sleep, or eating unhealthy food. What are you doing to stop them from doing that? Nothing, of course. Not your problem. Coq exists if you really feel you need a complete type system. But there is probably good reason why almost nobody uses it.

> Then the zero value is their problem, not yours. Except for all those times you're the consumer of someone else's library and there's no way for them to indicate that creating a zero-valued struct is a bug. Again, it's the philosophy of "Just do the right thing everywhere and you don’t have to worry!" Sometimes it's nice to work with a type system where designers of libraries can actually prevent you from writing b…

> Except for all those times you're the consumer of someone else's library and there's no way for them to indicate that creating a zero-valued struct is a bug.

Nonsense. Go has a built-in facility for documentation to communicate these things to other developers. Idiomatic Go strongly encourages you to use it. Consumers of the libraries expect it.

> Sometimes it's nice to work with a type system where designers of libraries can actually prevent you from writing bugs.

Well, sure. But, like I said, almost nobody uses Coq. The vast, vast, vast majority of projects – and I expect 100% of web projects – use languages with incomplete type systems, making what you seek impossible.

And there's probably a good reason for that. While complete type systems sound nice in theory, practice isn't so kind. There are tradeoffs abound. There is no free lunch in life. Sorry.

Re: How I write HTTP services in Go after 13 years

#132

Earlier quoted context omitted.

I always understood "parse don't validate" a bit differently. If you are doing the validation inside of a constructor, you are still doing validation instead of parsing. It is safer to do the validation in one place you know the execution will go through, of course, but not the idea I understand "parse don't validate" to mean. I understand it to mean: "write an actual parser, whatever passes the parser can be used in…

I'm not a Haskell developer, so it's possible that I misunderstood the original "Parse, Don't Validate" post. > If you are doing the validation inside of a constructor, you are still doing validation instead of parsing. Why that would be considered validation rather than parsing? From the original post: > Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces…

Parse don't validate means that if you want a function that converts an IP address string to a struct IpAddress{ address: string } you don't validate that the input string is a valid IP address then return a struct with that string inside. Instead you parse that IP into raw integers, then join those back into an IP string.

The idea is that your parsed representation and serializer are likely produce a much smaller and more predictable set of values than may pass the validator.

As an example there was a network control plane outage in GCP because the Java frontend validated an IP address then stored it (as a string) in the database. The C++ network control plane then crashed because the IP address actually contained non-ASCII "digits" that Java with its Unicode support accepted.

If instead the address was parsed into 4 or 8 integers and was reserialized before being written to the DB this outage wouldn't have happened. The parsing was still probably more lax than it should have been, but at least the value written to the DB was valid.

In this case it was funny Unicode, but it could be as simple as 1.2.3.04 vs 1.2.3.4. By parsing then re-serializing you are going to produce the more canonical and expected form.

Re: How I write HTTP services in Go after 13 years

#133
post #91

Earlier quoted context omitted.

It's not guaranteed at all, that's where go's zero-values come in. E.g. nested structs, un/marshaljson magic methods etc. How do you deal with that?

Every struct requiring its zero value to be meaningful is probably one of the worst design flaws in the language.

This is where we arrive at my conclusion that go is not well-suited to implementing business logic!

Re: How I write HTTP services in Go after 13 years

#134

Earlier quoted context omitted.

I'm not a Haskell developer, so it's possible that I misunderstood the original "Parse, Don't Validate" post. > If you are doing the validation inside of a constructor, you are still doing validation instead of parsing. Why that would be considered validation rather than parsing? From the original post: > Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces…

Parse don't validate means that if you want a function that converts an IP address string to a struct IpAddress{ address: string } you don't validate that the input string is a valid IP address then return a struct with that string inside. Instead you parse that IP into raw integers, then join those back into an IP string. The idea is that your parsed representation and serializer are likely produce a much smaller an…

Perhaps "normalize" or "canonicalize" is more appropriate. A parser can liberally interpret but I don't take it to imply some destructured form necessarily. There are countless scenarios where you want to be able to reproduce the exact input, and often preserving the input is the simplest solution.

But yes usually you do want to split something into it's elemental components, should it have any.

Re: How I write HTTP services in Go after 13 years

#135

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"

I've also seen it called primitive obsession, which is also applicable to other primitive types like using an integer in situations where an enum would be better.

Re: How I write HTTP services in Go after 13 years

#136

Earlier quoted context omitted.

I'm not a Haskell developer, so it's possible that I misunderstood the original "Parse, Don't Validate" post. > If you are doing the validation inside of a constructor, you are still doing validation instead of parsing. Why that would be considered validation rather than parsing? From the original post: > Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces…

Parse don't validate means that if you want a function that converts an IP address string to a struct IpAddress{ address: string } you don't validate that the input string is a valid IP address then return a struct with that string inside. Instead you parse that IP into raw integers, then join those back into an IP string. The idea is that your parsed representation and serializer are likely produce a much smaller an…

Thanks for that explanation! I hadn't appreciated that aspect of "parse, don't validate," before.

But even with that understanding and from re-reading the post, that seems to be an extra safety measure rather than the essence of the idea.

Going back to my original example of parsing a Username and verifying that it doesn't contain any illegal characters, how does a parser convert a string into a more direct representation of a username without using a string internally? Or if you're parsing an uint8 into a type that logically must be between 1 and 100, what's the internal type that you parse it into that isn't a uint8?

Re: How I write HTTP services in Go after 13 years

#137
post #100

Earlier quoted context omitted.

I’m not sure I understand. In your example you’ve grouped related data in a struct and validating that it matches your system’s invariants, that feels good to me. 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 na…

The countless bugs I've had to deal with and all the time I've lost fixing these bugs caused by people who forgot to validate data in a certain place or didn't realize they had to do so proves to me that the overhead of calling a get on a wrapper type is totally worth it. I value the hours wasted on diagnosing a bug far more than the extra keystrokes and couple of seconds required to avoid it in the first place.

No, you’ve achieved an illusion of that as now your spending hours wasted on discovering where a developer forgot to call NewUsername and instead called Username{“broken”}. I cant see the value in this abstraction in Go.

Re: How I write HTTP services in Go after 13 years

#138
post #107
post #73

I just run Go servers under fcgi. You get orchestration and crash recovery with a very simple interface. Fcgi will launch server processes as needed, feed them events, and shut it down when there's no traffic. Performance is good, and you can run on cheap hosting.

Which hosting do you use? I use fastcgi with python on Dreamhost and it works fine, but I’m sorta worried that they’ll turn it off because it seems kind of niche and under-documented

Dreamhost too. Dreamhost will let you run a continuously running process.

The amount of work you can get done on low-end shared hosting is really quite impressive.

Re: How I write HTTP services in Go after 13 years

#139
post #72

Earlier 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…

For what it's worth, I've always had an easier time combining WET code than untangling the knot than is too DRY code. Too little abstraction and you might have to read some extra code to understand it. Too much abstraction and no one other than the writer, and even then, may ever understand it.

Re: How I write HTTP services in Go after 13 years

#140
post #121

Earlier quoted context omitted.

There's a name for this anti-pattern: "Stringly typed"

This term is typically used to refer to things like data structures and numerical values all being passed as strings. I don't think a reasonable person would consider storing a username in a string to be "stringly typed".

It definitely is stringly typed. It's just that it's a very normalized example of it, that people don't think of as being an antipattern.

If you want to implement what Yaron Minsky described as "make illegal states unrepresentable", then you use a username type, not a string. That rules out multiple entire classes of illegal states.

If you do that, then when you compile your program, the typechecker can provide a much stronger correctness proof, for more properties. It allows you to do "static debugging" effectively, where you debug your code before it ever even runs.

Post reply on HN