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
411 lines
8.6 KiB
Markdown
411 lines
8.6 KiB
Markdown
# 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.
|
|
|
|
```bash
|
|
go version # Should show go1.25 or higher
|
|
```
|
|
|
|
### Development Tools
|
|
|
|
Before contributing, ensure you have the following tools installed:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
go vet -vettool=$(which fieldalignment) ./...
|
|
```
|
|
|
|
Or use fieldalignment directly:
|
|
|
|
```bash
|
|
fieldalignment -fix ./... # Automatically fix alignment issues
|
|
```
|
|
|
|
**Why it matters:**
|
|
- Reduces memory footprint
|
|
- Improves cache locality
|
|
- Better performance in memory-constrained environments
|
|
|
|
**Example:**
|
|
```go
|
|
// 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
# Run all checks
|
|
go vet ./...
|
|
staticcheck ./...
|
|
go test ./...
|
|
```
|
|
|
|
Create a simple script `check.sh` in your local environment:
|
|
|
|
```bash
|
|
#!/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
|
|
|
|
```bash
|
|
git clone https://github.com/YOUR_USERNAME/mux.git
|
|
cd mux
|
|
```
|
|
|
|
### 2. Create a Branch
|
|
|
|
```bash
|
|
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:
|
|
|
|
```go
|
|
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
|
|
|
|
```bash
|
|
go vet ./...
|
|
staticcheck ./...
|
|
go test -race ./...
|
|
```
|
|
|
|
### 6. Commit Your Changes
|
|
|
|
Write clear, descriptive commit messages:
|
|
|
|
```bash
|
|
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 feature
|
|
- `fix:` - Bug fix
|
|
- `docs:` - Documentation changes
|
|
- `test:` - Test additions or modifications
|
|
- `refactor:` - Code refactoring
|
|
- `perf:` - Performance improvements
|
|
- `chore:` - Maintenance tasks
|
|
|
|
### 7. Push and Create Pull Request
|
|
|
|
```bash
|
|
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
|
|
|
|
1. **Simplicity over cleverness** - Write clear, maintainable code
|
|
2. **Use standard library** - Avoid external dependencies unless absolutely necessary
|
|
3. **Zero allocation paths** - Optimize hot paths to minimize allocations
|
|
4. **Fail fast** - Use panics for programmer errors, errors for runtime issues
|
|
5. **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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```go
|
|
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:
|
|
|
|
```bash
|
|
go test -bench=. -benchmem ./...
|
|
```
|
|
|
|
### Allocation Analysis
|
|
|
|
Identify and minimize allocations:
|
|
|
|
```bash
|
|
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
|
|
|
|
```go
|
|
// 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. 🙏
|