# Code Guidelines for WhatsHooked ## General Principles - Write clean, idiomatic Go code following standard conventions - Use meaningful variable and function names - Keep functions small and focused on a single responsibility - Document exported functions, types, and packages - Handle errors explicitly, never ignore them - Use context.Context for cancellation and timeout handling ## Project Structure ``` whatshooked/ ├── cmd/ # Command-line entry points │ ├── server/ # HTTP server command │ └── cli/ # CLI tools ├── pkg/ # Public packages │ ├── auth/ # Authentication & authorization │ ├── api/ # API endpoints │ ├── cache/ # Caching layer │ ├── config/ # Configuration management │ ├── events/ # Event system │ ├── eventlogger/ # Event logging │ ├── handlers/ # HTTP handlers │ ├── hooks/ # Webhook management │ ├── logging/ # Structured logging │ ├── storage/ # Database storage layer │ ├── utils/ # Utility functions │ ├── webserver/ # Web server with ResolveSpec │ ├── whatsapp/ # WhatsApp integration │ └── whatshooked/ # Core application logic └── tooldoc/ # Tool & library documentation ``` ## Naming Conventions ### Packages - Use lowercase, single-word package names - Use descriptive names that reflect the package's purpose - Avoid generic names like `util` or `common` (use `utils` with specific subdirectories if needed) ### Files - Use snake_case for file names: `user_service.go`, `auth_middleware.go` - Test files: `user_service_test.go` - Keep related functionality in the same file ### Variables & Functions - Use camelCase for private: `userService`, `handleRequest` - Use PascalCase for exported: `UserService`, `HandleRequest` - Use descriptive names: prefer `userRepository` over `ur` - Boolean variables should be prefixed: `isValid`, `hasPermission`, `canAccess` ### Constants - Use PascalCase for exported: `DefaultTimeout` - Use camelCase for private: `defaultTimeout` - Group related constants together ### Interfaces - Name interfaces with -er suffix when appropriate: `Reader`, `Writer`, `Handler` - Use descriptive names: `UserRepository`, `AuthService` ## Error Handling ```go // Wrap errors with context if err != nil { return fmt.Errorf("failed to load user: %w", err) } // Check specific errors if errors.Is(err, sql.ErrNoRows) { return nil, ErrUserNotFound } // Custom error types type ValidationError struct { Field string Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("%s: %s", e.Field, e.Message) } ``` ## Logging Use zerolog for structured logging: ```go import "github.com/rs/zerolog/log" log.Info(). Str("user_id", userID). Msg("User logged in") log.Error(). Err(err). Str("operation", "database_query"). Msg("Failed to query database") ``` ## Context Usage Always pass context as the first parameter: ```go func ProcessRequest(ctx context.Context, userID string) error { // Check for cancellation select { case <-ctx.Done(): return ctx.Err() default: } // Pass context to downstream calls user, err := userRepo.GetByID(ctx, userID) if err != nil { return err } return nil } ``` ## Testing - Write table-driven tests - Use descriptive test names: `TestUserService_Create_WithValidData_Success` - Mock external dependencies - Aim for high coverage of business logic - Use t.Run for subtests ```go func TestUserService_Create(t *testing.T) { tests := []struct { name string input *User wantErr bool }{ { name: "valid user", input: &User{Name: "John", Email: "john@example.com"}, wantErr: false, }, { name: "missing email", input: &User{Name: "John"}, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { service := NewUserService() err := service.Create(tt.input) if (err != nil) != tt.wantErr { t.Errorf("Create() error = %v, wantErr %v", err, tt.wantErr) } }) } } ``` ## Database Operations - Use transactions for multiple related operations - Always use prepared statements - Handle NULL values properly - Use meaningful struct tags ```go type User struct { ID string `gorm:"primaryKey" json:"id"` Name string `gorm:"not null" json:"name"` Email string `gorm:"uniqueIndex;not null" json:"email"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } ``` ## ResolveSpec Integration When using ResolveSpec: - Register all models in the registry - Use schema.table format: "public.users", "core.accounts" - Implement hooks for auth and validation - Use lifecycle hooks for audit logging ## API Design - Use RESTful conventions - Return appropriate HTTP status codes - Include meaningful error messages - Version your APIs: `/api/v1/users` - Document all endpoints ## Security - Never log sensitive data (passwords, tokens, etc.) - Validate all input - Use parameterized queries - Implement rate limiting - Use HTTPS in production - Sanitize user input - Implement proper CORS policies ## Dependencies - Keep dependencies minimal - Pin versions in go.mod - Regularly update dependencies - Document why each dependency is needed ## Documentation - Document all exported functions, types, and packages - Use godoc format - Include examples in documentation - Keep README.md up to date - Document configuration options ## Comments ```go // Package auth provides authentication and authorization functionality. package auth // User represents a system user. type User struct { ID string } // Authenticate verifies user credentials and returns a token. // Returns ErrInvalidCredentials if authentication fails. func Authenticate(ctx context.Context, username, password string) (string, error) { // Implementation } ``` ## Performance - Use connection pools for databases - Implement caching where appropriate - Avoid N+1 queries - Use batch operations when possible - Profile critical paths - Set appropriate timeouts ## Git Workflow - Write clear commit messages - Keep commits atomic and focused - Use conventional commits format - Create feature branches - Run tests before committing - Review your own changes before pushing