--- title: Coding Standards description: Code style, conventions, and best practices for Wails v3 --- ## Code Style and Conventions Following consistent coding standards makes the codebase easier to read, maintain, and contribute to. ## Go Code Standards ### Code Formatting Use standard Go formatting tools: ```bash # Format all code gofmt -w . # Use goimports for import organization goimports -w . ``` **Required:** All Go code must pass `gofmt` and `goimports` before committing. ### Naming Conventions **Packages:** - Lowercase, single word when possible - `package application`, `package events` - Avoid underscores or mixed caps **Exported Names:** - PascalCase for types, functions, constants - `type WebviewWindow struct`, `func NewApplication()` **Unexported Names:** - camelCase for internal types, functions, variables - `type windowImpl struct`, `func createWindow()` **Interfaces:** - Name by behavior: `Reader`, `Writer`, `Handler` - Single-method interfaces: name with `-er` suffix ```go // Good type Closer interface { Close() error } // Avoid type CloseInterface interface { Close() error } ``` ### Error Handling **Always check errors:** ```go // Good result, err := doSomething() if err != nil { return fmt.Errorf("failed to do something: %w", err) } // Bad - ignoring errors result, _ := doSomething() ``` **Use error wrapping:** ```go // Wrap errors to provide context if err := validate(); err != nil { return fmt.Errorf("validation failed: %w", err) } ``` **Create custom error types when needed:** ```go type ValidationError struct { Field string Value string } func (e *ValidationError) Error() string { return fmt.Sprintf("invalid value %q for field %q", e.Value, e.Field) } ``` ### Comments and Documentation **Package comments:** ```go // Package application provides the core Wails application runtime. // // It handles window management, event dispatching, and service lifecycle. package application ``` **Exported declarations:** ```go // NewApplication creates a new Wails application with the given options. // // The application must be started with Run() or RunWithContext(). func NewApplication(opts Options) *Application { // ... } ``` **Implementation comments:** ```go // processEvent handles incoming events from the runtime. // It dispatches to registered handlers and manages event lifecycle. func (a *Application) processEvent(event *Event) { // Validate event before processing if event == nil { return } // Find and invoke handlers // ... } ``` ### Function and Method Structure **Keep functions focused:** ```go // Good - single responsibility func (w *Window) setTitle(title string) { w.title = title w.updateNativeTitle() } // Bad - doing too much func (w *Window) updateEverything() { w.setTitle(w.title) w.setSize(w.width, w.height) w.setPosition(w.x, w.y) // ... 20 more operations } ``` **Use early returns:** ```go // Good func validate(input string) error { if input == "" { return errors.New("empty input") } if len(input) > 100 { return errors.New("input too long") } return nil } // Avoid deep nesting ``` ### Concurrency **Use context for cancellation:** ```go func (a *Application) RunWithContext(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() case <-a.done: return nil } } ``` **Protect shared state with mutexes:** ```go type SafeCounter struct { mu sync.Mutex count int } func (c *SafeCounter) Increment() { c.mu.Lock() defer c.mu.Unlock() c.count++ } ``` **Avoid goroutine leaks:** ```go // Good - goroutine has exit condition func (a *Application) startWorker(ctx context.Context) { go func() { for { select { case <-ctx.Done(): return // Clean exit case work := <-a.workChan: a.process(work) } } }() } ``` ### Testing **Test file naming:** ```go // Implementation: window.go // Tests: window_test.go ``` **Table-driven tests:** ```go func TestValidate(t *testing.T) { tests := []struct { name string input string wantErr bool }{ {"empty input", "", true}, {"valid input", "hello", false}, {"too long", strings.Repeat("a", 101), true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := validate(tt.input) if (err != nil) != tt.wantErr { t.Errorf("validate() error = %v, wantErr %v", err, tt.wantErr) } }) } } ``` ## JavaScript/TypeScript Standards ### Code Formatting Use Prettier for consistent formatting: ```json { "semi": false, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5" } ``` ### Naming Conventions **Variables and functions:** - camelCase: `const userName = "John"` **Classes and types:** - PascalCase: `class WindowManager` **Constants:** - UPPER_SNAKE_CASE: `const MAX_RETRIES = 3` ### TypeScript **Use explicit types:** ```typescript // Good function greet(name: string): string { return `Hello, ${name}` } // Avoid implicit any function process(data) { // Bad return data } ``` **Define interfaces:** ```typescript interface WindowOptions { title: string width: number height: number } function createWindow(options: WindowOptions): void { // ... } ``` ## Commit Message Format Use [Conventional Commits](https://www.conventionalcommits.org/): ``` ():