eq2go/MODERNIZE.md

282 lines
6.9 KiB
Markdown

# Package Modernization Instructions
## Goal
Transform legacy packages to use generic MasterList pattern with simplified database operations.
## Steps
### 1. Implement Generic MasterList Base
Create `internal/common/master_list.go` with generic collection management:
```go
type MasterList[K comparable, V Identifiable[K]] struct {
items map[K]V
mutex sync.RWMutex
}
```
### 2. Consolidate Package Structure
**Remove:**
- Legacy wrapper functions (LoadAll, SaveAll, etc.)
- Duplicate database files (database.go, database_legacy.go)
- README.md (use doc.go instead)
- Separate "active_record.go" files
**Consolidate into:**
- `{type}.go` - Main type with embedded database operations
- `types.go` - Supporting types only (no main type)
- `master.go` - MasterList using generic base
- `player.go` - Player-specific logic (if applicable)
- `doc.go` - Primary documentation
- `{type}_test.go` - Focused tests
### 3. Refactor Main Type
Transform the main type to include database operations:
```go
// Before: Separate type and database operations
type Achievement struct {
ID uint32
Title string
}
func LoadAchievement(db *database.Database, id uint32) (*Achievement, error)
func SaveAchievement(db *database.Database, a *Achievement) error
// After: Embedded database operations
type Achievement struct {
ID uint32
Title string
db *database.Database
isNew bool
}
func New(db *database.Database) *Achievement
func Load(db *database.Database, id uint32) (*Achievement, error)
func (a *Achievement) Save() error
func (a *Achievement) Delete() error
func (a *Achievement) Reload() error
```
### 4. Update MasterList
Replace manual implementation with generic base:
```go
// Before: Manual thread-safety
type MasterList struct {
items map[uint32]*Achievement
mutex sync.RWMutex
}
func (m *MasterList) AddAchievement(a *Achievement) bool {
m.mutex.Lock()
defer m.mutex.Unlock()
// manual implementation
}
// After: Generic base
type MasterList struct {
*common.MasterList[uint32, *Achievement]
}
func NewMasterList() *MasterList {
return &MasterList{
MasterList: common.NewMasterList[uint32, *Achievement](),
}
}
func (m *MasterList) AddAchievement(a *Achievement) bool {
return m.MasterList.Add(a)
}
```
### 5. Implement Identifiable Interface
Ensure main type implements `GetID()`:
```go
func (a *Achievement) GetID() uint32 {
return a.AchievementID // or appropriate ID field
}
```
### 6. Simplify API
**Remove:**
- Legacy type variants (Achievement vs AchievementRecord)
- Conversion methods (ToLegacy, FromLegacy)
- Duplicate CRUD operations
- Complex wrapper functions
**Keep:**
- Single type definition
- Direct database methods on type
- Domain-specific extensions only
### 7. Update Documentation
Create concise `doc.go`:
```go
// Package achievements provides [brief description].
//
// Basic Usage:
//
// achievement := achievements.New(db)
// achievement.Title = "Dragon Slayer"
// achievement.Save()
//
// loaded, _ := achievements.Load(db, 1001)
// loaded.Delete()
//
// Master List:
//
// masterList := achievements.NewMasterList()
// masterList.Add(achievement)
package achievements
```
### 8. Testing
Create focused tests:
- Test core type operations (New, Save, Load, Delete)
- Test MasterList basic operations
- Remove legacy compatibility tests
- Keep tests simple and direct
## Key Principles
1. **Single Source of Truth**: One type definition, not multiple variants
2. **Embedded Operations**: Database methods on the type itself
3. **Generic Base**: Use common.MasterList for thread-safety
4. **No Legacy Baggage**: Remove all "Legacy" types and converters
5. **Documentation in Code**: Use doc.go, not README.md
## Migration Checklist
- [ ] Create/verify generic MasterList in common package
- [ ] Identify main type and supporting types
- [ ] Consolidate database operations into main type
- [ ] Add db field and methods to main type
- [ ] Replace manual MasterList with generic base
- [ ] Implement GetID() for Identifiable interface
- [ ] Remove all legacy types and converters
- [ ] Update doc.go with concise examples
- [ ] Simplify tests to cover core functionality
- [ ] **Create comprehensive benchmarks (benchmark_test.go)**
- [ ] **Verify performance meets targets**
- [ ] Run `go fmt`, `go test`, and `go test -bench=.`
## Expected Results
- **80% less code** in most packages
- **Single type** instead of multiple variants
- **Thread-safe** operations via generic base
- **Consistent API** across all packages
- **Better maintainability** with less duplication
## Benchmarking
After modernization, create comprehensive benchmarks to measure performance improvements and ensure no regressions:
### Required Benchmarks
**Core Operations:**
```go
func BenchmarkTypeCreation(b *testing.B) // New type creation
func BenchmarkTypeOperations(b *testing.B) // CRUD operations
func BenchmarkMasterListOperations(b *testing.B) // Collection operations
```
**Performance Critical Paths:**
```go
func BenchmarkCoreAlgorithm(b *testing.B) // Main business logic
func BenchmarkConcurrentAccess(b *testing.B) // Thread safety
func BenchmarkMemoryAllocation(b *testing.B) // Memory patterns
```
**Comparison Benchmarks:**
```go
func BenchmarkComparisonWithOldSystem(b *testing.B) // Before/after metrics
```
### Benchmark Structure
Use sub-benchmarks for detailed measurements:
```go
func BenchmarkMasterListOperations(b *testing.B) {
ml := NewMasterList()
// Setup data...
b.Run("Add", func(b *testing.B) {
for i := 0; i < b.N; i++ {
ml.Add(createTestItem(i))
}
})
b.Run("Get", func(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = ml.Get(randomID())
}
})
})
}
```
### Mock Implementations
Create lightweight mocks for interfaces:
```go
type mockPlayer struct {
level int16
location int32
}
func (p *mockPlayer) GetLevel() int16 { return p.level }
func (p *mockPlayer) GetLocation() int32 { return p.location }
```
### Performance Expectations
Target performance after modernization:
- **Creation operations**: <100ns per operation
- **Lookup operations**: <50ns per operation
- **Collection operations**: O(1) for gets, O(N) for filters
- **Memory allocations**: Minimize in hot paths
- **Concurrent access**: Linear scaling with cores
### Running Benchmarks
```bash
# Run all benchmarks
go test -bench=. ./internal/package_name
# Detailed benchmarks with memory stats
go test -bench=. -benchmem ./internal/package_name
# Compare performance over time
go test -bench=. -count=5 ./internal/package_name
# CPU profiling for optimization
go test -bench=BenchmarkCoreAlgorithm -cpuprofile=cpu.prof ./internal/package_name
```
## Example Commands
```bash
# Remove legacy files
rm README.md database_legacy.go active_record.go
# Rename if needed
mv active_record.go achievement.go
# Test the changes
go fmt ./...
go test ./...
go test -bench=. ./...
go build ./...
```