The only measure of code quality is the number of times you swear while reading it
An excellent team should maintain a consistent coding style. In practice, we have found that if the coding style of a project is inconsistent, it will cause the project to be messy, not conducive to cross-module development, affect development efficiency, and make people want to curse.
Before defining our coding style, I first recommend a very good book "clean code". If you have time, you can read it and sharpen your knife without losing time.
Naming basic rules
- From the caller's perspective, the package is not for your own use.
- Simple and clear
- Use common, well-known abbreviations. For example instead
buf
ofbufio
- If the abbreviated name creates ambiguity, discard or change it
Package names
- The package name is consistent with the directory name
- In most cases using named imports, renaming is not required
Use the caller less often to create aliases, unless the name is too bad
- Package names should be all lowercase, no underscores, no capital letters
- Singular, no plural
Example:
Bad:
configs
third_party
thirdParty
...
Import package
(1) If the package name does not match the last element of the import path, an import alias must be used
import (
client "example.com/client-go"
trace "example.com/trace/v2"
)
It is recommended to use aliases if the last element of the path is a version number to avoid ambiguity.
(2) In all other cases, avoid importing aliases unless there is a direct conflict between the imports
import (
"net/http/pprof"
gpprof "github.com/google/pprof"
)
(3) If there are duplicate names, please keep the standard package naming, customize the alias or third-party package
(4) It is prohibited to use relative paths to import. All import paths must comply with go get standards.
file name
- File names should be kept simple and easy to understand.
- File names should be all lowercase, no uppercase letters should appear, and different words should be separated by underscores.
- File names should avoid plural forms
Example:
Bad:
configs.yaml
cloudServer.go
cloud-server.go
function name/method name
- Function names/method names should remain in verb/verb-object form
- Abbreviations should not be used unless they are recognized abbreviations
- Avoid names that are too long. Long names do not make them more readable. A useful documentation or comment is usually more valuable than an extra long name.
Example:
Bad:
container.createContainer(in)
Good:
container.create(in)
- Go does not provide automatic support for getters and setters. For a certain variable or field, the getter name does not need to be carried
Get
, and the setter nameSet
starts with
note : If you have a field named owner (lower case, not exported), its getter should be named Owner (upper case, exportable) instead of GetOwner.
Example:
Good:
func Owner() string {
...
}
func SetOwner(ower string) string {
...
}
Bad:
func GetOwner() string {
...
}
- Use English for naming to avoid word mistakes.
Example:
Bad:
func (u *userRepo) syncDepartmentFormDatabase() {
...
}
From rather than Form.
Interface name
- By convention, an interface containing only one method should be named after the name of the method plus the -er suffix, such as Reader, Writer, Formatter/CloseNotifier, etc.
type Reader interface {
Read(p []byte) (n int, err error)
}
- Nouns are used for interface names and verbs are used for interface method names.
- A single verb should be used unless the included method name is not directly related to the interface name.
type ContainerRepo interface {
Create() error
Delete() error
...
}
Structure name
- Struct names must be named strictly in noun form.
- Avoid using plurals
- Use camelCase for multiple words
Example:
Good:
type Container struct {
}
type ContainerInfo struct {
}
Bad:
type ContainerDelete struct {
}
type Containers struct {
}
type Containerinfo struct {
}
constant name
- If it is a constant of enumeration type, you need to create the corresponding type first
type Scheme string
const (
Http Scheme = "http"
Https Scheme = "https"
)
- If the function of the module is more complex and the constant names are easily confused, in order to better distinguish the enumeration types, you can use the complete prefix
type Symbol string
const (
SymbolAdd Symbol = "+"
SymbolSub Symbol = "-"
)
variable name
- If it is a global variable, be sure to use the full name and be sure to add comments
// configPath is the absolute path of config file from the command line.
var configPath string
- If it is a local variable, in a relatively simple environment (a small number of objects and a strong focus), some names can be abbreviated from complete words to single letters.
user 可以简写为 u
userId 可以简写 uid
- If the variable type is bool, the name should start with Has, Is, Can or Allow
var isExist bool
var hasConflict bool
var canManage bool
var allowGitHook bool
Error
Error
Type namesError
end with
type CodeError struct {
message string
}
Error
Variables of type,Err
starting with
var ErrTokenExpired = errors.New("token expired")
- Variables of return type
Error
are abbreviated usingerr
func test() {
res, err := container.create(in)
...
}
- Do not use the same err to receive different types of errors.
Example:
Good:
func (c *container)Update(ctx context.Context,name string) error {
res, listErr := c.list()
if listErr!=nil {
return listErr
}
updateErr := c.update(name)
if updateErr!=nil {
return updateErr
}
...
}
Bad:
func (c *container)Update(ctx context.Context,name string) error {
res, err := c.list()
if err!=nil {
return err
}
_, err = c.update(name)
if err!=nil {
return err
}
...
}
URL
- URL names are all lowercase
- Use forward slashes
/
to indicate hierarchical relationships - Use hyphens
-
to improve readability of names in long paths and do not use underscores in URLs_
- The URL should not contain a forward slash at the end
/
- File extension should not be included in URL
- The URL must be clearly identifiable by name, but the server architecture must not be exposed
Bad:
/GetUserInfo
/photos_path
/My-Folder/my-doc/
/user/user-list
Good:
/user/list
/user/operator-logs
Proto
- The RPC of service should refer to the style of function name/method name.
- In the proto file, the naming style of the structure (message of proto) is as follows:
message CreateSecurityGroupRequest {}
message CreateSecurityResponse {}
// 除了request和response外,其他的命名风格需保持名词形式
message SecurityGroupInfo{}
enum Policy{}
other
- The name of the package content cannot start with the package name because there is no need to repeat the package name.
For example: http
The HTTP service provided by the standard package is named http.Server
instead HTTPServer
. User code http.Server
refers to the type via , so there is no ambiguity.
package http
type Server struct {
}
- Type names in different packages can be the same because the client can distinguish them by the package name
For example: The standard library contains multiple types named Reader, including jpeg.Reader
, bufio.Reader
and csv.Reader
. Every package name paired with Reader is a good type name.
package jpeg
type Reader struct {
}
package bufio
type Reader struct {
}
format
We don't have much choice, because Go has been standardized, and there is no such war in the Go world.
Json/Bson
- It should be pointed out in particular that we often need this field when defining a structure
id
. In order to be concise and easy to understand without ambiguity, we stipulate that in a structure, all fields involvedid
must be prefixed to distinguish them. - All json/bson fields with multiple words use underline format and do not use camel case format.
type SecurityGroup struct {
SgID string `json:"sg_id"`
Rule Rule `json:"rule"`
...
}
type Rule struct {
RuleID string `json:"rule_id"`
...
}
blank line style
- After the logic of a piece of code is processed, there should be an empty line
- There should be a blank line after if err
Example:
Good:
func Create() error {
if err:=container.create();err!=nil {
return err
}
if err:=store.update();err!=nil {
return err
}
...
}
func check() error {
isExist, err := contaier.check()
if err!=nil {
return err
}
if isExist {
...
}
if err:=store.update();err!=nil {
return err
}
...
}
- The first line of code in the function is not a blank line
Example:
Bad:
func test() error {
if err:=container.create();err!=nil {
return err
}
...
}
- It is recommended that the line before the return statement on the last line in the function be a blank line.
func test() error {
fmt.Println("test")
return nil
}
import style
- Follow our custom import order: standard package, third-party package, and project package, separated by blank lines.
import (
"flag"
"os"
"github.com/go-kratos/kratos/v2"
"github.com/go-kratos/kratos/v2/config"
"github.com/go-kratos/kratos/v2/config/file"
"github.com/go-kratos/kratos/v2/log"
"github.com/go-kratos/kratos/v2/middleware/tracing"
"github.com/go-kratos/kratos/v2/transport/grpc"
"github.com/go-kratos/kratos/v2/transport/http"
"backend/internal/conf"
"backend/internal/data"
)