[HN Gopher] Go Naming Conventions: A Practical Guide
       ___________________________________________________________________
        
       Go Naming Conventions: A Practical Guide
        
       Author : yurivish
       Score  : 92 points
       Date   : 2026-03-24 20:49 UTC (4 days ago)
        
 (HTM) web link (www.alexedwards.net)
 (TXT) w3m dump (www.alexedwards.net)
        
       | red_admiral wrote:
       | 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:
         | The booms receive regular updates. I got an email about an
         | update to Let's Go Further on 3/12 for Go 1.26
        
       | nasretdinov wrote:
       | I like this article -- short, accurate (which is somehow not a
       | given these days...) and useful, just like Go language itself.
        
       | qezz wrote:
       | 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?
        
         | 0x696C6961 wrote:
         | The example with the dash in it confused me as well.
        
         | alexfoo wrote:
         | 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 '1'         ./z.go:7:27: identifier
         | cannot begin with digit U+0661 '1'
         | 
         | (I tried a few of them but not all.)
        
         | pphysch wrote:
         | "Chat, generate me a table of bad Golang making practices"
        
         | alex_edwards wrote:
         | 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.
        
       | laserlight wrote:
       | > 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`.
        
         | piekvorst wrote:
         | 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.
        
           | monooso wrote:
           | I would argue that ambiguity and uncertainty slow down
           | reading, and more importantly _comprehension_ , far more than
           | a few additional characters.
        
             | lokar wrote:
             | 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?
        
           | Joker_vD wrote:
           | 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".
        
           | diath wrote:
           | 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:
             | _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.
        
               | Groxx wrote:
               | 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:
               | 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.
        
               | zahlman wrote:
               | > 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.)
        
           | bborud wrote:
           | 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).
        
             | ConcurrentCrab wrote:
             | 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.
        
             | zer00eyz wrote:
             | > 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:
             | https://arxiv.org/pdf/2110.00785 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.
        
               | bborud wrote:
               | _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.
        
               | functional_dev wrote:
               | 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:
               | I wasn't talking about just symbols but entire paragraphs
               | of code as well.
        
           | jayd16 wrote:
           | > 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:
             | 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:
               | 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:
               | 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.
        
         | saghm wrote:
         | 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:
           | > 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:
             | 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.
        
         | cookiengineer wrote:
         | 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.
        
         | jplona wrote:
         | 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.
        
         | bborud wrote:
         | If your loops are so long you can't fit them on one screenfull
         | you have much more fundamental issues.
        
           | zer00eyz wrote:
           | 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:
             | 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)
        
           | laserlight wrote:
           | person.Age is easier to understand than p.Age regardless of
           | the loop size.
        
         | esrauch wrote:
         | 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:
           | 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.
        
         | zahlman wrote:
         | >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:
           | > 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:
             | > 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".
        
         | hackthemack wrote:
         | 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.
        
         | giancarlostoro wrote:
         | 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.
        
       | HumblyTossed wrote:
       | 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.
        
       | menno-dot-ai wrote:
       | 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:
         | Why do you need AI agents for this?
         | 
         | You either catch and enforce it with a linter (e.g.
         | https://golangci-lint.run/docs/linters/configuration/#revive)
         | (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)
        
           | voidfunc wrote:
           | Using a linter doesn't get you noticed by leadership and net
           | you a promo.
        
             | graynk wrote:
             | 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:
               | Better yet, post about it on LinkedIn and explain what it
               | taught you about marriage proposals!
        
       | Mawr wrote:
       | > 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`.
        
       | syngrog66 wrote:
       | one rando set of opinions. stopped reading fast
        
       | wpollock wrote:
       | 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.
        
       ___________________________________________________________________
       (page generated 2026-03-28 23:01 UTC)