Architecture Decisions: App & Code
The environment is forced to test
in the tests, can never be test
when running the application, and the test
environment only read its configuration from config.yaml.test
, and never from config.yaml
Date: 05/06/2024
Why?
- We want to make sure tests are never run on a live database because they erase all the data.
More info and discussion: https://github.com/France-ioi/AlgoreaBackend/pull/1085
Fields that are not visible to the user due to access rights must not appear in the response at all
Date: 12/09/2023
Why?
- We want to be able to distinguish between
null
values and values that are not visible to the user.
Examples:
- The name is visible and has a value:
{"id": 1, "name": "John"}
- The name is visible but is set to
null
:{"id": 1, "name": null}
- The name is not visible:
{"id": 1}
Swagger documentation for Responses and Requests must always specify Nullable
and required: true
when applicable
Date: 31/08/2023
- When a field is always present (response) or must always be given (request), it must have a
// required:true
annotation. Otherwise, no annotation. - When a field can have the value
null
, for both responses and requests, it must have a// Nullable
annotation.
The single source of truth for Nullable is the database.
Example:
type item struct {
// required:true
ID int64 `json:"id,string"`
// required:true
// Nullable
// enum: Chapter,Task,Skill
Type *string `json:"type"`
// required:true
// Nullable
Title *string `json:"title"`
// required:true
LanguageTag string `json:"language_tag"`
}
Why?
- Because it has an influence on how the frontend or other third-party treat the responses.
End Of Line must be LF
Date: 22/03/2023
Why?
- go-fmt automatically changes the line endings of *.go files with LF. Make it LF everywhere for consistency.
Panic/recover error handling
Date: 05/03/2019
Besides the usual way of error handling via returning an error value from a function and checking it in the caller, Go has a special way of handling errors. It is called panic and recover. The panic function is used to raise a runtime error. When a function encounters a panic, it stops executing and unwinds the stack. The deferred functions are executed and then the program terminates. The recover function is used to catch a panic and resume normal execution. It is used in a deferred function. The recover function returns the value that was passed to the panic function. If the function is not called in a deferred function, it returns nil.
The technique of handling errors via panic/recover very convenient. It allows us to get rid of error handling in many places and concentrate it in one place. It is widely used in our code.
However, it is not a good idea to return errors via panic from a public method or a public function in a package. Only the package itself can recover from a panic. All panics that are not recovered by the package are propagated to the caller and considered as a bug/unexpected behavior. As it is written in “Effective Go”: “Useful though this pattern is, it should be used only within a package. … That is a good rule to follow.” (See https://go.dev/doc/effective_go#recover)
For the reasons mentioned above, it was decided not to return errors via panic from public methods/functions in our code. Instead, we should return an error value from a public function/method and check it in the caller.
As an exception it was decided to allow special public methods/functions prefixed with ‘Must’ to return errors via panic if their parameter is an error. The ‘Must’ prefix is a common convention in Go to indicate that the function panics if an error occurs. It is used in the standard library, for example, in the ‘template’ package. With this prefix the caller always knows that the function panics if an error occurs. You can find some functions named ‘MustNotBeError’ in our packages.