Goverhaul is a CLI tool to enforce architectural rules in Go projects. It helps teams maintain the intended architecture by defining and enforcing import boundaries between packages.
- Define allowed imports for specific package paths
- Define prohibited imports for specific package paths with explanatory causes
- Generate visual dependency graphs to better understand architectural violations
- Simple YAML configuration
- Easy integration into CI/CD pipelines
go install github.com/gophersatwork/goverhaul@latest
git clone https://github.com/gophersatwork/goverhaul.git && cd goverhaul && go build
Run goverhaul in your project directory:
goverhaul --path . --config .goverhaul.yml
-
Install Goverhaul:
go install github.com/gophersatwork/goverhaul@latest
-
Create a
.goverhaul.yml
file in your project root:# Basic configuration for a typical layered architecture 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
-
Integrate with CI/CD (example GitHub Actions workflow):
# .github/workflows/architecture.yml 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: Check architecture run: goverhaul --path . --config .goverhaul.yml
Copy and paste this complete example to get started immediately:
# Install Goverhaul
`go install github.com/gophersatwork/goverhaul@latest`
or with you are using go tool directive (1.24+):
`go get -tool github.com/gophersatwork/goverhaul@latest`
# Create a configuration file
cat > .goverhaul.yml << EOL
# Goverhaul configuration
modfile: "go.mod"
incremental: true
cache_file: ".goverhaul.cache"
rules:
# Core business logic should not depend on external packages
- path: "internal/core"
allowed:
- "context"
- "errors"
- "fmt"
- "time"
- "encoding/json"
- "strings"
- "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"
- name: "github.com/external/database"
cause: "Core should not have direct database dependencies"
# API layer can import core but not database directly
- path: "internal/api"
prohibited:
- name: "internal/db"
cause: "API should access database through core interfaces"
EOL
# Run Goverhaul
goverhaul --path .
This example creates a configuration that enforces a clean architecture pattern where:
- Core business logic is isolated from external dependencies
- API layer cannot access the database layer directly
For more examples, check the examples directory in the repository.
--path
: Path to lint (default: ".")--config
: Path to config file (default: "$HOME/.goverhaul.yml")--verbose
: Enable verbose logging for debugging
Goverhaul uses a YAML configuration file to define architectural rules. Create a .goverhaul.yml
file in your project or home directory.
modfile: "go.mod" # Optional: Path to go.mod file (default: "go.mod")
incremental: true # Optional: Enable incremental analysis (default: false)
cache_file: ".goverhaul.cache" # Optional: Path to cache file (default: "$HOME/.goverhaul.cache")
rules:
- path: "pkg/api"
allowed:
- "fmt"
- "net/http"
- path: "pkg/db"
prohibited:
- name: "pkg/api"
cause: "Database layer should not depend on API layer"
modfile
: Optional path to the go.mod file (default:go.mod
)incremental
: Optional boolean to enable incremental analysis for faster subsequent runs (default:false
)cache_file
: Optional path to the cache file for incremental analysis (default:$HOME/.goverhaul/cache.json
)rules
: List of architectural rules to enforcepath
: Package path to apply the rule toallowed
: List of allowed importsprohibited
: List of prohibited importsname
: Package name to prohibitcause
: Explanation for why the import is prohibited
Note
Incremental analysis is an experimental feature.
- If
allowed
is specified, only those imports are permitted for the package - If
prohibited
is specified, those imports are not allowed for the package - Rules are applied to all Go files in the specified path and its subdirectories
- Import paths can be standard library packages, third-party packages, or internal packages
- For internal packages, you can use either the full import path (including module name) or the relative path
rules:
# Domain layer can only import standard library
- path: "internal/domain"
allowed:
- "fmt"
- "errors"
- "context"
- "time"
- "encoding/json"
# Use case layer can import domain but not infrastructure
- path: "internal/usecase"
allowed:
- "fmt"
- "errors"
- "context"
- "time"
- "internal/domain"
prohibited:
- name: "internal/infrastructure"
cause: "Use cases should not depend directly on infrastructure and should declare their own interfaces"
# Infrastructure layer can import domain but not use cases
- path: "internal/infrastructure"
prohibited:
- name: "internal/usecase"
cause: "Infrastructure should not depend on use cases"
rules:
# Core module has no external dependencies
- path: "pkg/core"
prohibited:
- name: "pkg/api"
cause: "Core should not depend on API"
- name: "pkg/db"
cause: "Core should not depend on DB"
- name: "pkg/auth"
cause: "Core should not depend on Auth"
# API module can use core but not DB directly
- path: "pkg/api"
prohibited:
- name: "pkg/db"
cause: "API should access DB through core interfaces"
Before defining rules, establish a clear architectural vision:
- Identify the main components/layers of your application
- Define the intended dependencies between components
- Document the architectural decisions and constraints
For critical packages (like domain models or core business logic):
- Use the
allowed
list to explicitly whitelist permitted imports - Include only necessary standard library packages
- Be conservative with third-party dependencies
For packages with specific constraints:
- Use
prohibited
to prevent unwanted dependencies - Always include a clear
cause
explaining the architectural constraint - Focus on preventing dependency cycles and maintaining layer separation
Group rules logically:
- Layer-based rules (presentation, domain, data)
- Feature module boundaries
- Cross-cutting concerns (security, logging, etc.)
As your project grows:
- Start with a minimal set of critical rules
- Add new rules as architectural patterns emerge
- Refine existing rules based on team feedback
- Use the incremental analysis feature for faster feedback in large codebases
Use the cause
field effectively:
- Explain the architectural principle being enforced
- Reference design patterns or architectural styles
- Link to team documentation or discussions
Goverhaul helps maintain the dependency rule in clean architecture:
- Domain entities have no external dependencies
- Use cases depend only on domain entities
- Interface adapters depend on use cases but not frameworks
- Frameworks and drivers are isolated at the boundaries
For multi-module projects:
- Define clear boundaries between modules
- Enforce API contracts between modules
- Prevent implementation details from leaking across module boundaries
Limit the spread of external dependencies:
- Restrict which packages can import specific third-party libraries
- Isolate framework dependencies to adapter layers
- Prevent core business logic from depending on external packages
Use rules as executable documentation:
- Encode architectural decisions as enforceable rules
- Make architectural constraints visible to the team
- Ensure new team members understand the intended architecture
Issue: You've defined a rule, but it doesn't seem to be applied to your code.
Solutions:
- Verify that the
path
in your rule matches your project's package structure - Check that you're running
goverhaul
with the correct--path
argument - Use the
--verbose
flag to see which files are being analyzed - Ensure your Go files have proper package declarations
Issue: You're getting unexpected results because multiple rules are matching the same package.
Solutions:
- Make your rule paths more specific
- Review your rule order (rules are evaluated in the order they appear in the config)
- Use the
--verbose
flag to see which rules are being applied
Issue: Incremental analysis is not detecting changes or is skipping files that should be analyzed.
Solutions:
- Delete the cache file and run again ¯_(ツ)_/¯
- Specify a custom cache file location with the
cache_file
option - Disable incremental analysis if you're experiencing issues
Issue: goverhaul
is failing in CI but works locally.
Solutions:
- Ensure your CI environment has the correct Go version
- Check that your configuration file is being properly included in your repository
- Use absolute paths in your CI configuration
- Add debug output with the
--verbose
flag
GNU General Public License (GPL v3)
Contributions are welcome! Please feel free to open a discussion/PR.