Refactoring Configuration Management in Go: From Inline to Structured Constants

Introduction In this post, we will review a refactoring process for loading application configuration in Go. The original implementation (referred to as Version 2) directly used hard-coded environment variable names inside the Load function. The refactored version (Version 1) introduced constant definitions for environment variable keys and default values, improving both readability and maintainability. Original Code (Version 2) In the initial implementation, environment variable keys were inline literals within the Load function: func Load() *Config { return &Config{ EnvKeys: os.Getenv("INPUT_ENV_KEY"), EnvValues: os.Getenv("INPUT_ENV_VALUE"), OutputKeys: os.Getenv("INPUT_OUTPUT_KEY"), OutputValues: os.Getenv("INPUT_OUTPUT_VALUE"), GithubEnv: os.Getenv("GITHUB_ENV"), GithubOutput: os.Getenv("GITHUB_OUTPUT"), Delimiter: getEnvWithDefault("INPUT_DELIMITER", ","), FailOnEmpty: getBoolEnv("INPUT_FAIL_ON_EMPTY", true), TrimWhitespace: getBoolEnv("INPUT_TRIM_WHITESPACE", true), CaseSensitive: getBoolEnv("INPUT_CASE_SENSITIVE", true), ErrorOnDuplicate: getBoolEnv("INPUT_ERROR_ON_DUPLICATE", true), MaskSecrets: getBoolEnv("INPUT_MASK_SECRETS", false), MaskPattern: getEnvWithDefault("INPUT_MASK_PATTERN", ""), ToUpper: getBoolEnv("INPUT_TO_UPPER", false), ToLower: getBoolEnv("INPUT_TO_LOWER", false), EncodeURL: getBoolEnv("INPUT_ENCODE_URL", false), EscapeNewlines: getBoolEnv("INPUT_ESCAPE_NEWLINES", true), MaxLength: getIntEnv("INPUT_MAX_LENGTH", 0), AllowEmpty: getBoolEnv("INPUT_ALLOW_EMPTY", false), DebugMode: getBoolEnv("DEBUG_MODE", false), } } Issues with This Approach Magic Strings: Each environment variable name appears as a literal string, which increases the risk of typos. Maintainability: If the environment variable name changes, it needs to be updated in multiple places. Lack of Central Documentation: No clear central reference point for all supported environment variables. Refactored Code (Version 1) The refactored version introduces central constants for environment variable names and default values: // Environment Variable Names Constants const ( // Input Environment Variables EnvKeyInput = "INPUT_ENV_KEY" EnvValueInput = "INPUT_ENV_VALUE" OutputKeyInput = "INPUT_OUTPUT_KEY" OutputValueInput = "INPUT_OUTPUT_VALUE" DelimiterInput = "INPUT_DELIMITER" FailOnEmptyInput = "INPUT_FAIL_ON_EMPTY" TrimWhitespaceInput = "INPUT_TRIM_WHITESPACE" CaseSensitiveInput = "INPUT_CASE_SENSITIVE" ErrorOnDuplicateInput = "INPUT_ERROR_ON_DUPLICATE" MaskSecretsInput = "INPUT_MASK_SECRETS" MaskPatternInput = "INPUT_MASK_PATTERN" ToUpperInput = "INPUT_TO_UPPER" ToLowerInput = "INPUT_TO_LOWER" EncodeURLInput = "INPUT_ENCODE_URL" EscapeNewlinesInput = "INPUT_ESCAPE_NEWLINES" MaxLengthInput = "INPUT_MAX_LENGTH" AllowEmptyInput = "INPUT_ALLOW_EMPTY" DebugModeInput = "DEBUG_MODE" // GitHub Environment Variables GithubEnvVar = "GITHUB_ENV" GithubOutputVar = "GITHUB_OUTPUT" ) // Default Values Constants const ( DefaultDelimiter = "," DefaultFailOnEmpty = true DefaultTrimWhitespace = true DefaultCaseSensitive = true DefaultErrorOnDuplicate = true DefaultMaskSecrets = false DefaultMaskPattern = "" DefaultToUpper = false DefaultToLower = false DefaultEncodeURL = false DefaultEscapeNewlines = true DefaultMaxLength = 0 DefaultAllowEmpty = false DefaultDebugMode = false ) // Config holds the application configuration type Config struct { // Input/Output Keys and Values EnvKeys string EnvValues string OutputKeys string OutputValues string // GitHub File Paths GithubEnv string GithubOutput string // Input Processing Options Delimiter string FailOnEmpty bool TrimWhitespace bool CaseSensitive bool ErrorOnDuplicate bool AllowEmpty bool // Value Transformation Options ToUpper bool ToLower bool EncodeURL bool EscapeNewlines bool MaxLength int // Security Options MaskSecrets bool MaskPattern string // Debug Options DebugMode bool } // Load loads configuration from environment variables func Load() *Config { return &Config{ // Input/Output Keys and Values EnvKeys: os.Getenv(EnvKeyInput), EnvValues: os.Getenv(EnvValueInput), OutputKeys: os.Getenv(OutputKeyInput), OutputValues: o

Mar 4, 2025 - 08:33
 0
Refactoring Configuration Management in Go: From Inline to Structured Constants

Introduction

In this post, we will review a refactoring process for loading application configuration in Go.
The original implementation (referred to as Version 2) directly used hard-coded environment variable names inside the Load function.
The refactored version (Version 1) introduced constant definitions for environment variable keys and default values, improving both readability and maintainability.

Original Code (Version 2)

In the initial implementation, environment variable keys were inline literals within the Load function:

func Load() *Config {
    return &Config{
        EnvKeys:          os.Getenv("INPUT_ENV_KEY"),
        EnvValues:        os.Getenv("INPUT_ENV_VALUE"),
        OutputKeys:       os.Getenv("INPUT_OUTPUT_KEY"),
        OutputValues:     os.Getenv("INPUT_OUTPUT_VALUE"),
        GithubEnv:        os.Getenv("GITHUB_ENV"),
        GithubOutput:     os.Getenv("GITHUB_OUTPUT"),
        Delimiter:        getEnvWithDefault("INPUT_DELIMITER", ","),
        FailOnEmpty:      getBoolEnv("INPUT_FAIL_ON_EMPTY", true),
        TrimWhitespace:   getBoolEnv("INPUT_TRIM_WHITESPACE", true),
        CaseSensitive:    getBoolEnv("INPUT_CASE_SENSITIVE", true),
        ErrorOnDuplicate: getBoolEnv("INPUT_ERROR_ON_DUPLICATE", true),
        MaskSecrets:      getBoolEnv("INPUT_MASK_SECRETS", false),
        MaskPattern:      getEnvWithDefault("INPUT_MASK_PATTERN", ""),
        ToUpper:          getBoolEnv("INPUT_TO_UPPER", false),
        ToLower:          getBoolEnv("INPUT_TO_LOWER", false),
        EncodeURL:        getBoolEnv("INPUT_ENCODE_URL", false),
        EscapeNewlines:   getBoolEnv("INPUT_ESCAPE_NEWLINES", true),
        MaxLength:        getIntEnv("INPUT_MAX_LENGTH", 0),
        AllowEmpty:       getBoolEnv("INPUT_ALLOW_EMPTY", false),
        DebugMode:        getBoolEnv("DEBUG_MODE", false),
    }
}

  • Issues with This Approach
    • Magic Strings: Each environment variable name appears as a literal string, which increases the risk of typos.
    • Maintainability: If the environment variable name changes, it needs to be updated in multiple places.
    • Lack of Central Documentation: No clear central reference point for all supported environment variables.

Refactored Code (Version 1)

The refactored version introduces central constants for environment variable names and default values:

// Environment Variable Names Constants
const (
    // Input Environment Variables
    EnvKeyInput           = "INPUT_ENV_KEY"
    EnvValueInput         = "INPUT_ENV_VALUE"
    OutputKeyInput        = "INPUT_OUTPUT_KEY"
    OutputValueInput      = "INPUT_OUTPUT_VALUE"
    DelimiterInput        = "INPUT_DELIMITER"
    FailOnEmptyInput      = "INPUT_FAIL_ON_EMPTY"
    TrimWhitespaceInput   = "INPUT_TRIM_WHITESPACE"
    CaseSensitiveInput    = "INPUT_CASE_SENSITIVE"
    ErrorOnDuplicateInput = "INPUT_ERROR_ON_DUPLICATE"
    MaskSecretsInput      = "INPUT_MASK_SECRETS"
    MaskPatternInput      = "INPUT_MASK_PATTERN"
    ToUpperInput          = "INPUT_TO_UPPER"
    ToLowerInput          = "INPUT_TO_LOWER"
    EncodeURLInput        = "INPUT_ENCODE_URL"
    EscapeNewlinesInput   = "INPUT_ESCAPE_NEWLINES"
    MaxLengthInput        = "INPUT_MAX_LENGTH"
    AllowEmptyInput       = "INPUT_ALLOW_EMPTY"
    DebugModeInput        = "DEBUG_MODE"

    // GitHub Environment Variables
    GithubEnvVar    = "GITHUB_ENV"
    GithubOutputVar = "GITHUB_OUTPUT"
)

// Default Values Constants
const (
    DefaultDelimiter        = ","
    DefaultFailOnEmpty      = true
    DefaultTrimWhitespace   = true
    DefaultCaseSensitive    = true
    DefaultErrorOnDuplicate = true
    DefaultMaskSecrets      = false
    DefaultMaskPattern      = ""
    DefaultToUpper          = false
    DefaultToLower          = false
    DefaultEncodeURL        = false
    DefaultEscapeNewlines   = true
    DefaultMaxLength        = 0
    DefaultAllowEmpty       = false
    DefaultDebugMode        = false
)

// Config holds the application configuration
type Config struct {
    // Input/Output Keys and Values
    EnvKeys      string
    EnvValues    string
    OutputKeys   string
    OutputValues string

    // GitHub File Paths
    GithubEnv    string
    GithubOutput string

    // Input Processing Options
    Delimiter        string
    FailOnEmpty      bool
    TrimWhitespace   bool
    CaseSensitive    bool
    ErrorOnDuplicate bool
    AllowEmpty       bool

    // Value Transformation Options
    ToUpper        bool
    ToLower        bool
    EncodeURL      bool
    EscapeNewlines bool
    MaxLength      int

    // Security Options
    MaskSecrets bool
    MaskPattern string

    // Debug Options
    DebugMode bool
}

// Load loads configuration from environment variables
func Load() *Config {
    return &Config{
        // Input/Output Keys and Values
        EnvKeys:      os.Getenv(EnvKeyInput),
        EnvValues:    os.Getenv(EnvValueInput),
        OutputKeys:   os.Getenv(OutputKeyInput),
        OutputValues: os.Getenv(OutputValueInput),

        // GitHub File Paths
        GithubEnv:    os.Getenv(GithubEnvVar),
        GithubOutput: os.Getenv(GithubOutputVar),

        // Input Processing Options
        Delimiter:        getEnvWithDefault(DelimiterInput, DefaultDelimiter),
        FailOnEmpty:      getBoolEnv(FailOnEmptyInput, DefaultFailOnEmpty),
        TrimWhitespace:   getBoolEnv(TrimWhitespaceInput, DefaultTrimWhitespace),
        CaseSensitive:    getBoolEnv(CaseSensitiveInput, DefaultCaseSensitive),
        ErrorOnDuplicate: getBoolEnv(ErrorOnDuplicateInput, DefaultErrorOnDuplicate),
        AllowEmpty:       getBoolEnv(AllowEmptyInput, DefaultAllowEmpty),

        // Value Transformation Options
        ToUpper:        getBoolEnv(ToUpperInput, DefaultToUpper),
        ToLower:        getBoolEnv(ToLowerInput, DefaultToLower),
        EncodeURL:      getBoolEnv(EncodeURLInput, DefaultEncodeURL),
        EscapeNewlines: getBoolEnv(EscapeNewlinesInput, DefaultEscapeNewlines),
        MaxLength:      getIntEnv(MaxLengthInput, DefaultMaxLength),

        // Security Options
        MaskSecrets: getBoolEnv(MaskSecretsInput, DefaultMaskSecrets),
        MaskPattern: getEnvWithDefault(MaskPatternInput, DefaultMaskPattern),

        // Debug Options
        DebugMode: getBoolEnv(DebugModeInput, DefaultDebugMode),
    }
}


Question

I modified the code as above, but I think it got too long? Is it a lot? I'm a DevOps engineer and I'm also studying development.