Contributing Guidelines
Contributing Guidelines
Branch Strategy
- Main branch:
develop - Feature branches:
feature/<short-description> - Bug fixes:
fix/<short-description> - All PRs target
develop
Code Style
Follow standard Go conventions enforced by gofmt and golint:
gofmt -w ./...golint ./...go vet ./...- Package names: lowercase, single word (e.g.,
service,layers,auth) - Exported names:
CamelCase - Unexported names:
camelCase - Error variables:
ErrSomething - Constants:
CamelCasefor exported,camelCasefor package-level
Error Handling
Always wrap errors with context:
// β
Good β context preservedif err := db.FindOne(ctx, filter, &result); err != nil { return fmt.Errorf("FlowService.GetById: find flow %s: %w", id, err)}
// β Bad β context lostif err != nil { return err}Never swallow errors silently. If ignoring an error is intentional, add a comment:
_ = cache.Set(key, value) // best-effort cache; failure is non-criticalLogging
Use the injected zerolog logger. Never use fmt.Println or log.Printf in service code:
type MyService struct { Logger zerolog.Logger `container:"inject"`}
func (s *MyService) DoWork(id string) { s.Logger.Info().Str("id", id).Msg("starting work") // ... s.Logger.Error().Err(err).Str("id", id).Msg("work failed")}Log levels:
Debugβ detailed tracing (disabled in production)Infoβ normal operationsWarnβ recoverable issuesErrorβ failures requiring investigation
MongoDB Operations
Always filter by CompanyId β missing this filter causes cross-tenant data leakage:
// β
Correctfilter := bson.M{"companyId": companyId, "_id": id}
// β Never do this β leaks data across tenantsfilter := bson.M{"_id": id}Use the repo layer for list queries to get consistent pagination:
query := s.Repo.Select("*").From("flows"). Where("companyId", "=", companyId). Where("deleted", "=", false). OrderBy("createdAt", "desc"). Limit(size).Offset(page * size)Security Conventions
-
Never log sensitive data β passwords, tokens, API keys, encryption keys must never appear in logs:
// β Nevers.Logger.Info().Str("token", apiKey).Msg("authenticating") -
Always use encrypted KV for secrets β use the KV storeβs encryption, not plain string storage.
-
Validate all input β use struct validation tags and explicit checks:
if len(input) == 0 {return ErrInputRequired}if len(input) > MaxInputLength {return ErrInputTooLong} -
Parameterize all queries β never concatenate user input into MongoDB queries or SQL strings.
-
Audit significant actions β any create/update/delete of user-facing entities must call the audit logger:
s.AuditLogger.LogEntityAction(ctx, audit.ActionFlowCreate, flow)
Testing Requirements
- External calls β fixture tests: Any module or service that calls an external API must have a fixture-based test (record once, replay deterministically).
- Pure logic β unit tests: Data transformations, expression evaluation, and business rules should have direct unit tests.
- Minimum coverage: New services should have at least 70% test coverage.
# Run all testsgo test ./...
# Run with coveragego test -cover ./pkg/service/... -coverprofile=coverage.outgo tool cover -html=coverage.outModule Versioning
When a moduleβs argument schema changes in a breaking way:
- Increment the moduleβs
versionconstant - Implement the
Migrate(fromVersion int, args map[string]any) map[string]anymethod - Register the migration in
ModuleMigrationService
The migration service runs automatically at startup and updates existing flowlet args to the new schema.
PR Checklist
Before opening a PR:
-
gofmt -w ./...applied -
go vet ./...passes - All existing tests pass:
go test ./... - New code has tests
- Sensitive data not in logs
- All MongoDB queries filter by
companyId - Audit logger called for entity mutations
- Module catalog CSV updated (if new module added):
go run cmd/modulescsv/main.go - Environment variable documented in
internal/setup/environment.md(if new var added)