This document defines the code style guidelines for the Goca project. Following these guidelines ensures consistency and maintainability across the codebase.
- General Principles
- Go Style Guidelines
- File Organization
- Naming Conventions
- Comments and Documentation
- Error Handling
- Testing
- Templates
- Command Line Interface
Code is read more often than it is written. Prioritize:
- Clear, descriptive names
- Simple, straightforward logic
- Appropriate comments where needed
- Consistent formatting
All code must respect Clean Architecture principles:
- Dependencies point inward
- Domain layer remains pure
- Clear separation of concerns
- Interface-based design
Follow standard Go idioms and conventions:
- Use
gofmtfor formatting - Follow Effective Go
- Adhere to Go Code Review Comments
Use gofmt to format all Go code automatically:
go fmt ./...- Prefer lines under 100 characters
- Break long lines at logical points
- Use continuation indentation for wrapped lines
// Good
func CreateEntity(
name string,
fields []Field,
options EntityOptions,
) error {
// implementation
}
// Avoid
func CreateEntity(name string, fields []Field, options EntityOptions, config Config, metadata Metadata) error {
// implementation
}Group imports into three sections:
- Standard library
- External dependencies
- Internal packages
import (
// Standard library
"context"
"fmt"
"time"
// External dependencies
"github.com/spf13/cobra"
"gorm.io/gorm"
// Internal packages
"github.com/sazardev/goca/internal/domain"
"github.com/sazardev/goca/internal/usecase"
)Use short variable declarations when possible:
// Good
name := "example"
count := 10
// Acceptable for zero values
var buffer bytes.Buffer
var wg sync.WaitGroup
// Required for multiple variables
var (
width int
height int
)package-name/
├── package.go # Main package file with package documentation
├── types.go # Type definitions
├── interface.go # Interface definitions
├── implementation.go # Implementation
└── package_test.go # Tests
Include package documentation in the main package file:
// Package entity provides functionality for generating domain entities
// following Clean Architecture principles. It handles entity creation,
// field validation, and code generation.
package entityPlace imports immediately after package declaration:
package entity
import (
"context"
"fmt"
)
// Type definitions follow- Use lowercase, single-word names
- Avoid underscores or mixed caps
- Choose clear, descriptive names
// Good
package entity
package repository
package usecase
// Avoid
package entityManager
package repo_implUse PascalCase for exported types, camelCase for unexported:
// Exported
type EntityGenerator struct {
name string
}
// Unexported
type fieldValidator struct {
rules []Rule
}Use camelCase for unexported, PascalCase for exported:
// Exported
func GenerateEntity(name string) error {
return validateName(name)
}
// Unexported
func validateName(name string) error {
// implementation
}Use descriptive names, avoid single letters except in short scopes:
// Good
entityName := "User"
fieldCount := len(fields)
// Acceptable for short scopes
for i, field := range fields {
// i is clear in context
}
// Avoid in larger scopes
n := "User" // What does n represent?
c := 10 // What does c count?Use PascalCase for exported, camelCase for unexported:
const (
// Exported
DefaultTimeout = 30 * time.Second
MaxRetries = 3
// Unexported
maxFieldLength = 255
defaultPort = 8080
)Use descriptive names, often ending with "er" for single-method interfaces:
// Single-method interfaces
type Generator interface {
Generate() error
}
type Validator interface {
Validate() error
}
// Multi-method interfaces
type EntityRepository interface {
Create(entity Entity) error
FindByID(id string) (Entity, error)
Update(entity Entity) error
Delete(id string) error
}Every package should have documentation:
// Package entity provides functionality for generating domain entities
// in Clean Architecture projects. It supports field validation, custom
// types, and automatic code generation following best practices.
//
// Basic usage:
//
// gen := entity.NewGenerator("User")
// gen.AddField("name", "string")
// err := gen.Generate()
package entityDocument all exported functions:
// GenerateEntity creates a new domain entity with the specified name and fields.
// It validates the entity structure and generates the corresponding Go file in
// the internal/domain package.
//
// The function returns an error if:
// - The entity name is invalid
// - Field definitions contain errors
// - File generation fails
//
// Example:
//
// err := GenerateEntity("User", []Field{
// {Name: "name", Type: "string"},
// {Name: "email", Type: "string"},
// })
func GenerateEntity(name string, fields []Field) error {
// implementation
}Use inline comments for complex logic:
// Convert field type to Go type
goType := convertToGoType(field.Type)
// Special handling for time.Time fields
if goType == "time.Time" {
// Add time import if not already present
addImport("time")
}Format TODO comments consistently:
// TODO(username): Add support for custom validators
// TODO(username): Optimize performance for large entity setsProvide context in error messages:
// Good
if err != nil {
return fmt.Errorf("failed to generate entity %s: %w", name, err)
}
// Avoid
if err != nil {
return err // Loses context
}Always check errors explicitly:
// Good
data, err := readFile(path)
if err != nil {
return fmt.Errorf("failed to read file: %w", err)
}
// Never ignore errors
_ = file.Close() // Avoid
// Better
if err := file.Close(); err != nil {
log.Printf("warning: failed to close file: %v", err)
}Use custom error types for specific conditions:
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation error on field %s: %s", e.Field, e.Message)
}Use _test.go suffix:
// Implementation
entity.go
// Tests
entity_test.goUse descriptive names starting with Test:
func TestGenerateEntity(t *testing.T) {}
func TestGenerateEntity_InvalidName(t *testing.T) {}
func TestGenerateEntity_WithCustomFields(t *testing.T) {}Use table-driven tests for multiple scenarios:
func TestValidateName(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
}{
{
name: "valid name",
input: "User",
wantErr: false,
},
{
name: "empty name",
input: "",
wantErr: true,
},
{
name: "invalid characters",
input: "User-123",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateName(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("validateName() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}Create helper functions for common test setup:
func setupTestEnvironment(t *testing.T) (cleanup func()) {
// Setup code
return func() {
// Cleanup code
}
}
func TestSomething(t *testing.T) {
cleanup := setupTestEnvironment(t)
defer cleanup()
// Test code
}Organize templates logically:
const (
entityTemplate = `package domain
type {{ .Name }} struct {
{{ range .Fields -}}
{{ .Name }} {{ .Type }}
{{ end -}}
}`
)Format templates for readability:
const handlerTemplate = `
package handler
import (
"{{ .Module }}/internal/domain"
"{{ .Module }}/internal/usecase"
)
type {{ .Name }}Handler struct {
useCase usecase.{{ .Name }}UseCase
}
`Use clear, verb-based command names:
goca init # Initialize project
goca feature # Generate feature
goca entity # Generate entityUse descriptive flag names:
cmd.Flags().StringVar(&name, "name", "", "entity name")
cmd.Flags().StringSliceVar(&fields, "fields", nil, "entity fields")
cmd.Flags().BoolVar(&force, "force", false, "force overwrite")Provide clear usage information:
var entityCmd = &cobra.Command{
Use: "entity [name]",
Short: "Generate a domain entity",
Long: `Generate a domain entity following Clean Architecture principles.
The entity command creates a new entity in the internal/domain package
with proper structure, validation, and documentation.
Example:
goca entity User --fields "name:string,email:string"`,
RunE: runEntityCommand,
}Before submitting code, verify:
- Code is formatted with
gofmt - All functions are documented
- Error handling is appropriate
- Tests are included and pass
- No unnecessary dependencies
- Follows naming conventions
- Clean Architecture principles respected
- Code is clear and maintainable
Recommended tools for maintaining code quality:
# Format code
go fmt ./...
# Vet code
go vet ./...
# Run tests
go test ./...
# Check test coverage
go test -cover ./...
# Lint (if golangci-lint installed)
golangci-lint runFollowing these guidelines helps maintain a high-quality, consistent codebase that's easy to understand and maintain.