A Go library for finding reverse dependencies and determining which main packages are affected by file changes.
godepfind helps you identify which packages import your target packages (reverse dependency analysis) and, more importantly, which main packages need to be recompiled when you modify a specific Go file.
This is particularly useful for:
- Build systems that need to know which applications to rebuild when files change
- Development tools that optimize compilation by only rebuilding affected main packages
- CI/CD pipelines that want to minimize build time by targeting only affected applications
go get github.com/tinywasm/depfindNEW: Intelligent caching system for performance in development environments where files change regularly.
NEW: Determine which handler should process a file change using smart dependency analysis.
Find which packages import specified target packages.
Main feature: Given a modified file name, find which main packages depend on it (directly or transitively).
import "github.com/tinywasm/depfind"
// Create a new finder instance for your project
finder := godepfind.New("/path/to/your/go/project")
// Optional: Include test imports in dependency analysis
finder.SetTestImports(true)Primary use case: When you modify a file, find which main packages need recompilation.
// After modifying "database.go", find affected main packages
mains, err := finder.GoFileComesFromMain("database.go")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Main packages affected by database.go changes: %v\n", mains)
// Output: [myproject/cmd/server myproject/cmd/cli]For development tools: Determine which handler should process a file change.
// Check if a file change belongs to this handler
mainInputFileRelativePath := "app/server/main.go"
isMine, err := finder.ThisFileIsMine(mainInputFileRelativePath, "./internal/db/database.go", "write")
if err != nil {
log.Fatal(err)
}
if isMine {
fmt.Println("This handler should process the file change")
// Process the file change...
}// Find packages that import "fmt" or "os"
deps, err := finder.FindReverseDeps("./...", []string{"fmt", "os"})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Packages importing fmt or os: %v\n", deps)Imagine you have a Go project with multiple applications:
myproject/
├── go.mod
├── cmd/
│ ├── server/main.go (web server)
│ ├── cli/main.go (command line tool)
│ └── worker/main.go (background worker)
├── internal/
│ ├── database/db.go (shared database package)
│ ├── auth/auth.go (authentication)
│ └── utils/helpers.go (utilities)
Scenario: You modify internal/database/db.go
finder := godepfind.New("/path/to/myproject")
// Traditional approach: Find which main packages are affected
affected, err := finder.GoFileComesFromMain("db.go")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Need to rebuild: %v\n", affected)
// Output: [myproject/cmd/server myproject/cmd/worker]
// Note: cmd/cli is NOT affected because it doesn't import the database package
// NEW: Handler-based approach for development tools
// Check if server handler should process this change
mainInputFileRelativePath := "cmd/server/main.go"
shouldProcess, err := finder.ThisFileIsMine(mainInputFileRelativePath, "./internal/database/db.go", "write")
if err != nil {
log.Fatal(err)
}
if shouldProcess {
fmt.Println("Server handler will process this database change")
// Automatically compile server, restart, etc.
}Now your build system knows to only recompile the server and worker applications, not the cli tool.
Creates a new GoDepFind instance with intelligent caching.
rootDir: Path to the Go module root directory (where go.mod is located)
Enable/disable inclusion of test imports in dependency analysis.
Main function: Find which main packages depend on the given file.
fileName: Name of the file (e.g., "database.go", "helpers.go")- Returns: Slice of main package paths that depend on this file
Find packages in sourcePath that import any of the targetPaths.
sourcePath: Path pattern to search (e.g., "./...", "./cmd/...")targetPaths: Packages to find dependencies for- Returns: Slice of packages that import the targets
NEW: Determine if a file change belongs to a specific handler using intelligent dependency analysis.
mainInputFileRelativePath: Path to the main file that this handler is responsible for managing.filePath: Full path to the changed file (e.g., "./internal/db/database.go") - filePath must include directory separatorsevent: Type of change ("write", "create", "remove", "rename")- Returns: (true if handler should process, error if any)
Important: filePath must be a complete path with directory separators (e.g., "./internal/db/database.go"). Simple filenames like "database.go" are not allowed and will return an error.
The ThisFileIsMine function has strict validation requirements for the filePath parameter:
"./internal/db/database.go"- Relative path with directory"app/web/main.go"- Relative path with subdirectory"/absolute/path/main.go"- Absolute path"pwa/main.server.go"- Path with filename containing dots
""- Empty string"database.go"- Filename only (no directory separators)"file.go"- Filename only (no directory separators)
filePathcannot be emptyfilePathmust contain at least one directory separator (/or\)- Simple filenames without directory paths will return an error
This validation ensures deterministic file ownership by preventing ambiguity between files with the same name in different directories.
godepfind now includes an intelligent caching system that dramatically improves performance in development environments:
| Scenario | Without Cache | With Cache | Speedup |
|---|---|---|---|
| GoFileComesFromMain | ~14,000,000 ns/op | ~194 ns/op | ~72,000x |
| ThisFileIsMine | ~13,000,000 ns/op | ~22,000 ns/op | ~590x |
| Real-World Scenario | ~8,500,000 ns/op | ~310 ns/op* | ~27,000x* |
| Multiple Files | ~55,000,000 ns/op | ~860 ns/op | ~64,000x |
| Cache Invalidation | N/A | ~305 ns/op | - |
See docs/BENCHMARK.md for full results and details.
Note: Real-World Scenario with cache is extremely fast; actual value is similar to other cached operations.
Expert Note: GoDepFind's cache system achieves real-time performance (from ~14,000,000 ns/op to ~194 ns/op, ~72,000x faster) and reduces memory allocations to nearly zero in repeated queries. This makes it highly suitable for modern development environments, file watchers, and incremental build systems.
- Lazy Loading: Cache is built only when needed
- Selective Invalidation: Only affected packages are re-analyzed when files change
- Memory Efficient: Cache is stored in memory and cleaned up automatically
- Event-Driven: Cache updates automatically based on file change events
This makes godepfind suitable for real-time file watching in development tools.
- Smart Build Systems: Only rebuild applications affected by code changes with automatic cache management
- Development Tools: IDE extensions that show which apps are affected by current changes
- File Watchers: Real-time development environments that respond to file changes intelligently
- CI/CD Optimization: Reduce build time by targeting only affected main packages
- Dependency Analysis: Understand how your modules are interconnected
- Refactoring Safety: Know the blast radius of changes before making them
- Go 1.19+
- Valid Go module (go.mod file)
- Project must be buildable with
go list ./...
This library is based on the excellent work of Andrew Wilkins and his rdep tool. The core reverse dependency detection logic was adapted and extended from his implementation to provide file-to-main package mapping functionality.
Special thanks to Andrew for creating the foundational reverse dependency analysis that made this library possible.