Goverhaul is a high-performance CLI tool to enforce architectural rules in Go projects. It helps teams maintain the intended architecture by defining and enforcing import boundaries between packages, with intelligent caching for blazing-fast analysis.
- Define allowed imports for specific package paths
- Define prohibited imports with explanatory causes
- Generate visual dependency graphs
- High-performance caching using MUS binary encoding
- Incremental analysis for fast subsequent runs
- Simple YAML configuration
- Easy CI/CD integration
Goverhaul uses MUS (Marshal/Unmarshal/Size) binary serialization for cache storage, delivering exceptional performance:
- 2,700+ MB/s encoding throughput
- 1,000+ MB/s decoding throughput
- Single allocation design for minimal GC pressure
- Compact binary format for efficient storage
- Linear scalability from small projects to enterprise monorepos
| Project Size | Cache Write | Cache Read | Memory Usage |
|---|---|---|---|
| Small (100 files) | <1ms | <1ms | Negligible |
| Medium (1,000 files) | ~100μs | ~200μs | <200KB |
| Large (10,000 files) | ~760μs | ~2ms | ~2MB |
| Enterprise (100,000 files) | ~7.4ms | ~14.3ms | ~20MB |
This means faster CI/CD pipelines, real-time IDE integration, and efficient analysis for projects of any size.
go install github.com/gophersatwork/goverhaul@latestgo get -tool github.com/gophersatwork/goverhaul@latestgit clone https://github.com/gophersatwork/goverhaul.git
cd goverhaul
go build-
Install Goverhaul:
go install github.com/gophersatwork/goverhaul@latest
-
Create a
.goverhaul.ymlconfiguration:# Enable high-performance incremental analysis incremental: true cache_file: ".goverhaul.cache" rules: # Domain layer should not depend on infrastructure - path: "internal/domain" prohibited: - name: "internal/infrastructure" cause: "Domain should not depend on infrastructure" # API layer should not access database directly - path: "internal/api" prohibited: - name: "internal/database" cause: "API should access database through domain services"
-
Run Goverhaul:
goverhaul --path . --config .goverhaul.yml -
Subsequent runs use the cache for instant results!
# Run with default configuration
goverhaul --path . --config .goverhaul.yml
# Enable verbose logging
goverhaul --path . --config .goverhaul.yml --verbose
# Analyze specific directory
goverhaul --path ./internal --config .goverhaul.yml--path: Path to analyze (default: ".")--config: Configuration file path (default: "$HOME/.goverhaul.yml")--verbose: Enable verbose logging for debugging
Goverhaul uses YAML for configuration. Here's a complete example:
# Path to go.mod file
modfile: "go.mod"
# Enable incremental analysis with high-performance MUS caching
incremental: true
# Cache file location (uses MUS binary encoding)
cache_file: ".goverhaul.cache"
rules:
# Core business logic isolation
- path: "internal/core"
allowed:
- "context"
- "errors"
- "fmt"
- "time"
- "internal/core"
prohibited:
- name: "internal/api"
cause: "Core should not depend on API layer"
- name: "internal/db"
cause: "Core should not depend on database layer"
# API layer constraints
- path: "internal/api"
prohibited:
- name: "internal/db"
cause: "API should access database through core interfaces"- modfile: Path to go.mod file (default: "go.mod")
- incremental: Enable incremental analysis for faster runs (default: false)
- cache_file: Cache file location (default: "$HOME/.goverhaul/cache.json")
- rules: List of architectural rules
- path: Package path to apply the rule to
- allowed: Whitelist of permitted imports
- prohibited: Blacklist of forbidden imports
- name: Package name to prohibit
- cause: Explanation for the prohibition
Goverhaul's caching system uses MUS binary encoding for exceptional performance:
- Minimal allocations: Single allocation per operation
- Excellent throughput: 2,700+ MB/s encoding, 1,000+ MB/s decoding
- Compact size: Efficient varint encoding for small cache files
- Linear scalability: Consistent performance from 10 to 100,000 files
- First run: Analyzes all files and builds the cache
- Subsequent runs: Only analyzes changed files
- CI/CD: Dramatically reduced build times
- IDE integration: Real-time feedback with minimal overhead
By default, the cache is stored in $HOME/.goverhaul/cache.json. You can customize this:
cache_file: ".goverhaul.cache" # Project-local cacheOr use an absolute path:
cache_file: "/tmp/goverhaul.cache" # Temporary cacherules:
# Domain entities: no external dependencies
- path: "internal/domain"
allowed:
- "fmt"
- "errors"
- "context"
- "time"
# Use cases: depend on domain only
- path: "internal/usecase"
allowed:
- "fmt"
- "errors"
- "context"
- "internal/domain"
prohibited:
- name: "internal/infrastructure"
cause: "Use cases should depend on interfaces, not implementations"
# Infrastructure: implements interfaces
- path: "internal/infrastructure"
prohibited:
- name: "internal/usecase"
cause: "Infrastructure should not depend on use cases"rules:
# Core domain: completely isolated
- path: "pkg/core"
prohibited:
- name: "pkg/api"
cause: "Core should not depend on adapters"
- name: "pkg/db"
cause: "Core should not depend on adapters"
- name: "pkg/messaging"
cause: "Core should not depend on adapters"
# Adapters: can use core
- path: "pkg/api"
prohibited:
- name: "pkg/db"
cause: "Adapters should not depend on each other"rules:
# Presentation layer
- path: "internal/presentation"
prohibited:
- name: "internal/data"
cause: "Presentation should access data through business layer"
# Business layer
- path: "internal/business"
prohibited:
- name: "internal/presentation"
cause: "Business should not depend on presentation"
# Data layer
- path: "internal/data"
prohibited:
- name: "internal/business"
cause: "Data should not depend on business"
- name: "internal/presentation"
cause: "Data should not depend on presentation"name: Architecture Check
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
goverhaul:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.24'
- name: Install Goverhaul
run: go install github.com/gophersatwork/goverhaul@latest
- name: Cache Goverhaul results
uses: actions/cache@v3
with:
path: .goverhaul.cache
key: goverhaul-${{ hashFiles('**/*.go') }}
restore-keys: goverhaul-
- name: Check architecture
run: goverhaul --path . --config .goverhaul.ymlarchitecture-check:
stage: test
image: golang:1.24
script:
- go install github.com/gophersatwork/goverhaul@latest
- goverhaul --path . --config .goverhaul.yml
cache:
key: goverhaul-${CI_COMMIT_REF_SLUG}
paths:
- .goverhaul.cacheMaintain the dependency rule:
- Domain entities have no external dependencies
- Use cases depend only on domain entities
- Adapters depend on use cases but not frameworks
- Frameworks are isolated at the boundaries
For multi-module projects:
- Define clear boundaries between modules
- Enforce API contracts
- Prevent implementation leakage
Limit external dependency spread:
- Restrict third-party library usage
- Isolate framework dependencies
- Keep core business logic clean
Use rules as executable documentation:
- Encode architectural decisions
- Make constraints visible
- Help new team members
Focus on the most important architectural constraints first:
rules:
# Start here: protect core business logic
- path: "internal/domain"
prohibited:
- name: "external/dependencies"
cause: "Domain should be framework-agnostic"For large projects, always enable caching:
incremental: true
cache_file: ".goverhaul.cache"This dramatically reduces analysis time for subsequent runs.
Make violations easy to understand:
prohibited:
- name: "internal/db"
cause: "API layer should access database through repository interfaces defined in domain layer"Commit .goverhaul.yml to track architectural decisions over time.
Add Goverhaul to CI/CD pipelines early to prevent architectural drift.
Issue: Rule doesn't seem to work.
Solutions:
- Verify path matches your package structure
- Use
--verboseto see which files are analyzed - Check package declarations in Go files
Issue: Incremental analysis not detecting changes.
Solutions:
- Delete cache file and run again
- Verify cache file location is correct
- Ensure file permissions allow cache writes
Issue: Works locally but fails in CI.
Solutions:
- Verify Go version matches
- Check configuration file is in repository
- Use absolute paths in CI configuration
- Add
--verbosefor debug output
- Enable incremental analysis: Mandatory for 1,000+ files
- Use local cache: Place cache in project directory
- Fast storage: Put cache on SSD/NVMe
- Parallel CI: Cache persists across builds
- Pre-allocate cache: First run may be slower, subsequent runs are instant
- Network storage: Avoid network-mounted cache locations
- Memory: More RAM helps for very large projects (100,000+ files)
Check the examples directory for complete examples:
- layered-architecture: Three-tier architecture enforcement
- microservices: Service boundary enforcement
- monolith: Module isolation in monolithic applications
Contributions are welcome! Please feel free to open a discussion or PR.
See CONTRIBUTING.md for guidelines.
GNU General Public License (GPL v3)
For implementation details about the high-performance MUS cache, see IMPLEMENTATION_SUMMARY.md.
