Thank you for your interest in contributing to goburn! This guide will help you understand the codebase and make effective contributions.
The codebase follows clear separation of concerns:
-
hardware: System interaction layer
- Pure functions, no state
- Linux sysfs reading only
- No dependencies on other packages
-
worker: Business logic layer
- Manages worker lifecycle
- No UI concerns
- No hardware reading
-
ui: Presentation layer
- Uses hardware and worker packages
- No business logic
- Two independent implementations
-
main: Orchestration layer
- Minimal logic
- CLI flag parsing
- Delegates to appropriate packages
- Package by Feature: Each package represents a distinct feature
- Dependency Injection: Pass dependencies (counter, duration) explicitly
- Interface Segregation: Packages export only what's needed
- Single Responsibility: Each file has one clear purpose
- Packages: Short, lowercase, single word (e.g.,
worker, notworkers) - Exported Types: PascalCase (e.g.,
Pool,Stats) - Unexported Functions: camelCase (e.g.,
getCPUFrequency) - Constants: PascalCase or UPPER_SNAKE_CASE for emphasis
Every exported symbol must have a comment:
// Get retrieves current hardware statistics from the system.
func Get() Stats {
// ...
}Format:
- Start with the symbol name
- Use present tense ("retrieves", not "retrieve")
- Explain what, not how (details go in code comments)
- Return errors, don't panic
- Handle errors at the appropriate level
- Log errors only when they matter
- Gracefully degrade when hardware unavailable
Example:
// Good: Return zero value on error, caller decides importance
func getCPUTemperature() float64 {
temp, err := readFileInt(path)
if err != nil {
return 0 // Caller checks if > 0
}
return float64(temp) / 1000.0
}- Use channels for signaling, not data sharing
- Atomic operations for counters only
- Document goroutine lifecycle
- Ensure graceful shutdown
Create *_test.go files alongside source:
// hardware/stats_test.go
func TestReadFileInt(t *testing.T) {
// Test with temp file
}Test package interactions:
// worker/pool_test.go
func TestPoolScaling(t *testing.T) {
// Test worker add/remove
}Always test both modes:
# Test line mode
./goburn -duration=10s
# Test graph mode
./goburn -duration=10s -graph
# Test interactive controls
# In graph mode: press +, -, verify workers change- Add to hardware package:
// hardware/stats.go
type Stats struct {
// ... existing fields ...
MemoryUsedMB int // Memory used in MB
MemoryTotalMB int // Total memory in MB
}
func getMemoryUsage() (used, total int) {
// Read from /proc/meminfo
return
}
func Get() Stats {
stats := Stats{}
// ... existing code ...
stats.MemoryUsedMB, stats.MemoryTotalMB = getMemoryUsage()
return stats
}- Add to line mode:
// ui/line.go
func formatHardwareStats(stats hardware.Stats) string {
// ... existing code ...
if stats.MemoryTotalMB > 0 {
parts = append(parts, fmt.Sprintf("mem=%d/%dMB",
stats.MemoryUsedMB, stats.MemoryTotalMB))
}
// ... existing code ...
}- Add to TUI (optional):
// ui/tui.go
type Model struct {
// ... existing fields ...
memHistory []float64
}
// Update updateHistory()
// Update renderGraphs() to add 3rd row or replace existing graph- Test thoroughly:
go build
./goburn -duration=30s # Verify line output
./goburn -duration=30s -graph # Verify TUI shows it❌ Bad:
// hardware/stats.go
func Get() Stats {
stats := Stats{}
// ... get stats ...
fmt.Println("CPU:", stats.CPUFreqCur) // NO! UI concern
return stats
}✅ Good:
// hardware/stats.go
func Get() Stats {
stats := Stats{}
// ... get stats ...
return stats // Just return data
}❌ Bad:
hardware → ui → hardware // Circular!
✅ Good:
main → ui → hardware // One direction
→ worker
❌ Bad:
// worker/pool.go
func (wp *Pool) SetWorkers(n int) {
fmt.Printf("Setting workers to %d\n", n) // NO!
}✅ Good:
// worker/pool.go
func (wp *Pool) SetWorkers(n int) {
// Just do the work, let caller handle UI
}Before submitting:
- Code builds without warnings:
go build - Code is formatted:
go fmt ./... - All packages have package documentation
- Exported symbols have doc comments
- Manual testing in both modes completed
- No new dependencies added without discussion
- README.md updated if user-facing changes
- CONTRIBUTING.md updated if architecture changes
- Read the code! It's well-documented.
- Check existing patterns before inventing new ones
- Keep it simple - complexity is technical debt
- Hardware stats reading: Limit to ~1 Hz, file I/O is slow
- Worker operations: Batch counter updates (current: 1M ops)
- TUI updates: 1 Hz is smooth enough, more wastes CPU
- Graph history: 60 points is sufficient, more wastes memory
Some ideas for future contributors:
- Add network I/O stress testing
- Add disk I/O stress testing
- Support macOS hardware monitoring
- Add JSON output mode for scripting
- Add configuration file support
- Add benchmark comparison mode
- Add GPU monitoring/stress testing
- Add web server mode for remote monitoring
Choose one that interests you and open an issue to discuss before implementing!