BREAKING CHANGES:
- Renamed HandleGET -> MemberGET for member-level routes
- Renamed HandlePOST -> MemberPOST for member-level routes
- Renamed HandlePUT -> MemberPUT for member-level routes
- Renamed HandlePATCH -> MemberPATCH for member-level routes
- Renamed HandleDELETE -> MemberDELETE for member-level routes
New Features:
- Added collection-level route methods: GET, POST, PUT, PATCH, DELETE
- Clear distinction between collection (/pattern/action) and member (/pattern/{id}/action) routes
- Comprehensive documentation (README, CONTRIBUTING, QUICKSTART, DOCS)
- Development tooling (Makefile, check.sh script)
- AI coding assistant guidelines (.cursorrules)
- GitHub Actions CI/CD pipeline
- golangci-lint configuration
Code Quality:
- Optimized struct field alignment for better memory usage
- All code passes go vet, staticcheck, and fieldalignment
- All tests pass with race detector
- Go 1.25+ requirement enforced
Documentation:
- Complete README rewrite with examples
- CONTRIBUTING.md with development guidelines
- QUICKSTART.md for new users
- CHANGELOG.md with version history
- SUMMARY.md documenting all changes
- DOCS.md as documentation index
8.6 KiB
Contributing to Mux
Thank you for your interest in contributing to Mux! This document provides guidelines and standards for contributing to this project.
Code of Conduct
Be respectful, constructive, and collaborative. We're all here to build better software together.
Requirements
Go Version
This project requires Go 1.25 or higher. We leverage the latest Go features and improvements, so please ensure your development environment meets this requirement.
go version # Should show go1.25 or higher
Development Tools
Before contributing, ensure you have the following tools installed:
# Install staticcheck
go install honnef.co/go/tools/cmd/staticcheck@latest
# Install fieldalignment (part of go vet)
# This is included with Go 1.25+
Code Quality Standards
All code contributions MUST pass the following checks before being submitted:
1. go vet
Ensure your code passes go vet which catches common mistakes:
go vet ./...
What it checks:
- Suspicious constructs
- Printf-like function calls with incorrect arguments
- Unreachable code
- Common mistakes in error handling
- And many other potential issues
2. fieldalignment
Ensure struct fields are optimally ordered to minimize memory usage:
go vet -vettool=$(which fieldalignment) ./...
Or use fieldalignment directly:
fieldalignment -fix ./... # Automatically fix alignment issues
Why it matters:
- Reduces memory footprint
- Improves cache locality
- Better performance in memory-constrained environments
Example:
// Bad - wastes memory due to padding
type BadStruct struct {
a bool // 1 byte + 7 bytes padding
b int64 // 8 bytes
c bool // 1 byte + 7 bytes padding
}
// Good - optimally aligned
type GoodStruct struct {
b int64 // 8 bytes
a bool // 1 byte
c bool // 1 byte + 6 bytes padding
}
3. staticcheck
Run staticcheck to catch bugs and ensure code quality:
staticcheck ./...
What it checks:
- Unused code
- Inefficient code patterns
- Deprecated API usage
- Common bugs and mistakes
- Style and consistency issues
- Performance problems
- Security vulnerabilities
Pre-Submission Checklist
Before submitting a pull request, run all checks:
# Run all checks
go vet ./...
staticcheck ./...
go test ./...
Create a simple script check.sh in your local environment:
#!/bin/bash
set -e
echo "Running go vet..."
go vet ./...
echo "Running staticcheck..."
staticcheck ./...
echo "Running tests..."
go test -race -v ./...
echo "Checking field alignment..."
go vet -vettool=$(which fieldalignment) ./...
echo "All checks passed! ✅"
Development Workflow
1. Fork and Clone
git clone https://github.com/YOUR_USERNAME/mux.git
cd mux
2. Create a Branch
git checkout -b feature/your-feature-name
3. Make Your Changes
- Write clear, idiomatic Go code
- Follow Go conventions and best practices
- Keep functions small and focused
- Add comments for exported functions and complex logic
- Update documentation if needed
4. Write Tests
All new features and bug fixes should include tests:
func TestNewFeature(t *testing.T) {
// Arrange
m := mux.New()
// Act
m.GET("/test", testHandler)
// Assert
req := httptest.NewRequest(http.MethodGet, "/test", nil)
rec := httptest.NewRecorder()
m.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("expected status 200, got %d", rec.Code)
}
}
5. Run Quality Checks
go vet ./...
staticcheck ./...
go test -race ./...
6. Commit Your Changes
Write clear, descriptive commit messages:
git commit -m "feat: add support for custom error handlers"
git commit -m "fix: resolve race condition in middleware chain"
git commit -m "docs: update README with new examples"
Commit message prefixes:
feat:- New featurefix:- Bug fixdocs:- Documentation changestest:- Test additions or modificationsrefactor:- Code refactoringperf:- Performance improvementschore:- Maintenance tasks
7. Push and Create Pull Request
git push origin feature/your-feature-name
Then create a pull request on GitHub with:
- Clear description of changes
- Link to related issues (if any)
- Screenshots/examples if applicable
Code Style Guidelines
General Principles
- Simplicity over cleverness - Write clear, maintainable code
- Use standard library - Avoid external dependencies unless absolutely necessary
- Zero allocation paths - Optimize hot paths to minimize allocations
- Fail fast - Use panics for programmer errors, errors for runtime issues
- Document public APIs - All exported functions, types, and methods should have comments
Naming Conventions
- Use short, descriptive names for local variables
- Use full words for exported identifiers
- Follow Go naming conventions (MixedCaps, not snake_case)
- Avoid stuttering (e.g.,
mux.MuxRouter→mux.Router)
Error Handling
// Good - clear error handling
func processRequest(w http.ResponseWriter, r *http.Request) {
data, err := fetchData(r.Context())
if err != nil {
http.Error(w, "Failed to fetch data", http.StatusInternalServerError)
return
}
// process data...
}
// Avoid - ignoring errors
func processRequest(w http.ResponseWriter, r *http.Request) {
data, _ := fetchData(r.Context()) // Don't do this
// process data...
}
Comments
// Good - explains why, not what
// Use buffered channel to prevent goroutine leaks when context is cancelled
// before the worker finishes processing.
ch := make(chan result, 1)
// Avoid - states the obvious
// Create a channel
ch := make(chan result, 1)
Testing Guidelines
Test Structure
Use the Arrange-Act-Assert pattern:
func TestRouteRegistration(t *testing.T) {
// Arrange
m := mux.New()
handler := func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("test"))
}
// Act
m.GET("/test", handler)
routes := m.RouteList()
// Assert
if len(routes) != 1 {
t.Errorf("expected 1 route, got %d", len(routes))
}
}
Table-Driven Tests
For testing multiple scenarios:
func TestPathParameters(t *testing.T) {
tests := []struct {
name string
pattern string
path string
expected string
}{
{"simple param", "/users/{id}", "/users/123", "123"},
{"multiple params", "/posts/{year}/{month}", "/posts/2024/01", "2024"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// test implementation
})
}
}
Test Coverage
Aim for high test coverage, especially for:
- Public APIs
- Edge cases
- Error conditions
- Concurrent access patterns
# Check coverage
go test -cover ./...
# Generate detailed coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
Performance Considerations
Benchmarking
Include benchmarks for performance-critical code:
func BenchmarkMiddlewareChain(b *testing.B) {
m := mux.New()
m.Use(middleware1, middleware2, middleware3)
m.GET("/test", testHandler)
req := httptest.NewRequest(http.MethodGet, "/test", nil)
rec := httptest.NewRecorder()
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.ServeHTTP(rec, req)
}
}
Run benchmarks:
go test -bench=. -benchmem ./...
Allocation Analysis
Identify and minimize allocations:
go test -bench=. -benchmem -memprofile=mem.out ./...
go tool pprof mem.out
Documentation
Code Documentation
- All exported functions, types, and methods must have comments
- Comments should start with the name of the thing being described
- Use complete sentences with proper punctuation
// New creates and returns a new Mux router instance with an empty route table
// and no middleware configured.
func New() *Mux {
return &Mux{
mux: http.NewServeMux(),
routes: new(RouteList),
}
}
README Updates
If your change affects usage:
- Update relevant sections in README.md
- Add examples if introducing new features
- Update the table of contents if adding new sections
Questions or Issues?
- Open an issue for bugs or feature requests
- Start a discussion for questions or ideas
- Check existing issues and PRs before creating new ones
License
By contributing to Mux, you agree that your contributions will be licensed under the MIT License.
Thank you for contributing to Mux! Your efforts help make this project better for everyone. 🙏