_______ __ _______
| | |.---.-..----.| |--..-----..----. | | |.-----..--.--.--..-----.
| || _ || __|| < | -__|| _| | || -__|| | | ||__ --|
|___|___||___._||____||__|__||_____||__| |__|____||_____||________||_____|
on Gopher (inofficial)
URI Visit Hacker News on the Web
COMMENT PAGE FOR:
URI Go Naming Conventions: A Practical Guide
wpollock wrote 3 hours 21 min ago:
Allowing Unicode characters, then stating best practice is to stick
with ASCII, is weird. (Go is not alone in this practice.) Unicode
identifiers have a host of issues, such as some characters have no case
distinction, some have title-case but not uppercase, some "capitalize"
the last letter in a word and not the first (Hebrew has five "final
form" letters), etc. Does Go specify the meaning (exported or not) if
a letter has no case, or if an identifier starts with a zero-width
joiner character? Without a huge list of detailled rules, too much is
left to the implementation to decide. I prefer to stick with ASCII for
names.
Fun fact: When printing with movable type began, printers would travel
with large "type cases" containing the small wood or metal blocks with
glyphs on them. The ones the used frequently were kept in the lower
half of the case, in easy reach. That's where the terms "lowercase" and
"uppercase" come from.
arcxi wrote 1 hour 12 min ago:
it is weird, especially for Go with its semantic naming and famously
opinionated compiler. it will gladly build code with a variable named
ð¤çᥱᥲΣà©, but God forbid it's unused.
syngrog66 wrote 4 hours 18 min ago:
one rando set of opinions. stopped reading fast
Mawr wrote 6 hours 5 min ago:
> Words that are acronyms or initialisms (like API, URL or HTTP) should
use a consistent case within the identifier. So, for example, apiKey or
APIKey are conventional, but ApiKey is not. This rule also applies to
ID when it is used as shorthand for the words "identity" or
"identifier" â so that means write userID rather than userId.
Outdated.
Over time, it's become clear that breaking the camelCase convention in
this manner is inappropriate:
- The inconsistency with the convention is jarring, consider `APIURL` -
is that a variable (ApiUrl) or a constant (APIURL)?
- The inconsistency introduces doubt on how to write any given
identifier - which is why the above advice even needs to exist
- The whole point of the convention is to make separate parts of the
name visually separate, consider `someAPIURLHTMLJSONExtension` vs
`someApiUrlHtmlJsonExtension`
- It's hard to keep this consistent - we may reasonably disagree
whether `ID` should be capitalized or not, meaning you may just as well
find both `ID` and `id` across codebases. This erases the benefits of
capitalization altogether.
The benefits of keeping these acronyms capitalized are dubious and
don't outweigh the downsides.
And of course, the real solution is to use the one correct naming
convention - `snake_case`. Then you can capitalize all you want without
trouble - `some_API_URL_HTML_JSON_extension`.
menno-dot-ai wrote 6 hours 49 min ago:
This is great!
My team started using Go last year so I fed this article to set off a
fleet of agents on our Go codebase and generate a report out w/ code
samples based on it. Ended up with a pretty good little document to
present on Monday :)
graynk wrote 5 hours 8 min ago:
Why do you need AI agents for this?
You either catch and enforce it with a linter (e.g. [1] ) (in which
case you don't need AI to tell you the current state, you just add
the same config to all projects) or you don't enforce it (because
everyone will forget unless it's automated)
URI [1]: https://golangci-lint.run/docs/linters/configuration/#revive
voidfunc wrote 4 hours 52 min ago:
Using a linter doesn't get you noticed by leadership and net you a
promo.
graynk wrote 4 hours 1 min ago:
Sure it does, just say you "established org-wide coding standards
and drove adoption of automated linting tooling, reducing review
friction and enforcing style consistency at scale" in your
assessment.
ManuelKiessling wrote 2 hours 53 min ago:
Better yet, post about it on LinkedIn and explain what it
taught you about marriage proposals!
HumblyTossed wrote 7 hours 32 min ago:
People have been arguing this stuff since the dawn of (computer) time.
I don't get it. I've been at it so long now, IDGAF what or how you
name something. Short names in loops? Long? I don't care. I really
don't. Just be consistent in what you decide and I can read it.
All this arguing... FFS, go DO something with your time!
EDIT: Oh, yeah, as for the article itself, it's a good article. But
again, just be consistent in what you choose.
laserlight wrote 11 hours 19 min ago:
> we use the identifier p to represent a value in the people slice â
the range block is so small and tight that using a single letter name
is clear enough.
No, it's not. When you see `p.Age`, you have to go back and find the
body of the loop, see what it operates on and decipher what p stands
for. When you see `person.Age`, you understand it. I've never
understood what is gained by using `p` instead of spelling it out as
`person`.
giancarlostoro wrote 5 hours 14 min ago:
This comes from some dated idea for stuff like C where "its okay to
use shorthands for variables" but is it really? The only place I
allow it is simple iterators, but now we have enhanced loops where
even this is unnecessary. We don't need to save on pixel screen space
like if its still the 90s. Even with a simple 1080p monitor you can
fit plenty of words and code.
Give your variables, functions, classes meaningful descriptive names
that make sense to humans.
hackthemack wrote 5 hours 40 min ago:
But what if your codebase has to interact with leads, customers, and
another 3rd party system called Metrica with leads, customers?
When you write a loop, do you now name the variable
OurPerson.Age
MetricaPerson.Age
?
What if, 3 years from now, you include another 3rd party vendor into
the system and have to write code against that data and in the data
they name their stuff OurPerson.Age?
Not saying you are wrong at all. Just naming things is hard and
context dependent. I think that is why it is endlessly argued.
zahlman wrote 7 hours 30 min ago:
>you have to go back and find the body of the loop
If the loop is long enough that you don't naturally remember how it
was introduced, that's the problem. In the given example, the use of
`p.Age` is literally on the next line of code after ` for _, p :=
range people`.
> I've never understood what is gained by using `p` instead of
spelling it out as `person`.
Wisdom I received from, IIRC, the Perl documentation decades ago:
tightly-scoped names should be shorter and less attention-grabbing
than more broadly-scoped ones, because you should really notice when
you're using a global, and you don't want to suffer attention
fatigue. (I'm sure the exact wording was quite different.)
Also because it's better for information density. As I recall, Larry
Wall also had the idea that more commonly used language keywords
should be shorter than rare ones. Good code uses the locals much more
often than globals, so you shouldn't need to expend the same amount
of effort on them. (The limiting case of this is functional
programming idioms where you can eliminate the variable name
completely, in cases like (Python examples) `lambda x: int(x)` ->
`int`, or `(foo(x) for x in xs)` -> `map(foo, xs)`.
laserlight wrote 6 hours 17 min ago:
> remember how it was introduced
The problem is that many times I have not read the definition to
remember. Debugger puts me into a context where I have to figure
out what `p` stands for. I go up the call stack and now there's `s`
to be deciphered. Worse is the reuse of `p` for person, product,
part, etc. in different contexts.
Debugging is not the only problem. Code is read rarely linearly.
Many times I browse different uses of a function, or see how a data
structure is modified in different contexts. Looking up single
letter variables is just a waste of time.
gopher_space wrote 4 hours 50 min ago:
> Debugger puts me into a context where I have to figure out what
`p` stands for.
`p` stands for "the process in question".
I like to think of single-character vars as idea or topic headers
that track the single thing I'm currently up to. I'm rarely
working with more than one at a time, frequently it's the only
variable, and there are contexts where I wouldn't use them at
all.
IMHO if you're in a situation where `p` isn't obvious to you,
"something has gone wrong".
esrauch wrote 9 hours 21 min ago:
This is something that it seems some Go people just don't "believe"
in my experience, that for some people that letter in that context is
not mentally populated immediately.
It's honestly a shame because it seems like Go is a good language but
with such extremely opinionated style that is so unpleasant (not just
single letters but other things stuff about tests aren't supposed to
ever have helpers or test frameworks) feels aggressively bad enough
to basically ruin the language for me.
gadflyinyoureye wrote 5 hours 43 min ago:
I think the community is split on such things. I ended up telling
the side that gets persnickety about short names and only using if
statements in tests to pound sand. I use things that make my life
easier. I now care less about some rude rando on r/golang than I
did five years ago.
bborud wrote 9 hours 24 min ago:
If your loops are so long you can't fit them on one screenfull you
have much more fundamental issues.
laserlight wrote 8 hours 36 min ago:
person.Age is easier to understand than p.Age regardless of the
loop size.
mysterymath wrote 24 min ago:
Another point of view: ideally it would just be "Age". But in
languages that don't have the ability to "open" scopes, one might
be satisfied p.Age, being "the age". I've also seen $.age and
it.age, in languages with constructs that automatically break out
"it" anaphora.
zer00eyz wrote 8 hours 41 min ago:
You arent wrong, but it is not an absolute.
Furniture maker, house framer, finish carpenter are all under the
category of woodworking, but these jobs are not the same. Years of
honed skill in tool use makes working in the other categories
possible, but quality and productivity will suffer.
Does working in JS, on the front end teach you how to code, it sure
does. So does working in an embedded system. But these jobs might
be further apart than any of the ones I highlighted in the previous
category.
There are plenty of combinations of systems and languages where
your rule about a screen just isn't going to apply. There are
plenty of problems that make scenarios where "ugly loops" are a
reality.
bborud wrote 8 hours 8 min ago:
I didn't say it was an absolute. But once a scope grows to the
point where you have to navigate to absorb a function or a loop,
both readability and complexity tends to worsen. As does your
mental processing time. Especially for people who "scan" code
rapidly rather than reading it.
The slower "readers" will probably not mind as much.
This is why things like function size is usually part of coding
standards at a company or on a project. (Look at Google, Linux
etc)
jplona wrote 9 hours 28 min ago:
I think this is clearly a matter of preference. Shorter variable (or
rather, appropriately short variables for the context) for me are
easier to recognize and disambiguate. They take up fewer tokens, so
to speak. When I see `p.Age` I don't have to go back and look at the
beginning of the loop because I just read that line and I remember
it.
cookiengineer wrote 10 hours 39 min ago:
I agree with this comment so much.
Tried to use the new slices package or comparables? It's a nightmare
to debug, for no reason whatsoever. If they would've used interface
names like Slice or Comparable or Stringable or something, it would
have been so much easier.
The naming conventions are something that really fucks up my coding
workflow, and it can be avoided 100% of the time if they would stop
with those stupid variable names. I am not a machine, and there is no
reason to make code intentionally unreadable.
saghm wrote 10 hours 48 min ago:
I've felt strongly for a while now that abbreviations should be
"lossless" in order to be useful; it should be unambiguous now get
back to the unabbreviated form. For whatever reason, people seem to
love trying to optimize for character count with abbreviations that
actually make things more confusing (like `res` in a context where it
might mean either "response" or "result).
I just don't get the obsession with terseness when we have modern
tooling. I don't type particularly fast, but autocomplete makes it
pretty quick for me to type out even longer names, and any decent
formatter will split up long lines automatically in a way that's
usually sane (and in my experience, the times when it's annoying are
usually due to something like a function with way too many arguments
or people not wanting to put a subexpression in a separate variable
because I guess they don't know that the compiler will just inline
it) rather than the names being a few characters too many.
Meanwhile, pretty much everywhere I've worked has had at least some
concerns about code reviews either already being or potentially
becoming a burden on the team due to the amount of time and effort it
takes to read through someone else's code. I feel like more emphasis
on making code readable rather than just functional and quick to
write would be a sensible thing to consider, but somehow it never
seems to be part of the discussion.
ickyforce wrote 10 hours 7 min ago:
> and any decent formatter will split up long lines
Any decent editor can wrap long lines on demand. But it's even
better not to have to do either of those if not necessary.
> I've felt strongly for a while now that abbreviations should be
"lossless" in order to be useful
This is how we got lpszClassName. The world moved away from
hungarian notation and even away from defining types for variables
in some contexts (auto in cpp, := in Go, var in Java). Often it
just adds noise and makes it harder to understand the code at a
glance, not easier.
saghm wrote 2 hours 7 min ago:
I'd argue there's a stark difference between abbreviating words
and adding extra ones. `p` as a shorthand for `person` is silly
to me, but that doesn't mean that `personObject` would also be
silly to me. I fundamentally don't agree with the premise that
it's possible to be too verbose means that terseness is its own
goal; the goal should be clarity, and I don't think that lossy
abbreviations actually help with that except when someone already
knows what the code is doing, in which case they don't need to
read it in the first place.
piekvorst wrote 10 hours 49 min ago:
Long lines make reading rhythm uncomfortable (long jumps, prolonged
eye movements) and long words make the text too dense and slow down
the reading. Itâs bad typography.
I have heard an idea that a good variable should be understood by
just reading its name, out of context. That would make
âProductIndexâ superior to âiâ, which doesn't add any
clarity.
jayd16 wrote 8 hours 33 min ago:
> That would make âProductIndexâ superior to âiâ, which
doesn't add any clarity.
Adds a ton of clarity, especially if you have a nested loop.
Groxx wrote 8 hours 21 min ago:
and god help you if those loops are pairing People and Products.
though now that I write that out... it would be really nice if
you could optionally type iteration vars so they couldn't be used
on other collections / as plain integers. I haven't seen any
languages that do that though, aside from it being difficult to
do by accident in proof-oriented languages.
jayd16 wrote 7 hours 29 min ago:
You usually don't need an index that can't be used elsewhere.
If you don't then you can abstract it away entirely and use an
iterator or foreach features.
Groxx wrote 6 hours 46 min ago:
Depends on the language. Doing that is a huge pain in Go
(until fairly recently, and it's still quite abnormal or
closure-heavy), so the vast majority of code there does
manual index-pairing instead of e.g. a zip iterator when
going through two paired arrays.
bborud wrote 8 hours 54 min ago:
I think this may be related to how people read code. You have
people who scan shapes, and then you have people who read code
almost like prose.
I scan shapes. For me, working with people who read code is
painful because their code tends to to have less clear "shapes"
(more noise) and reads like more like a verbal description.
For instance, one thing I've noticed is the preference for "else
if" rather than switch structures. Because they reason in terms of
words. And convoluted logic that almost makes sense when you read
it out loud, but not when you glance at it.
This is also where I tend to see unnecessarily verbose code like
func isZero(a int) bool {
if a == 0 {
return true
} else {
retur false
}
}
strictly speaking not wrong, but many times slower to absorb. (I
think most developers screech to a halt and their brain goes "is
there something funny going on in the logic here that would
necessitate this?")
I deliberately chose to learn "scanning shapes" as the main way to
orient myself because my first mentor showed me how you could
navigate code much faster that way. (I'd see him rapidly skip
around in source files and got curious how he would read that fast.
Turns out he didn't. He just knew what shape the code he was
looking for would be).
zer00eyz wrote 8 hours 12 min ago:
> I think this may be related to how people read code. You have
people who scan shapes, and then you have people who read code
almost like prose.
I think this is an astute observation.
I think there is another category of "reading" that happens, is
what you're reading for "interaction" or "isolation".
Sure c.method is a scalable shape but if your system deals with
Cats, Camels, Cars, and Crabs that same c.method when dealing
with an abstract api call divorced from the underlying
representation might not be as helpful.
I would think that we would have more and better research on
this, but the only paper I could find was this: [1] its a meta
analysis of 57 other papers, a decent primer but nothing ground
breaking here.
> I scan shapes. ... verbal description.
I would be curious if you frequently use a debugger? Because I
tend to find the latter style much more useful (descriptive) in
that context.
URI [1]: https://arxiv.org/pdf/2110.00785
functional_dev wrote 7 hours 29 min ago:
The shape argument works well in small packages but it starts
to fail once you have multiple domain models starting with the
same letter
bborud wrote 3 hours 38 min ago:
I wasnât talking about just symbols but entire paragraphs
of code as well.
bborud wrote 7 hours 40 min ago:
dealing with an abstract api call divorced from the underlying
representation
I don't understand what you mean. Could you give me an
example?
I would be curious if you frequently use a debugger?
I practically never use a debugger.
ConcurrentCrab wrote 8 hours 30 min ago:
I think this is pretty insightful, and I might add this as
another reason LLM code looks so revolting. It's basically
writing prose in a different language, which make sense - it's a
_language_ model, it has no structural comprehension to speak of.
Whereas I write code (and expect good code to be written) such
that most information is represented structurally: in types,
truth tables, shape of interfaces and control flow, etc.
diath wrote 9 hours 26 min ago:
Something like "AnIteratorObjectWithPersonPointer" would be a long
word, "person" is absolutely not. If a 6 letter identifier causes
you that much trouble with code being too verbose, then it's likely
a screen resolution/density/font issue, not a naming issue.
> That would make âProductIndexâ superior to âiâ, which
doesn't add any clarity.
And then you introduce extra two levels of nested loops and
suddenly "i", "j", and "k" don't make any sense on their own, but
"ProductIndex", "BatchIndex" and "SeriesIndex" do.
bborud wrote 8 hours 27 min ago:
And then you introduce extra two levels of nested loops and
suddenly "i", "j", and "k" don't make any sense on their own, but
"ProductIndex", "BatchIndex" and "SeriesIndex" do.
ijk for indices in loops are actually clearer than random names
in nested loops precisely because it is a *very common
convention* and because they occur in a defined order. So you
always know that "j" is the second nesting level, for instance.
Which relates to the visual layout of the code.
You may not have known of this convention or you are unable to
apply "the principle of least astonishment". A set of random
names for indices is less useful because it communicates less and
takes longer to comprehend.
Just like most humans do not read text one letter at a time, many
programmers also do not read code as prose. They scan it rapidly
looking at shapes and familiar structures. "ProductIndex",
"BatchIndex" and "SeriesIndex" do not lend themselves to
scanning, so you force people who need to understand the code to
slow down to the speed of someone who reads code like they'd read
prose. That is a bit amateurish.
zahlman wrote 7 hours 27 min ago:
> ijk for indices in loops are actually clearer than random
names in nested loops precisely because it is a very common
convention and because they occur in a defined order. So you
always know that "j" is the second nesting level, for instance.
Which relates to the visual layout of the code.
In problem domains that emphasize multidimensional arrays, yes.
More often nowadays I would see `i` and think "an element of
some sequence whose name starts with i". (I tend to use `k` and
`v` to iterate keys and values of dictionaries, but spell
`item` in full. I couldn't tell you why.)
Groxx wrote 8 hours 17 min ago:
I partly agree, and partly don't. When ijk really is
unambiguous and the order is common (say you're implementing a
well-known algorithm) I totally agree, the convention aids
understanding.
But nesting order often doesn't control critical semantics.
Personally, it has much more often implied a heuristic about
the lengths or types (map, array, linked list) of the
collections (i.e. mild tuning for performance but not
critical), and it could be done in any order with different
surrounding code. There the letters are meaningless, or
possibly worse because you can't expect that similar code
elsewhere does things in the same nesting order.
This likely depends heavily on your field though.
bborud wrote 7 hours 51 min ago:
I think I know what you mean. Let's assume a nesting
structure like this:
Company -> Employee -> Device
That is, a company has a number of employees that have a
number of devices, and you may want to traverse all cars. If
you are not interested in where in the list/array/slice a
given employee is, or a given device is, the index is
essentually a throwaway variable. You just need it to address
an entity. You're really interested in the Person structure
-- not its position in a slice. So you'd assign it to a
locally scoped variable (pointer or otherwise).
In Go you'd probably say something like:
for _, company := range companies {
for _, employee := range company.Employees {
for _, device := range employee.Devices
// ..do stuff
}
}
ignoring the indices completely and going for the thing you
want (the entity, not its index).
Of course, there are places where you do care about the
indices (since you might want to do arithmetic on them). For
instance if you are doing image processing or work on dense
tensors. Then using the convention borrowed from math tends
to be not only convenient, but perhaps even expected.
Joker_vD wrote 9 hours 34 min ago:
A good variable name is the one that is understood by reading it in
context, which is why you don't have names like "current_person" or
"CurrentIndexOfProductBeingUpdated".
monooso wrote 9 hours 52 min ago:
I would argue that ambiguity and uncertainty slow down reading, and
more importantly comprehension, far more than a few additional
characters.
lokar wrote 8 hours 2 min ago:
It depends on whom you are optimizing for. Someone who knows the
language, but not this system/codebase, or someone who works in
this area often?
qezz wrote 12 hours 39 min ago:
I was surprised to see literally invalid names in the "bad" section,
e.g. "Cannot start with a digit". Why even presenting this if it's
rejected by the compiler?
alex_edwards wrote 8 hours 6 min ago:
Author here. The answer is because I mentioned it as one of the
bullet pointed hard-rules, and I wanted to include an example to
illustrate it.
pphysch wrote 8 hours 44 min ago:
"Chat, generate me a table of bad Golang making practices"
alexfoo wrote 8 hours 53 min ago:
I wondered if you could sneak in some unicode digit but it seems to
reject those too:
$ go run z.go
# command-line-arguments
./z.go:6:2: identifier cannot begin with digit U+0661 'Ù¡'
./z.go:7:27: identifier cannot begin with digit U+0661 'Ù¡'
(I tried a few of them but not all.)
0x696C6961 wrote 12 hours 18 min ago:
The example with the dash in it confused me as well.
nasretdinov wrote 12 hours 51 min ago:
I like this article â short, accurate (which is somehow not a given
these days...) and useful, just like Go language itself.
red_admiral wrote 13 hours 56 min ago:
Another of mine: don't name a struct after an interface method that
it's supposed to implement. If you have a package linearalgebra, then
making a custom error type linearalgebra.LinearAlgebraError is too
"chatty" but linearalgebra.Error will cause you pain if it implements
"Error string()", as it probably should, and you decide to make a
linearalgebra.MatrixSingularError that wraps a linearalgebra.Error to
"inherit" its methods.
In the end, it ended up called linearalgebra.Err .
P.S Alex Edwards' "let's go" and "let's go further" are great books to
get someone up to date with golang, just keep an eye on features that
are newer than the book(s).
DauntingPear7 wrote 12 hours 52 min ago:
The booms receive regular updates. I got an email about an update to
Letâs Go Further on 3/12 for Go 1.26
DIR <- back to front page