Go Conventions¶
All Amadla tools and libraries follow consistent Go coding conventions.
Naming¶
Interfaces¶
Interfaces are prefixed with I:
type IGit interface {
Clone(url string) error
Pull() error
}
type IFile interface {
Read(path string) ([]byte, error)
Write(path string, data []byte) error
}
Structs¶
Implementation structs are prefixed with S:
Constructors¶
Constructor functions follow the New*Service() pattern and return the interface type:
This pattern enables dependency injection and mocking.
Mocks¶
Mocks are generated by mockery and follow the Mock* or mock_I*.go naming:
Dependency Injection¶
Package-Level Function Variables¶
OS and system calls are assigned to package-level variables so tests can replace them:
// Production code
var osOpen = os.Open
var execCommand = exec.Command
func readFile(path string) ([]byte, error) {
f, err := osOpen(path)
// ...
}
// Test code
func TestReadFile(t *testing.T) {
osOpen = func(name string) (*os.File, error) {
return nil, errors.New("mock error")
}
defer func() { osOpen = os.Open }()
// ...
}
Interface-Based Injection¶
Services accept interfaces in constructors, enabling mock injection:
type SEntity struct {
git IGit
cache ICache
schema ISchema
}
func NewEntityService(git IGit, cache ICache, schema ISchema) IEntity {
return &SEntity{git: git, cache: cache, schema: schema}
}
Error Handling¶
- Use typed errors in
message/packages - Wrap errors with context:
fmt.Errorf("failed to clone repo: %w", err) - Return errors up the call stack; handle at command level
Code Organization¶
- One interface + implementation per file (named after the interface)
- Tests alongside implementation:
git.go+git_test.go - Mocks generated in-package:
mock_IGit.go