2019-03-14 10:17:20 -05:00
# Backend style guide
2020-07-30 10:48:41 -05:00
Grafana's backend has been developed for a long time with a mix of code styles. This guide explains how we want to write Go code in the future.
2019-03-14 10:17:20 -05:00
2019-10-30 15:59:27 -05:00
Unless stated otherwise, use the guidelines listed in the following articles:
- [Effective Go ](https://golang.org/doc/effective_go.html )
- [Code Review Comments ](https://github.com/golang/go/wiki/CodeReviewComments )
- [Go: Best Practices for Production Environments ](http://peter.bourgon.org/go-in-production/#formatting-and-style )
2019-03-14 10:17:20 -05:00
## Linting and formatting
2019-10-30 15:59:27 -05:00
2023-02-07 08:44:56 -06:00
To ensure consistency across the Go codebase, we require all code to
pass a number of linter checks.
2019-10-30 15:59:27 -05:00
2023-02-07 08:44:56 -06:00
We use [GolangCI-Lint ](https://github.com/golangci/golangci-lint ) with a
custom configuration [.golangci.toml ](/.golangci.toml ) to run these
checks.
2019-10-30 15:59:27 -05:00
To run all linters, use the `lint-go` Makefile target:
2019-07-02 08:06:59 -05:00
```bash
2019-10-30 15:59:27 -05:00
make lint-go
2019-07-02 08:06:59 -05:00
```
2019-03-14 10:17:20 -05:00
2019-10-30 15:59:27 -05:00
## Testing
2019-03-14 10:17:20 -05:00
2019-10-30 15:59:27 -05:00
We value clean and readable code, that is loosely coupled and covered by unit tests. This makes it easier to collaborate and maintain the code.
2019-06-18 02:31:51 -05:00
2019-10-30 15:59:27 -05:00
Tests must use the standard library, `testing` . For assertions, prefer using [testify ](https://github.com/stretchr/testify ).
2019-03-14 10:17:20 -05:00
2024-02-09 08:35:39 -06:00
### Test Suite and Database Tests
We have a [testsuite ](https://github.com/grafana/grafana/tree/main/pkg/tests/testsuite ) package which provides utilities for package-level setup and teardown.
2024-03-01 04:00:15 -06:00
Currently, this is just used to ensure that test databases are correctly set up and torn down, but it also provides a place we can attach future tasks.
2024-02-09 08:35:39 -06:00
Each package SHOULD include a [TestMain ](https://pkg.go.dev/testing#hdr-Main ) function that calls `testsuite.Run(m)` :
```go
package mypkg
import (
"testing"
"github.com/grafana/grafana/pkg/tests/testsuite"
)
func TestMain(m *testing.M) {
testsuite.Run(m)
}
```
You only need to define `TestMain` in one `_test.go` file within each package.
> Warning
> For tests that use the database, you MUST define `TestMain` so that the test databases can be cleaned up properly.
2022-11-04 09:14:21 -05:00
### Integration Tests
We run unit and integration tests separately, to help keep our CI pipeline running smoothly and provide a better developer experience.
To properly mark a test as being an integration test, you must format your test function definition as follows, with the function name starting with `TestIntegration` and the check for `testing.Short()` :
2024-02-09 08:35:39 -06:00
```go
2022-11-04 09:14:21 -05:00
func TestIntegrationFoo(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
// function body
}
```
2023-02-07 08:44:56 -06:00
> Warning
> If you do not follow this convention, your integration test may be run twice or not run at all.
2022-11-04 09:14:21 -05:00
2020-12-16 12:35:07 -06:00
### Assertions
2021-09-29 07:34:40 -05:00
Use respectively [`assert.*` ](https://github.com/stretchr/testify#assert-package ) functions to make assertions that
should _not_ halt the test ("soft checks") and [`require.*` ](https://github.com/stretchr/testify#require-package )
2024-03-01 04:00:15 -06:00
functions to make assertions that _should_ halt the test ("hard checks"). Typically, you want to use the latter type of
2020-12-16 12:35:07 -06:00
check to assert that errors have or have not happened, since continuing the test after such an assertion fails is
chaotic (the system under test will be in an undefined state) and you'll often have segfaults in practice.
### Sub-tests
Use [`t.Run` ](https://golang.org/pkg/testing/#T.Run ) to group sub-test cases, since it allows common setup and teardown
code, plus lets you run each test case in isolation when debugging. Don't use `t.Run` to e.g. group assertions.
### Cleanup
2023-02-07 08:44:56 -06:00
Use [`t.Cleanup` ](https://golang.org/pkg/testing/#T.Cleanup ) to clean up resources in tests. It's a preferable to `defer` , as it can be called from helper functions. It will always execute after the test is over in reverse call order (last `t.Cleanup` first, same as `defer` ).
2020-12-16 12:35:07 -06:00
2022-03-09 07:08:55 -06:00
### Mock
2024-01-05 10:40:11 -06:00
Optionally, we use [`mock.Mock` ](https://github.com/stretchr/testify#mock-package ) package to write mocks.
This is useful when you expect different behaviors of the same function.
2022-03-09 07:08:55 -06:00
#### Tips
2023-11-03 08:33:12 -05:00
- Use `Once()` or `Times(n)` to make a method call work `n` times.
- Use `mockedClass.AssertExpectations(t)` to guarantee that methods are called the times asked.
- If any method is not called the expected amount of times, the test fails.
2022-03-09 07:08:55 -06:00
- You can pass `mock.Anything` as argument if you don't care about the argument passed.
2023-11-03 08:33:12 -05:00
- Use `mockedClass.AssertNotCalled(t, "MethodName")` to assert that a method was not called.
2022-03-09 07:08:55 -06:00
#### Example
Given this interface:
```go
2023-11-03 08:33:12 -05:00
type MyInterface interface {
2022-03-09 07:08:55 -06:00
Get(ctx context.Context, id string) (Object, error)
}
```
Mock implementation should be like this:
```go
2023-11-03 08:33:12 -05:00
import "github.com/stretchr/testify/mock"
2022-03-09 07:08:55 -06:00
2023-11-03 08:33:12 -05:00
type MockImplementation struct {
2022-03-09 07:08:55 -06:00
mock.Mock
}
2023-11-03 08:33:12 -05:00
func (m *MockImplementation) Get(ctx context.Context, id string) (Object, error) {
2022-03-09 07:08:55 -06:00
args := m.Called(ctx, id) // Pass all arguments in order here
return args.Get(0).(Object), args.Error(1)
}
```
2023-11-03 08:33:12 -05:00
And use it in the following way:
2022-03-09 07:08:55 -06:00
```go
objectToReturn := Object{Message: "abc"}
errToReturn := errors.New("my error")
myMock := & MockImplementation{}
defer myMock.AssertExpectations(t)
2023-11-03 08:33:12 -05:00
myMock.On("Get", mock.Anything, "id1").Return(Object{}, errToReturn).Once()
myMock.On("Get", mock.Anything, "id2").Return(objectToReturn, nil).Once()
2022-03-09 07:08:55 -06:00
anyService := NewService(myMock)
2023-11-03 08:33:12 -05:00
resp, err := anyService.Call("id1")
2022-03-09 07:08:55 -06:00
assert.Error(t, err, errToReturn)
resp, err = anyService.Call("id2")
assert.Nil(t, err)
2023-11-03 08:33:12 -05:00
assert.Equal(t, resp.Message, objectToReturn.Message)
2022-03-09 07:08:55 -06:00
```
#### Mockery
2024-01-05 10:40:11 -06:00
When an interface to test is too big, it may be toilsome to mock each function manually. To avoid this, you can
2022-03-09 07:08:55 -06:00
use [`mockery` ](https://github.com/vektra/mockery ) library to generate the mocks.
The command is like the following (there are more options documented if you need to use another one):
```
mockery --name InterfaceName --structname MockImplementationName --inpackage --filename my_implementation_mock.go
```
- `--name` : Interface to mock
- `--structname` : Mock implementation name
- `--inpackage` : To use the same package name as the interface
- `--filename` : Your mock generated file name
If any interface signature changes, executing the command again updates the mock.
Additionally, you can put `go:generate` command on the top of the file as a comment. It's useful because some IDEs
like Goland and Visual Studio Code allows executing scripts from the IDE.
```
package < package >
import (
...
)
//go:generate mockery --name InterfaceName --structname MockImplementationName --inpackage --filename my_implementation_mock.go
```
2020-12-14 02:38:52 -06:00
## Globals
As a general rule of thumb, avoid using global variables, since they make the code difficult to maintain and reason
about, and to write tests for. The Grafana codebase currently does use a lot of global variables, especially when
it comes to configuration, but that is a problem we're trying to solve.
2020-12-16 12:35:07 -06:00
## Pointers
In general, use value types and only reach for pointers when there's a real need. The reason being that pointers
increase the risk of bugs, since a pointer can be nil and dereferencing a nil pointer leads to a panic (AKA segfault).
Valid reasons to use a pointer include (but not necessarily limited to):
2021-09-29 07:34:40 -05:00
- You might need to pass a modifiable argument to a function
- Copying an object might incur a performance hit (benchmark to check your assumptions, copying is often faster than
2020-12-16 12:35:07 -06:00
allocating heap memory)
2021-09-29 07:34:40 -05:00
- You might _need_ `nil` to tell if a variable isn't set, although usually it's better to use the type's zero
2020-12-16 12:35:07 -06:00
value to tell instead
2021-01-12 09:24:22 -06:00
## Database
In database related code, we follow certain patterns.
### Foreign keys
While they can be useful, we don't generally use foreign key constraints in Grafana, for historical and
2021-09-29 07:34:40 -05:00
technical reasons. See this [comment ](https://github.com/grafana/grafana/issues/3269#issuecomment-383328548 ) by Torkel
2021-01-12 09:24:22 -06:00
for context.
### Unique columns
If a column, or column combination, should be unique, add a corresponding uniqueness constraint through a migration.
2021-01-14 00:49:25 -06:00
2023-03-02 05:01:36 -06:00
### Usage of XORM Session.Insert() and Session.InsertOne()
The [Session.Insert() ](https://pkg.go.dev/github.com/go-xorm/xorm#Session.Insert ) and [Session.InsertOne() ](https://pkg.go.dev/github.com/go-xorm/xorm#Session.InsertOne ) are poorly documented and return the number of affected rows contrary to a common mistake that they return the newly introduced primary key. Therefore, contributors should be extra cautious when using them.
The same applies for the respective [Engine.Insert() ](https://pkg.go.dev/github.com/go-xorm/xorm#Engine.Insert ) and [Engine.InsertOne() ](https://pkg.go.dev/github.com/go-xorm/xorm#Engine.InsertOne )
2021-01-14 00:49:25 -06:00
## JSON
2023-02-07 08:44:56 -06:00
The simplejson package is used a lot throughout the backend codebase,
but it's legacy, so if at all possible avoid using it in new code.
Use [encoding/json ](https://golang.org/pkg/encoding/json/ ) instead.