Name Conventions In Go
Summary: The ground rules for naming in Go are: use mixedCaps, keep it short and be consistent.
As a developer, naming is what we do every day, although it is quite easy most of the time, sometime it's really hard to come up with a good name, especially for fresher developers who are still struggling learning the languages. Some developers end up using i
, j
, v
,... for almost everything. Those are good names in some cases but can be very confusing if being used in an inappropriate context.
Some time developers just want to name their code their own way, this is OK if you work on your side project. But in a project which involve multiple developers, the standard must be respected. Everyone in a same project must follow the same standard, otherwise your code would like like a mess and become super hard to maintain.
In this post, I would like to provide some simple rules so that you can name your code easier and avoid endless arguments with your colleagues about naming conventions.
This guideline can be used as a supplement to the name conventions of the Go guideline at effective-go.
Project Name #
Name your project in lowercase, otherwise it would look really weird in the imports. Logrus - a very good structured logging library in Go made this mistake at the beginning and they had to fix it a very hard way. You still can see their README mention about the naming problem today.
The import github.com/sirupsen/logrus
is much better than github.com/Sirupsen/logrus
If your project name is long, use dash or underscore to separate the name. Use effective-go
or effective_go
instead of effectiveGo
or EffectiveGo
.
Package #
For package, use singular noun instead of plural. Use user
instead of users
, notification
instead of notifications
.
Use plural noun in case using singular noun causes collision with existing primitive type names. But this should be used with very careful considerations. Some examples from the standard libraries are bytes
, errors
.
If your package adds some extension to the current standard libraries, try to use another name instead of using the same name and fool yourselves with import alias. Use contextutil
or contextx
instead of context
, use errorsx
instead of errors
so that you can use both the packages in your code without defining alias for your imports.
Try to keep package name short, use lower case, never use mixedCaps for package name and even snake_case should be avoided too. Use contextutil
instead of contextUtil
or context_util
. This is similar why Go name its httputil
package, and not httpUtil
or http_util
.
File Name #
For file name, try to keep it short, but still meaningful.
I have seen some developers naming their entry file in Go different from main.go
, don't do that! Be different, be intelligent in something that avoid bugs instead.
Use lower case and use snake_case instead of mixedCaps. So, use server.go
, notification.go
or notification_server.go
instead of notificationServer.go
.
Use underscore instead of dash, dash would make your unit test file looks weird. So, use notification_server.go
, notification_server_test.go
or repository_mongo.go
instead of notification-server.go
or notification-server_test.go
.
If there are multiple files for different purposes in a same package, all the files of a same purpose should be prefixed the same way, that way your code would be perfectly aligned in your IDE and super easy to find.
Function/Method #
Use mixedCaps for naming your function. Use getArticles
or GetArticles
instead of get_articles
or get-articles
.
Name of a function should reflect what it does. Don't naming your function getArticles
but inside the function you also modify it.
Try to void the word Get
from the getters of a struct. Use Name()
and SetName(string)
instead of GetName()
and SetName(string)
.
If a function receives a context, the context must be the first parameter and its name should be ctx
.
Use DeleteArticle(ctx context.Context, id string) error
instead of DeleteArticle(id string, myCtx context.Context) error
.
For the method receiver, 2 or 3 letter maybe enough for the name. If you prefer more meaningful name, that's OK but remember to try to shorten them. So, srv
or svc
are better than server
, service
or myService
. r
or repo
is better than repository
or myRepository
If a function requires only one struct as its request param, req
is much better than getArticleRequest
. If it requires multiple param, concrete names should be provided but tries to keep them short.
If a param of a function is a writer, then w
is meaningful enough. If it's a reader, r
is a good choice.
Variable #
Name of variable should be short but should be meaningful enough. So, you can use srv
or svc
instead of service
, server
or myService
. User
or repo
instead of repository
or myRepository
.
Use ctx
for context.Context
. If it's a new context then newCtx
is good enough.
Use i
, j
for your indexing of a loop. But what if you have 3 loops? Please don't! Having 3 loops means you should refactor your code.
Some developers prefer to use rs
for naming result of something. This is OK, but in case you have multiple results of different API calls, the name should be more concrete, otherwise it would be really hard to read the code. You can use article
if the result is just a single struct represent an article, articles
if it's a slice, or searchArticleRs
if it's a response struct.
Constant #
Constant follows the same rule with variables and functions, hence use mixedCaps for naming. No underscore unless it's generated code. So, use StatusActive
instead of STATUS_ACTIVE
.
All constants of same type or represent a same values of something should be named with same prefix for easier to remember.
Error #
Name of error variable should start with Err
. Use ErrNotFound
instead of NotFoundError
. Note that it's Err
, not Error
.
But when it's an error type, Error
should be the suffix of the type name. Use NotFoundError
instead of ErrorNotFound
.
Interfaces #
The simplest rule for naming interface is adding er
into the name of the action/method it represents.
Here are some examples from the standard library:
type Writer interface {
Write(p []byte) (n int, err error)
}
type Reader interface {
Read(p []byte) (n int, err error)
}
type ReadWriter interface{
Reader
Writer
}
The standard guideline is easy but sometime you have big interfaces which includes 4 or 5 or even more actions/methods and it's hard to come up with a good name. My recommendation is naming them base on the meaning of the set of the methods/actions they represent.
If it's all about retrieving/updating data for something, DataProvider
, DataAdaptor
, DataUpdater
are good enough. If you want more concrete names, ArticleDataProvider
, ArticleGetter
, ArticleUpdater
, ... are good names to choose.
If it's a service that provide a lot of actions for something, maybe Service
or Server
are good suffixes for the name. So you can use ArticleService
, ArticleServer
, ...