Live data from Hacker News

Practical Go: Real-world advice for writing maintainable Go programs

dave.cheney.net

121–130 of 237 posts

Re: Practical Go: Real-world advice for writing maintainable Go programs

#121
post #35

> Naming the Config parameter config is redundant. We know its a Config, it says so right there. > In this case consider conf or maybe c will do if the lifetime of the variable is short enough. This seems petty. Is it really that problematic to type out a few extra characters?

Yeah, I used to go by this advice, and I found the maintainability of my code dramatically increased when I typed out full names. I don't even use "i" for loop variables anymore. If the length is a problem, invest in an editor with autocomplete. Well-known abbreviations are fine, like "iter" and "prev", but single-letter variable names notoriously impede readability for me.

I think 'k' and 'v' are fine for iterating over maps.

Re: Practical Go: Real-world advice for writing maintainable Go programs

#122

Earlier quoted context omitted.

I generally find if you need multi letter variable names it means your function or scope is to long, or manipulating to many things. It's a nice little red flag for me. Up to four or five single letter variables is pretty trivial to remember. Especially when three of those are i, j, and k. More than 6 or 7 rapidly becomes painful. But if you are manipulating 6 or 7 variables _in the same scope_ you are doing to much.…

I could not disagree more. When I'm skimming code, I want to immediately know what a variable means. I don't want to go cross-reference elsewhere.

If you have to go "cross-reference" elsewhere you are modifying something "to far away" from you. That's a giant sign of spaghetti code.

Also names vary with there contextual scope. Larger scopes mean longer names generally. Russ Cox gives a succinct description here: https://research.swtch.com/names

  A name's length should not exceed its information content.
  For a local variable, the name i conveys as much information as 
  index or idx and is quicker to read. Similarly, i and j are a 
  better pair of names for index variables than i1 and i2 
  (or, worse, index1 and index2), because they are easier to
  tell apart when skimming the program. Global names must convey
  relatively more information, because they appear in a larger
  variety of contexts. Even so, a short, precise name can say more
  than a long-winded one: compare acquire and take_ownership. 
  
  Make every name tell.
Variables should generally have short scope (we don't want a lot of global, or even package level variables). So _variable names_ in particular should be short.

Re: Practical Go: Real-world advice for writing maintainable Go programs

#123
post #35

> Naming the Config parameter config is redundant. We know its a Config, it says so right there. > In this case consider conf or maybe c will do if the lifetime of the variable is short enough. This seems petty. Is it really that problematic to type out a few extra characters?

It says so in the signature, but Go code tends to have long functions, so it's kind of a fallacy to say that it's going to be easily recognizable 50 lines in because it's in the signature. Now if this were Haskell and it was a single small expression, it would be different.

Re: Practical Go: Real-world advice for writing maintainable Go programs

#124

Earlier quoted context omitted.

I believe you've misunderstood this. A function can be very long and do only one thing or very short and do many things. The function: func ManyThing(i int) int { fmt.Println(i) return i+1 } does two things, and it's two lines long. The function tcp_send_message_locked ( https://github.com/torvalds/linux/blob/master/net/ipv4/tcp.c... ) does one thing at it's 261 lines long. Shorter code is _indicative of_ orthogonali…

My critique is of single-caller functions as a documentation device, not shorter functions.

But he doesn't advocate that... anywhere. He advocates factoring out orthogonal code to get shorter functions.

Re: Practical Go: Real-world advice for writing maintainable Go programs

#125

> 2.3. Don’t name your variables for their types What's the best way to handle a situation where you use two different types for the same data? e.g. var usersMap map[string]*User var usersList []*User

You could use a struct with private fields, e.g.

  type Users struct{
    dict map[string]*User
    list []*User
  }
and attach methods to both access the data and coordinatedly update both forms, e.g.

  func (us *Users) byID(id string) *User { ... }
  func (us *Users) sortedByID() []*User { ... }
  func (us *Users) addUser(id string, nu *User) {
    dict[id] = nu
    list = append(list, nu)
    ...
  }

Re: Practical Go: Real-world advice for writing maintainable Go programs

#126

Earlier quoted context omitted.

I took issue with this because it's a conventional wisdom, and does a fair bit of damage. Single-caller functions attract other callers over time, gain backwards-incompatible features, and result in regressions.

This is one reason I like nested functions. They’re not available to the surrounding scope so they don’t succumb to these weaknesses, while also allowing you to organize your very long function internally by task. I use ‘em in Python all the time. It’s a bummer that more languages don’t support them, though you can get there with lambdas too, sometimes at the cost of more syntax.

GNU C supports nested functions as an extension. I use them for the exact reasons you mention.

Re: Practical Go: Real-world advice for writing maintainable Go programs

#127
post #35

> Naming the Config parameter config is redundant. We know its a Config, it says so right there. > In this case consider conf or maybe c will do if the lifetime of the variable is short enough. This seems petty. Is it really that problematic to type out a few extra characters?

Also in this case Go makes you use types in the name since it doesn't allow function overloading.

Re: Practical Go: Real-world advice for writing maintainable Go programs

#128

This is so fortuitous as I started writing my first real Golang service and as a python dev I have no idea what I am doing. But one refreshing thing is how opinionated the language and the frameworks are refreshing as there’s only one acceptable way to do many things.

I have found this advice spot-on for more subtle questions, over my last two years of learning and building with Go full time:

https://golang.org/doc/effective_go.html

Also see back issues of the Go team blog for insight into concrete implementation realities of slices, interface types, GC and more.

Re: Practical Go: Real-world advice for writing maintainable Go programs

#129

Earlier quoted context omitted.

This is one reason I like nested functions. They’re not available to the surrounding scope so they don’t succumb to these weaknesses, while also allowing you to organize your very long function internally by task. I use ‘em in Python all the time. It’s a bummer that more languages don’t support them, though you can get there with lambdas too, sometimes at the cost of more syntax.

A bit of our “heritage” in programming destroyed by the C family of programming languages Pascal, like Algol, had nested subroutines for decomposing longer operations without leaking the details. Nested functions is one of the things I like about JavaScript as well.

C has static functions for decomposinglo ger operations without leaking the details.

For better or for worse, taking advantage of that forces you to keep source files fairly short and cohesive in functionality.

Re: Practical Go: Real-world advice for writing maintainable Go programs

#130
post #35

> Naming the Config parameter config is redundant. We know its a Config, it says so right there. > In this case consider conf or maybe c will do if the lifetime of the variable is short enough. This seems petty. Is it really that problematic to type out a few extra characters?

It can be a problem with Config comes from package `config`.
Post reply on HN