Go Naming Conventions: Avoid the 'What's This?' in Every Code Review
Go Naming Convention is especially important for larger projects where multiple developers may be working on the code simultaneously…
Sticking to naming conventions really does matter. When done right, it makes your code clear and easy to update and it’s even more crucial in bigger projects where many developers are involved.
It ensures that everyone, whether a newbie or a pro, can dive in, understand and contribute.
Before we dive in, it’s important to remember that conventions come from team decisions.
Changing them just because you’ve heard some new ideas might not always be best and what really makes conventions effective is everyone on your team, or in your company, being consistent.
So, if you think about making changes, always talk to everyone and get their input.
1. Package Naming
lowercase
When you’re deciding on a package name, always use lowercase. If you find yourself with a two-word name, simply put them together without adding dots or underscores.
This way of naming is in line with Go’s standard.
Avoid generic names
Ever opened a project and found yourself lost in a util
or common
package? They’re like those big boxes where we throw all kinds of stuff, hoping to sort them out later, but never really do
If you’ve seen a util
package overflowing with numerous files, you understand the struggle and for our newer teammates, navigating such a package can be a real puzzle.
Domain-specific naming
Here’s a thought: Why not name packages by their function? Words like parser
, validator
, or connector
directly hint at their role and this offers immediate clarity on its purpose.
Singular
While naming, always lean towards the singular form. So, it’s ‘user’ instead of ‘users’ (unless this package contain collection of users).
// CAN BE BAD
package users
// BETTER
package user
“Hmph, ever noticed the built-in packages with plurals like bytes, errors, strings?”
Well, they have a reason, they can’t use names like byte
, error
or string
because Go already uses those words (reserved keywords).
Name collision
Package names are usually short, so it’s easy to accidentally use the same name twice. If you have two packages with the same name but they do different things, that’s a recipe for bad design.
package util // <--- mathutil
package util // <--- stringutil
Think about when you want to use a util
package, and your IDE shows 3 or 4 choices and it gets even more tricky if you want to use two util
packages at the same file.
2. Variable name
camelCase
When it comes to un-exported or local variables, camelCase is the way to go, think seasonPoints
or userItem
.
It’s like how we sometimes shorten names in casual conversations, “Robert” becomes “Rob”, “Alexander” might become “Alex”.
Descriptive
Your variable name should tell you something about what it’s used for. Skip one-letter names, they won’t help anyone trying to read your code.
Short but sweet
Now, while you want descriptive names, sometimes if a variable is just around for a short time or in places where it’s common (its context), a shorter name works.
For instance, i
for loop indices?
for i := 0; i < 10; i++ { /*...*/ }
Constant
C/camelCase: think of them a bit like formal titles. You use camelCase, adjusting the first letter based on whether you want others outside the package to see it.
So, it’s either Pi
or MongoURL
.
const Pi = 3.14159
const MongoDBURL = "mongodb://localhost"
“Hey, with previous programming experience, all my constants are in UPPER_CASE. Should I adjust them to match the best practice?”
If you have a lot of code written in that style and changing it seems challenging, then it’s okay to keep it and the most important thing is to stay consistent. If everyone in your team is using UPPER_SNAKE_CASE, then stick with it.
Issues arise when members change styles without informing the team.
3. Exported and un-exported
Anything you want accessible outside the current package or struct, start with an uppercase letter. If it’s meant to stay private, tucked away within the package or struct, begin with a lowercase.
Understanding the Context
The name of a variable should paint a clear picture of its role in the bigger scheme of things. Let’s take the User
struct as an example, using ID
inside this context is clearer than userID
.
// BAD: user.UserID
type User struct {
UserID string
}
// BETTER: user.ID
type User struct {
ID string
}
Un-exported global var/ const
In Go, you usually start un-exported variables and constants with a lowercase letter.
“But hold on, doesn’t this make un-exported global variables look like local ones? They both use camelCase, like
mongoURL
.”
That’s a good point and this can be confusing.
So, what’s my solution? I add a little underscore at the start of un-exported global variables and constants and this way, “_mongoURL” is easier to identify.
const (
_requiredWords = 16
_requiredName = true
)
This helps tell them apart easily.
4. Function
C/camelCase
In Go, internal functions should stick to camelCase, like fetchUserDetails
or loadConfig
.
For those functions that you’d like to expose to other packages, start with an uppercase: FetchUserDetails
or LoadConfig
.
Action First
You’ve probably spotted that my function names often begin with a verb and that’s deliberate. Starting with a verb makes the purpose of the function clearer at first glance.
“So, if I’ve got a
UserRepository
struct, would you name the function receiverrepo.Get()
orrepo.GetUser()
?”
For me? It’s Get()
.
“But Phuong, what if there’s both
GetUser()
andGetUsers()
? Would you chooseGet
andGets
?”
Not really.
User
and Users
can be confusing due to the mere “s” difference, so instead, I’d lean towards Get()
and GetMany()
, or maybe even GetOne()
and Get()
.
5. File Name
snake_lower_case
When I name my files, I prefer snake_case. It’s readable, quick to type, and aligns well with Go’s recommendations.
suffix “_test”
Files meant for tests or benchmarks should have the “_test” suffix. It keeps your test code separate, ensuring it doesn’t clutter your main code.
Domain separation
If your package contains multiple files, give them descriptive names: user_api_handlers.go
or user_db_operations.go
are good examples.
And a small piece of advice, avoid using special characters in file names, they can sometimes cause hiccups.
6. Acronyms
Avoid abbreviations
It’s tempting to use abbreviations like SSP or ADU for short name, but it can make code maintenance tricky.
“Wait, what does SSP or ADU even mean?”
You don’t know what they are? Me neither.
It’s best to avoid such abbreviations unless they are widely recognized in your field
Exceptions Do Exist
However, when it comes to acronyms that have been universally accepted like “URL” or “HTML”, you’re in the clear. They are entirely fine to use as they are widely recognized.
var URL string
var HTMLContent string
UPPERCASE
In Go, the norm is to have these well-known acronyms in all-uppercase, like above URL
or HTML
.
This is a bit different compared to other languages such as Java or JavaScript, where you might see these acronyms in camelCase, such as html
or mediumUrl
(in Go: mediumURL
).
And here’s something to ponder: “Would you choose ID or Id?”
I tend to lean towards ID, despite it being a departure from conventions in other languages I’ve used. What about you?
What’s crucial is not my preference, but your team’s. Align with your team’s conventions, be it userId or userID, just ensure consistency and everything will fall into place.
7. Error
Start With “Err”
When it comes to naming an error variable, initiate it with “Err…” and follow it up with a capitalized word, such as ErrInvalidInput
or ErrNotFound
.
var ErrConnectionFailed = errors.New("connection to database failed")
This practice aids in maintaining clarity in your code.
Jumping between languages in a project can be confusing, right? I’ve found that each programming language has its own way of doing things, especially when it comes to naming.
And by sticking to the conventions of the language you’re using and keeping a consistent style, it makes everything clearer and easier to handle.
Trust me, it helps a lot.
Such a good blog, thank you so much