-
Notifications
You must be signed in to change notification settings - Fork 266
Description
Description
The codebase heavily relies on map[string]any for configuration parsing (1,074 usages across 184 files, 42% of codebase), sacrificing compile-time type safety and requiring extensive type assertions. Introducing strongly-typed intermediate structures will improve type safety, IDE support, and reduce runtime errors.
Problem
Current State:
- 1,074 usages of
map[string]anyacross 184 files - Type assertions can panic if types don't match expectations
- No compile-time safety for required fields
- IDE autocomplete doesn't work for configuration fields
- Difficult to understand expected structure without reading code
Impact:
- Runtime panics from failed type assertions
- Difficult to refactor configuration structures
- Poor IDE support and developer experience
- Type mismatches only caught at runtime
Suggested Changes
Replace map[string]any parser functions with strongly-typed intermediate structures using YAML/JSON tags.
Phase 1: High-Impact Files (Week 1-2)
Focus on files with highest map[string]any usage:
pkg/workflow/compiler_safe_outputs_config.go- 50+ usages (handler builders)pkg/workflow/compiler_jobs.go- 40+ usages (job parsing)pkg/workflow/tools.go- 30+ usages (tool merging)pkg/workflow/create_project.go- 25+ usages (project config parsing)
Implementation Pattern
Before (weakly typed):
func (c *Compiler) parseIssuesConfig(outputMap map[string]any) *CreateIssuesConfig {
configData, _ := outputMap["create-issue"].(map[string]any)
// Type assertions throughout
if expires, exists := configData["expires"]; exists {
if _, ok := expires.(string); ok {
// Parse and convert...
}
}
}After (strongly typed):
// Define typed intermediate structures
type RawSafeOutputConfig struct {
CreateIssue *json.RawMessage `yaml:"create-issue"`
}
type RawCreateIssueConfig struct {
TitlePrefix string `yaml:"title-prefix"`
Labels []string `yaml:"labels"`
Expires string `yaml:"expires"` // Can handle string or int
Max int `yaml:"max"`
}
func (c *Compiler) parseIssuesConfig(outputMap map[string]any) *CreateIssuesConfig {
// Use yaml.Unmarshal with proper error handling
var raw RawCreateIssueConfig
if err := unmarshalFromMap(outputMap, "create-issue", &raw); err != nil {
return nil
}
// Convert to final config with validation
return &CreateIssuesConfig{
TitlePrefix: raw.TitlePrefix,
Labels: raw.Labels,
Expires: parseExpiresDuration(raw.Expires),
Max: raw.Max,
}
}Benefits
- ✅ Compile-time field validation
- ✅ Clear API documentation through struct tags
- ✅ IDE autocomplete support
- ✅ Easier refactoring with compile-time safety
- ✅ Reduced runtime panics from type mismatches
Phase 2: Validation Files (Week 3-4)
Apply same pattern to validation files that parse map[string]any:
pkg/workflow/bundler_runtime_validation.gopkg/workflow/repository_features_validation.gopkg/workflow/mcp_config_validation.go
Migration Strategy
- Introduce typed structs gradually - Add new types alongside existing code
- Create helper functions -
unmarshalFromMap()to reduce boilerplate - Update callers incrementally - Migrate one parser at a time
- Maintain backward compatibility - Keep old signatures during migration
- Add tests - Verify typed parsing matches old behavior
Success Criteria
- Top 4 high-usage files migrated to typed structures
- Helper function
unmarshalFromMap()created - Type assertions reduced by at least 300 in Phase 1 files
- IDE autocomplete works for configuration fields
- All existing tests pass without changes
- New tests added for typed parsing
- Documentation updated with typed examples
- No new
map[string]anyusage in updated files
Testing Strategy
Unit Tests
func TestParseIssuesConfig_TypedParsing(t *testing.T) {
tests := []struct {
name string
input map[string]any
want *CreateIssuesConfig
wantErr bool
}{
{
name: "valid config",
input: map[string]any{
"create-issue": map[string]any{
"title-prefix": "Bug: ",
"labels": []string{"bug"},
"max": 5,
},
},
want: &CreateIssuesConfig{
TitlePrefix: "Bug: ",
Labels: []string{"bug"},
Max: 5,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := parseIssuesConfig(tt.input)
assert.Equal(t, tt.want, got)
})
}
}Validation
make test-unit # Verify all tests pass
make lint # Check for type safety issuesLong-term Vision
After initial migration, establish coding standards:
- Prefer typed structs over
map[string]anyfor all new configuration parsing - Document structure using struct tags and comments
- Add linter rule to prevent new
map[string]anyin public APIs - Create migration guide for remaining files
Files Affected
Phase 1 (Priority)
pkg/workflow/compiler_safe_outputs_config.gopkg/workflow/compiler_jobs.gopkg/workflow/tools.gopkg/workflow/create_project.go
Phase 2
pkg/workflow/bundler_runtime_validation.gopkg/workflow/repository_features_validation.gopkg/workflow/mcp_config_validation.go
Priority
Medium - Improves maintainability and type safety, but not blocking production issues
Source
Extracted from Type Consistency and Strong Typing Opportunities discussion #12238
Analysis Context:
- 1,074
map[string]anyusages across 42% of codebase - Top 5 files account for 195+ usages
- Significant opportunities for type safety improvements
- Pattern is well-understood and migration path is clear
AI generated by Discussion Task Miner - Code Quality Improvement Agent
- expires on Feb 18, 2026, 5:19 PM UTC