Skip to content

[Code Quality] Reduce map[string]any usage by introducing typed configuration structures #13762

@github-actions

Description

@github-actions

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]any across 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:

  1. pkg/workflow/compiler_safe_outputs_config.go - 50+ usages (handler builders)
  2. pkg/workflow/compiler_jobs.go - 40+ usages (job parsing)
  3. pkg/workflow/tools.go - 30+ usages (tool merging)
  4. 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.go
  • pkg/workflow/repository_features_validation.go
  • pkg/workflow/mcp_config_validation.go

Migration Strategy

  1. Introduce typed structs gradually - Add new types alongside existing code
  2. Create helper functions - unmarshalFromMap() to reduce boilerplate
  3. Update callers incrementally - Migrate one parser at a time
  4. Maintain backward compatibility - Keep old signatures during migration
  5. 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]any usage 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 issues

Long-term Vision

After initial migration, establish coding standards:

  1. Prefer typed structs over map[string]any for all new configuration parsing
  2. Document structure using struct tags and comments
  3. Add linter rule to prevent new map[string]any in public APIs
  4. Create migration guide for remaining files

Files Affected

Phase 1 (Priority)

  • pkg/workflow/compiler_safe_outputs_config.go
  • pkg/workflow/compiler_jobs.go
  • pkg/workflow/tools.go
  • pkg/workflow/create_project.go

Phase 2

  • pkg/workflow/bundler_runtime_validation.go
  • pkg/workflow/repository_features_validation.go
  • pkg/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]any usages 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions