Zum Inhalt springen

Coding Standards

Dieser Inhalt ist noch nicht in deiner Sprache verfügbar.

Following consistent coding standards makes the codebase easier to read, maintain, and contribute to.

Use standard Go formatting tools:

Terminal window
# Format all code
gofmt -w .
# Use goimports for import organization
goimports -w .

Required: All Go code must pass gofmt and goimports before committing.

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
// Good
type Closer interface {
Close() error
}
// Avoid
type CloseInterface interface {
Close() error
}

Always check errors:

// Good
result, err := doSomething()
if err != nil {
return fmt.Errorf("failed to do something: %w", err)
}
// Bad - ignoring errors
result, _ := doSomething()

Use error wrapping:

// Wrap errors to provide context
if err := validate(); err != nil {
return fmt.Errorf("validation failed: %w", err)
}

Create custom error types when needed:

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)
}

Package comments:

// Package application provides the core Wails application runtime.
//
// It handles window management, event dispatching, and service lifecycle.
package application

Exported declarations:

// 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:

// 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
// ...
}

Keep functions focused:

// 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:

// 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

Use context for cancellation:

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:

type SafeCounter struct {
mu sync.Mutex
count int
}
func (c *SafeCounter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}

Avoid goroutine leaks:

// 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)
}
}
}()
}

Test file naming:

window.go
// Tests: window_test.go

Table-driven tests:

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)
}
})
}
}

Use Prettier for consistent formatting:

{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}

Variables and functions:

  • camelCase: const userName = "John"

Classes and types:

  • PascalCase: class WindowManager

Constants:

  • UPPER_SNAKE_CASE: const MAX_RETRIES = 3

Use explicit types:

// Good
function greet(name: string): string {
return `Hello, ${name}`
}
// Avoid implicit any
function process(data) { // Bad
return data
}

Define interfaces:

interface WindowOptions {
title: string
width: number
height: number
}
function createWindow(options: WindowOptions): void {
// ...
}

Use Conventional Commits:

<type>(<scope>): <subject>
<body>
<footer>

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • refactor: Code refactoring
  • test: Adding or updating tests
  • chore: Maintenance tasks

Examples:

feat(window): add SetAlwaysOnTop method
Implement SetAlwaysOnTop for keeping windows above others.
Adds platform implementations for macOS, Windows, and Linux.
Closes #123
fix(events): prevent event handler memory leak
Event listeners were not being properly cleaned up when
windows were closed. This adds explicit cleanup in the
window destructor.
  • Code passes gofmt and goimports
  • All tests pass (go test ./...)
  • New code has tests
  • Documentation updated if needed
  • Commit messages follow conventions
  • No merge conflicts with master
## Description
Brief description of what this PR does.
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
How was this tested?
## Checklist
- [ ] Tests pass
- [ ] Documentation updated
- [ ] No breaking changes (or documented)
  • Be constructive and respectful
  • Focus on code quality, not personal preferences
  • Explain why changes are suggested
  • Approve once satisfied
  • Respond to all comments
  • Ask for clarification if needed
  • Make requested changes or explain why not
  • Be open to feedback
  • Avoid premature optimization
  • Profile before optimizing
  • Use benchmarks for performance-critical code
func BenchmarkProcess(b *testing.B) {
for i := 0; i < b.N; i++ {
process(testData)
}
}
  • Validate all user input
  • Sanitize data before display
  • Use crypto/rand for random data
  • Never log sensitive information
  • Document exported APIs
  • Include examples in documentation
  • Update docs when changing APIs
  • Keep README files current
window.go // Common interface
window_darwin.go // macOS implementation
window_windows.go // Windows implementation
window_linux.go // Linux implementation
//go:build darwin
package application
// macOS-specific code

Run linters before committing:

Terminal window
# golangci-lint (recommended)
golangci-lint run
# Individual linters
go vet ./...
staticcheck ./...

If you’re unsure about any standards:

  • Check existing code for examples
  • Ask in Discord
  • Open a discussion on GitHub