Live data from Hacker News

Go Style

google.github.io

161–170 of 212 posts

Re: Go Style

#161

Earlier quoted context omitted.

This advice should be universal in coding. When I first started programming, I had a manager that hated 1-2 character variables. But they make sense for loop iterators.

When I started coding I'd type 10 FOR F = 1 TO 10 20 PRINT F, F*F 30 NEXT F Because "FOR" and "F" shared the same key, on my ZX Spectrum keyboard.

And for those that don't know - entering Basic code on the Spectrum (well the original one at least) was ALL single/shifted key shortcuts for keywords rather than typing them out.

eg https://www.old-computers.com/museum/photos/sinclair_zx-spec...

The low cost and quirky masochism was mainly why we loved it.

Re: Go Style

#162
post #60

I found the following statement in the Maintainability section interesting: > Maintainable code minimizes its dependencies (both implicit and explicit). Depending on fewer packages means fewer lines of code that can affect behavior. Avoiding dependencies on internal or undocumented behavior makes code less likely to impose a maintenance burden when those behaviors change in the future. Obviously this guide was writte…

There's definitely a balance to strike.

On the one hand, you don't want to be constantly re-inventing the wheel. If someone has made a great package for doing a specific thing you need (printing data in tables, making ergonomic http requests that cover edge cases, reading an epub file, etc), then IMO you're better off using theirs. They care about it, they've tested it, and the implementation of it is one less thing you have to think about it. If you consider that every line of code that you write/maintain is a liability, then offloading that to others is very convenient.

On the other hand, external packages typically come without guarantees. Their owner/maintainer could lose interest, get hit by a bus, get hacked, anything. You're running their code in your projects and suddenly, all this uncontrolled code becomes the liability. You'd do best to do _everything_ in house in order to limit your external risk.

Ultimately, I think there's no single right answer here. Sure, you probably shouldn't add dependencies for things like "left-pad", but the line is a little blurrier for things like npm's "is-promise". It sounds simple enough (and the implementation [0] is only a 3-line function), but I'm unlikely to have written it correctly if I did it from scratch. Plus, it's tested! And lastly, I think your risk is lower the larger an external dependency is. Large projects, like React or Django, are much more upside than liability; it's everything in the medium range that you really need to consider.

[0]: https://github.com/then/is-promise/blob/ec9bd8a3f576324a1343...

Re: Go Style

#163

Earlier quoted context omitted.

Hiding errors like that is going to make it hard to figure out what is going on as more and more wrappers like this pile up. If time.Parse is failing, you either have bad input or a bug, right? If you are ok with this failing without even logging the error, something might be wrong with the design.

Hiding errors saves less than a second of typing, at the cost of making every five minute bugfix a one day bugfix. Don't do it. You can't build your own programming language inside of Go. If you absolutely cannot mentally handle functions returning an error and you having to type "if err != nil { fix the problem }", you really need to find a different programming language. But, errors happen all the time, and handlin…

Plus, even if you just return an error/bubble up an exception, that is probably better than bubbling up a nil/null value for some of the consuming code to try to dereference.

Re: Go Style

#164

> Go interfaces generally belong in the package that consumes values of the interface type, not a package that implements the interface type. The implementing package should return concrete (usually pointer or struct) types. I like this rule. Most companies violate it everywhere. There are good times to ignore it but I always push for func NewThing To return something other than the interface type. The last Go interv…

I prefer to make the implementation package-private and hide it behind an interface if there's no way to make the implementation's zero value useful. Doing so prevents someone from trying to use it without calling the (mandatory) constructor.

Re: Go Style

#165

In my opinion the hardest style rules to accept when trying to use this guide are: 1. Do not create "assertion libraries" like `assertEqual(x, y)` [1] 2. Leave testing to the Test function [2] 3. Intialisms (HTTPURL, IOS, gRPC) [3] 4. Function formatting [4] For the record I'm not saying I disagree with these. I just think that folks coming from other languages have a lot of built in muscle memory to do it other ways…

I've read the assertions section a few times and I still don't understand the argument. How is: if got == nil { t.Errorf("blog post was nil, want not-nil") } Better than assert.NotNil(t, got, "blog post") ? They seem to suggest that you lose context, but their "Good" examples are similarly devoid of context.

Drawing inspiration from:

> Complex assertion functions often do not provide useful failure messages and context that exists within the test function.

I think the best compromise is to avoid combining individual logical units into one assertion. For example:

Bad:

  assert.True(t, myStr == "expected" && myInt == 5)
Good:

  assert.Equal(t, "expected", myStr)
  assert.Equal(t, 5, myInt)
Real world example: at my workplace we have some code that tests HTTP request formation for correctness (right URL, body, headers, etc). Replacing big all-or-nothing booleans with individual assertions on each property of the request provides much more useful test failure messages.

As with any published "best practices" like this, have an open mind but don't just cargo cult whatever Google does. Best to be selective about what does and doesn't work for your situation.

Re: Go Style

#166

Earlier quoted context omitted.

Hiding errors saves less than a second of typing, at the cost of making every five minute bugfix a one day bugfix. Don't do it. You can't build your own programming language inside of Go. If you absolutely cannot mentally handle functions returning an error and you having to type "if err != nil { fix the problem }", you really need to find a different programming language. But, errors happen all the time, and handlin…

Plus, even if you just return an error/bubble up an exception, that is probably better than bubbling up a nil/null value for some of the consuming code to try to dereference.

Bubbling up is a great option. It is such a good opportunity to capture relevant context that the caller doesn't know about; like the internal state of the method, and the "why" behind making the call. If everyone does this, you can end up with an error message like "handle /ping: health check servers: ping server 1.2.3.4 (3/42): i/o timeout" instead of just "i/o timeout" (which you get if you just lazily "return err"). You see the first error message, and you know you need to adjust the ping function to treat a timeout as a part of the response. You see the second one, and all you can do is scratch your head. It could be anywhere.

(BTW, stack trace proponents... stack traces don't capture loop iterator variables, or "why" you're calling a particular function. But when you write a quick error message with fmt.Errorf, you can include all of those things.)

Software engineering is a job of continuous improvement. Make it really easy to find the right place to target improvements!

Re: Go Style

#167

The kubernetes ecosystem has a lot of go code which consequentially suffer from nil panics. Thankfully they recover otherwise we'd see an absolute shit ton of pod restarts. In general, please stop panicking in library Go and Rust code. It's rude.

Yes! This is one of the tiny few things I don‘t like about rust. In a language so focused on safety, why can anything panic? Every single failable function should return result. No ifs or buts.

Re: Go Style

#168

Earlier quoted context omitted.

I don't believe so. Imagine you're new to Go and you haven't mentally mapped all of these abbreviations. What would you rather read, `r` or `reader`?

Designing an API that "stutters" is a very common mistake that many programmers make. reader.Read() is pretty jarring. There is also nothing wrong with naming variables after what they're for instead of what they are. Consider io.Copy(dst, src). That's nicer than io.Copy(reader1, reader2).

But not necessarily nicer than io.Copy(destination, source).

Re: Go Style

#169
post #107
post #64

Earlier quoted context omitted.

You're assuming that the code was written exception safely. E.g. mu.Lock() foo := bar[baz] // Go is sold as a language without exceptions, so people don't write exception-safe code. Which is fine, except when exceptions are actually caught.

That wouldn't pass a code review where I work... Use a defer to do the unlock

I would also not allow it. I'm saying the problem is that core Go developers say "Go doesn't have exceptions", which is manifestly false, but causes people to not write exception safe code.

But despite you and me, I'm saying there's a lot of broken code out there because of this doesn't-but-actually-does misinformation.

And it's very annoying that you have to tell people to do:

    var i int
    func() {
      mu.Lock()
      defer mu.Unlock()
      i = foo[bar]
    }()
Clean code, that is not. (even if you simplify it by having the lambda return the int)

Re: Go Style

#170
post #69

Earlier quoted context omitted.

This makes me wonder: what if there was a language where variable names are determined according to the type, with the option of overriding with a custom name. So a variable of type http.Request would automatically be named “req”, the next one in scope would be “req2”, etc. If you think about it, when you solve a physics problem, for instance, you call every mass “m1”, “m2”, etc. Maybe this would be another step in G…

I agree that most types come with a natural variable name. However, in many cases a more descriptive name is way appropriate. On top of my mind: - Multiple variables of the same type. How are you supposed to distinguish between req and req2? Compare it to something like "apiReq" and "cdnReq" - Primitive types, that does not inherently carry a domain value. An integer called "seconds" or "max_offset" has a lot more me…

You’re right. Going back to my physics example, all the variables in a physics problem are actually of the same type, float. So you’d need more specific types, but that would be infeasible-the whole point of types in a general purpose language is that they’re general enough for any use case.

If you were just coding physics problems you could have types restricted to the physics domain, that is, physical units. Which it looks like MATLAB does now support: https://www.mathworks.com/help/symbolic/units-of-measurement...

Post reply on HN