# Feature Specification: Development Guidelines & Best Practices

**ID:** DEV-001
**Version:** 1.0
**Status:** Planned
**Priority:** High

## Overview
Guidelines for development workflow, code quality, testing, and contribution standards for OpenSpeak project.

## Development Workflow

### Setting Up Development Environment

#### Prerequisites
- Go 1.21+
- Git
- Protobuf compiler (protoc) v3.20+
- Visual Studio Code or preferred IDE
- Audio libraries (PortAudio, libopus)

#### Initial Setup
```bash
# Clone repository
git clone https://github.com/yourusername/openspeak.git
cd openspeak

# Install dependencies
go mod download
go mod tidy

# Install tools
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

# Setup pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
go fmt ./...
golangci-lint run ./...
go test ./...
EOF
chmod +x .git/hooks/pre-commit
```

### Development Commands

#### Build
```bash
# Build server
go build -o bin/openspeak-server ./cmd/openspeak-server

# Build client
go build -o bin/openspeak-client ./cmd/openspeak-client

# Build all
make build

# Debug build (with symbols)
go build -gcflags="all=-N -l" -o bin/openspeak-server ./cmd/openspeak-server
```

#### Run
```bash
# Run server with default config
./bin/openspeak-server

# Run with custom config
./bin/openspeak-server --config config.dev.yaml

# Run with debug logging
OPENSPEAK_LOG_LEVEL=debug ./bin/openspeak-server

# Run client
./bin/openspeak-client
```

#### Test
```bash
# Run all tests
go test ./...

# Run with coverage
go test -cover ./...

# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

# Run specific test
go test -run TestChannelManager ./internal/channel

# Run with verbose output
go test -v ./...

# Run benchmarks
go test -bench=. -benchmem ./...

# Run tests with race detector
go test -race ./...
```

#### Code Quality
```bash
# Format code
go fmt ./...

# Run linters
golangci-lint run ./...

# Vet code
go vet ./...

# Check imports
goimports -w ./...

# Staticcheck
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...
```

#### Protocol Buffers
```bash
# Generate protobuf code
make proto

# Equivalent to:
protoc --go_out=. --go-grpc_out=. proto/*.proto

# Watch for changes
watch -n 1 'make proto'
```

#### Documentation
```bash
# Generate documentation
go doc ./...

# View package docs
godoc -http=:6060
# Then open http://localhost:6060
```

### Makefile
```makefile
.PHONY: build test lint fmt clean proto run-server run-client coverage

all: fmt lint test build

build:
	@echo "Building server and client..."
	go build -o bin/openspeak-server ./cmd/openspeak-server
	go build -o bin/openspeak-client ./cmd/openspeak-client

test:
	@echo "Running tests..."
	go test -v -race -coverprofile=coverage.out ./...
	go tool cover -func=coverage.out

lint:
	@echo "Running linters..."
	go fmt ./...
	go vet ./...
	golangci-lint run ./...

fmt:
	@echo "Formatting code..."
	go fmt ./...
	goimports -w ./...

clean:
	@echo "Cleaning..."
	rm -rf bin/
	rm -f coverage.out

proto:
	@echo "Generating protobuf code..."
	protoc --go_out=. --go-grpc_out=. proto/*.proto

run-server:
	go run ./cmd/openspeak-server -- --config config.dev.yaml

run-client:
	go run ./cmd/openspeak-client

coverage:
	@echo "Generating coverage report..."
	go test -coverprofile=coverage.out ./...
	go tool cover -html=coverage.out -o coverage.html
	@echo "Report saved to coverage.html"

coverage-watch:
	watch -n 2 'make coverage'

bench:
	@echo "Running benchmarks..."
	go test -bench=. -benchmem ./...

help:
	@echo "Available targets:"
	@grep -E '^[a-zA-Z_-]+:' Makefile | sed 's/:.*//g' | sort
```

## Code Style & Conventions

### Go Code Style
Follow [Effective Go](https://golang.org/doc/effective_go) and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).

#### Naming Conventions
```go
// Interfaces: adjective names (Reader, Writer, Closer)
type Reader interface {
  Read(p []byte) (n int, err error)
}

// Packages: short, single-word names
package voice

// Exported: PascalCase
func PublishVoicePacket(packet *VoicePacket) error {}

// Unexported: camelCase
func publishVoicePacket(packet *voicePacket) error {}

// Constants: UPPER_SNAKE_CASE or PascalCase
const (
  DefaultBitrate = 64
  MaxBitrate = 128
)

// Errors: errors.New or fmt.Errorf, start with lowercase or "Error" prefix
var ErrChannelNotFound = errors.New("channel not found")
var ErrInvalidBitrate = fmt.Errorf("invalid bitrate: %d", bitrate)

// Receiver names: short (1-2 chars)
func (m *Manager) CreateChannel(name string) (*Channel, error) {}
```

#### File Organization
```go
// 1. Package declaration
package channel

// 2. Imports (stdlib, third-party, internal)
import (
  "context"
  "sync"

  "google.golang.org/grpc"

  "openspeak/internal/logger"
)

// 3. Constants
const (
  DefaultMaxUsers = 0 // unlimited
)

// 4. Errors
var (
  ErrChannelNotFound = errors.New("channel not found")
  ErrChannelFull     = errors.New("channel is full")
)

// 5. Type definitions
type Channel struct {
  ID        string
  Name      string
  Members   []string
  MaxUsers  int
  CreatedAt time.Time
}

// 6. Receiver methods (sorted by type)
func (c *Channel) IsFull() bool {
  return c.MaxUsers > 0 && len(c.Members) >= c.MaxUsers
}

// 7. Package-level functions
func NewChannel(name string) *Channel {
  return &Channel{
    ID:        generateID(),
    Name:      name,
    Members:   []string{},
    MaxUsers:  0,
    CreatedAt: time.Now(),
  }
}
```

#### Error Handling
```go
// Good: Simple, clear error propagation
func (m *Manager) CreateChannel(name string) (*Channel, error) {
  if err := validateName(name); err != nil {
    return nil, fmt.Errorf("validate name: %w", err)
  }

  channel := NewChannel(name)
  if err := m.store.Save(channel); err != nil {
    return nil, fmt.Errorf("save channel: %w", err)
  }

  return channel, nil
}

// Bad: Ignoring errors
func (m *Manager) CreateChannel(name string) (*Channel, error) {
  validateName(name)  // Error ignored!
  channel := NewChannel(name)
  m.store.Save(channel)  // Error ignored!
  return channel, nil
}

// Bad: Generic error messages
return nil, errors.New("error")  // Useless error message
```

#### Comments
```go
// Good: Explains purpose, not what code does
// ChannelManager handles creation and deletion of voice channels.
// It maintains the current state of all channels and enforces permissions.
type Manager struct {
  channels map[string]*Channel
  mu       sync.RWMutex
}

// Bad: Explains code, not purpose
// This is a map of channels
channels := make(map[string]*Channel)

// Good: Exported functions have godoc
// PublishVoicePacket accepts a voice packet from a client and broadcasts it
// to all members of the packet's channel.
func PublishVoicePacket(packet *VoicePacket) error {
  // ...
}

// Bad: No comment on exported function
func PublishVoicePacket(packet *VoicePacket) error {
  // ...
}
```

### Project Layout
```
openspeak/
├── cmd/                      # Executable entry points
│   ├── openspeak-server/
│   │   └── main.go
│   └── openspeak-client/
│       └── main.go
├── internal/                 # Private packages (not importable from outside)
│   ├── auth/
│   ├── channel/
│   ├── presence/
│   ├── voice/
│   ├── config/
│   ├── logger/
│   └── grpc/
├── proto/                    # Protocol buffer definitions
│   ├── common.proto
│   ├── auth.proto
│   ├── channel.proto
│   ├── presence.proto
│   └── voice.proto
├── pkg/                      # Generated code (protobuf)
│   └── api/
│       └── openspeak/
│           └── v1/
├── config/                   # Configuration files
│   ├── config.yaml
│   └── config.dev.yaml
├── test/                     # Test utilities and fixtures
│   ├── fixtures/
│   └── mocks/
├── Makefile
├── go.mod
├── go.sum
├── README.md
├── CONTRIBUTING.md
└── LICENSE
```

## Testing Standards

### Test Structure
```go
// File: internal/channel/channel_test.go
package channel

import (
  "testing"
)

// TestChannelCreation tests basic channel creation
func TestChannelCreation(t *testing.T) {
  // Arrange
  name := "general"

  // Act
  channel := NewChannel(name)

  // Assert
  if channel.Name != name {
    t.Errorf("expected name %q, got %q", name, channel.Name)
  }

  if channel.ID == "" {
    t.Error("channel ID should not be empty")
  }
}

// TestChannelIsFull tests capacity checking
func TestChannelIsFull(t *testing.T) {
  tests := []struct {
    name     string
    maxUsers int
    members  int
    want     bool
  }{
    {"unlimited capacity", 0, 100, false},
    {"not full", 10, 5, false},
    {"exactly full", 10, 10, true},
    {"over capacity", 10, 11, true},
  }

  for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
      channel := NewChannel("test")
      channel.MaxUsers = tt.maxUsers
      channel.Members = make([]string, tt.members)

      got := channel.IsFull()
      if got != tt.want {
        t.Errorf("IsFull() = %v, want %v", got, tt.want)
      }
    })
  }
}
```

### Test Requirements
- Write tests for all exported functions
- Use table-driven tests for multiple cases
- Use sub-tests (t.Run) for related test cases
- Mock external dependencies
- Test both success and error cases
- Write benchmarks for performance-critical code

### Coverage Goals
- Unit tests: Aim for 80%+ coverage
- Integration tests: Critical paths
- Packages with <50% coverage: Flag in review

## Git Workflow

### Branch Naming
```
feature/<description>      # New feature
bugfix/<description>       # Bug fix
docs/<description>         # Documentation
refactor/<description>     # Refactoring
perf/<description>         # Performance improvement
test/<description>         # Test additions
```

### Commit Messages
Follow [Conventional Commits](https://www.conventionalcommits.org/):

```
<type>(<scope>): <subject>

<body>

<footer>
```

**Types:**
- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation
- `style`: Code style (formatting, etc)
- `refactor`: Code refactoring
- `perf`: Performance improvement
- `test`: Test additions/changes
- `chore`: Build, CI, dependencies

**Examples:**
```
feat(channel): add channel archival feature

Implement soft delete for channels. Users can still access archived
channels, but no new users can join them. Includes audit logging.

Fixes #123
Closes #456

feat(voice): implement Opus codec support

refactor(presence): simplify idle detection

docs(server): add deployment guide

fix(auth): validate token format before checking expiration

perf(voice): optimize packet routing for large channels
```

### Pull Request Process
1. Create feature branch from `main`
2. Make commits with clear messages
3. Keep commits focused and logical
4. Push to your fork
5. Create PR with description
6. Respond to code review feedback
7. Merge once approved

## Documentation Standards

### Code Documentation
- All exported functions and types must have godoc comments
- Comments should explain "why", not "what"
- Use examples in documentation for complex functions

### README Files
- Package-level `README.md` in each major directory
- Usage examples
- Important design decisions
- Known limitations

### Architecture Documentation
- Decision records for major changes
- Diagrams for complex systems
- Examples of common usage patterns

## Debugging Techniques

### Logging
```go
// Use structured logging
logger.Info("user connected",
  "user_id", userID,
  "channel_id", channelID,
  "timestamp", time.Now(),
)

logger.Error("voice routing failed",
  "error", err,
  "packet_id", packetID,
  "channel_id", channelID,
)
```

### Profiling
```bash
# CPU profiling
go run -cpuprofile=cpu.prof ./cmd/openspeak-server

# Memory profiling
go run -memprofile=mem.prof ./cmd/openspeak-server

# View profiles
go tool pprof cpu.prof
go tool pprof mem.prof

# Live profiling (server running)
go tool pprof http://localhost:6060/debug/pprof/profile
```

### Debugging with Delve
```bash
# Install
go install github.com/go-delve/delve/cmd/dlv@latest

# Run server with debugger
dlv debug ./cmd/openspeak-server

# Commands in debugger:
# (dlv) break main.main
# (dlv) continue
# (dlv) next
# (dlv) step
# (dlv) print variable
# (dlv) quit
```

## Performance Optimization

### Benchmarking
```bash
# Run benchmarks
go test -bench=. -benchmem ./internal/voice

# Output:
# BenchmarkPublishVoicePacket-8    100000    10500 ns/op    2048 B/op    8 allocs/op
```

### Memory Optimization
- Reuse buffers with sync.Pool
- Avoid unnecessary allocations
- Use value types for small structs
- Profile before optimizing

### Concurrency Optimization
- Use sync.Map for high-concurrency maps
- Avoid locks in hot paths
- Use channels for coordination
- Profile with -race flag

## Continuous Integration (Future)

### GitHub Actions Workflow
```yaml
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v4
        with:
          go-version: '1.21'
      - run: make lint
      - run: make test
      - run: make coverage

  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: securego/gosec@master
```

## Version Management

### Semantic Versioning
- MAJOR: Breaking changes
- MINOR: New features (backwards compatible)
- PATCH: Bug fixes

**Tag format:** `v1.2.3`

```bash
# Create release
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
```

## Contributing Guidelines
- Follow all above conventions
- Write tests for new code
- Update documentation
- Get code reviewed
- Keep commits clean
- Respond to feedback professionally
