diff --git a/docs/contributing/coding-style.md b/docs/contributing/coding-style.md index fd54e2d35d..13c2895161 100644 --- a/docs/contributing/coding-style.md +++ b/docs/contributing/coding-style.md @@ -12,8 +12,11 @@ Code formatting and naming conventions affect coding readability and maintainabi But having tools to format source code doesn't mean you do not need to care the formatting of the code, for example: + + +
BadGood
+ ```go -// bad badStr := "apiVersion: v1\n" + "kind: Service\n" + "metadata:\n" + @@ -24,8 +27,11 @@ badStr := "apiVersion: v1\n" + " port: 3000\n" + " targetPort: 3000\n" + " protocol: TCP\n" +``` -// good + + +```go goodStr := `apiVersion: v1 kind: Service metadata: @@ -39,6 +45,8 @@ spec: ` ``` +
+ ### Project Layout The project layout includes the folder and the file structure in the project. We follow the [project-layout](https://github.com/golang-standards/project-layout) example in Vald. @@ -90,9 +98,79 @@ All packages should contain `doc.go` file under the package to describe what is package cache ```` +### General style + +This section describes the general guideline for the Vald programming style, every Vald contributor should keep these general guidelines in mind while working on the implementation of Vald. + +#### Order of declaration + +Put the higher priority or frequently used declaration on the top of other declaration. +It makes Vald easier to read and search the target source code in Vald. + +For example, the interface declaration should have higher priority than struct or function declaration, hence it should be put above other declaration. + + + +
BadGood
+ +```go +type S struct {} + +func (s *S) fn() {} + +type I interface {} +``` + + + +```go +type I interface {} + +type S struct {} + +func (s *S) fn() {} +``` + +
+ +#### Group similar definition + +Group similar definitions such as struct or interface declaration. +We should not group interface and struct declaration in the same block, for example: + + + +
BadGood
+ +```go +type ( + I interface {} + I2 interface {} + + s struct {} + s2 struct {} +) +``` + + + +```go +type ( + I interface {} + I2 interface {} +) + +type ( + s struct {} + s2 struct {} +) +``` + +
+ ### Interfaces -Interface defines the program interface for usability and future extendability. +The interface defines the program interface for usability and future extendability. Unlike other languages like Java, Go supports implicit interface implementation. The type implements do not need to specify the interface name; to "implements" the interface the structs only need to defined the methods the same as the interface, so please be careful to define the method name inside the interface. The interface should be named as: @@ -303,46 +381,53 @@ Please use [internal/errgroup](https://github.com/vdaas/vald/blob/master/interna All functions return `error` if the function can fail. It is very important to ensure the error checking is performed. To reduce human mistake that missing the error checking, please check the error using the following style: + + +
BadGood
+ ```go -// good -if err := fn(); err != nil { +err := fn() +if err != nil { // handle error } ``` -Instead of this style. + ```go -// bad -err := fn() -if err != nil { +if err := fn(); err != nil { // handle error } ``` +
+ If you need the value outside the if statement, please use the following style: + + +
BadGood
+ ```go -// good -conn, err := net.Dial("tcp", "localhost:80") -if err != nil { +if conn, err := net.Dial("tcp", "localhost:80"); err != nil { // handle error +} else { + // use the conn } - -// use the conn ``` -Instead of this style. + ```go -// bad -if conn, err := net.Dial("tcp", "localhost:80"); err != nil { +conn, err := net.Dial("tcp", "localhost:80") +if err != nil { // handle error -} else { - // use the conn } +// use the conn ``` +
+ ### Logging We define our own logging interface in [internal/log package](https://github.com/vdaas/vald/blob/master/internal/log). By default we use [glg](https://github.com/kpango/glg) to do the logging internally. @@ -356,6 +441,65 @@ We defined the following logging levels. | ERROR | The message that indicates the application is having a serious issue or,
represent the failure of some important going on in the application.
It does not cause the application to go down.
Someone must investigate the error later. | Failed to insert an entry into the database, with retry count exceeded.
Failed to update the cache, and the cache is not important. | User 1 is failed to insert in the database, errors:
retry count: 1, error: ErrMsg1, retry count: 2, error: ErrMsg2, .... | | FATAL | Message that indicate the application is corrupting or having serious issue.
The application will go down after logging the fatal error.
Someone must investigate and resolve the fatal as soon as possible. | Failed to init the required cache during the application start. | | +## Implementation + +This section includes some examples of general implementation which is widely used in Vald. +The implementation may differ based on your use case. + +### Functional Option + +In Vald, the functional option pattern is widely used in Vald. +You can refer to [this section](#Struct-initialization) for more details of the use case of this pattern. + +We strongly recommend the following implementation to set the value using functional option. + +```go +func WithVersion(version string) Option { + return func(c *client) error { + if len(version) != 0 { + c.version = version + } + return nil + } +} +``` + +We recommend the following implementation to parse the time string and set the time to the target struct. + +```go +func WithTimeout(dur string) Option { + func(c *client) error { + if dur == "" { + return nil + } + d, err := timeutil.Parse(dur) + if err != nil { + return err + } + c.timeout = d + return nil + } +} +``` + +We recommend the following implementation to append the value to the slice if the value is not nil. + +```go +func WithHosts(hosts ...string) Option { + return func(c *client) error { + if len(hosts) == 0 { + return nil + } + if c.hosts == nil { + c.hosts = hosts + } else { + c.hosts = append(c.hosts, hosts...) + } + return nil + } +} +``` + ## Program comments Program comments make easier to understand the source code. We suggest not to write many comments inside the source code unless the source code is very complicated and confusing; otherwise we should divide the source code into methods to keep the readability and usability of the source code.