Go Micro Logo Go Micro

ADR-009: Progressive Configuration

Status

Accepted

Context

Microservices frameworks face a paradox:

Too simple: Framework is toy, not production-ready Too complex: High barrier to entry, discourages adoption

Decision

Implement progressive configuration where:

  1. Zero config works for development
  2. Environment variables provide simple overrides
  3. Code-based options enable fine-grained control
  4. Defaults are production-aware but not production-ready

Levels of Configuration

Level 1: Zero Config (Development)

svc := micro.NewService(micro.Name("hello"))
svc.Run()

Uses defaults:

Level 2: Environment Variables (Staging)

MICRO_REGISTRY=consul \
MICRO_REGISTRY_ADDRESS=consul:8500 \
MICRO_BROKER=nats \
MICRO_BROKER_ADDRESS=nats://nats:4222 \
./service

No code changes, works with CLI flags.

Level 3: Code Options (Production)

reg := consul.NewConsulRegistry(
    registry.Addrs("consul1:8500", "consul2:8500"),
    registry.TLSConfig(tlsConf),
)

b := nats.NewNatsBroker(
    broker.Addrs("nats://nats1:4222", "nats://nats2:4222"),
    nats.DrainConnection(),
)

svc := micro.NewService(
    micro.Name("myservice"),
    micro.Version("1.2.3"),
    micro.Registry(reg),
    micro.Broker(b),
    micro.Address(":8080"),
)

Full control over initialization and configuration.

Level 4: External Config (Enterprise)

cfg := config.NewConfig(
    config.Source(file.NewSource("config.yaml")),
    config.Source(env.NewSource()),
    config.Source(vault.NewSource()),
)

// Use cfg to initialize plugins with complex configs

Environment Variable Patterns

Standard vars for all plugins:

MICRO_REGISTRY=<type>              # consul, etcd, nats, mdns
MICRO_REGISTRY_ADDRESS=<addrs>     # Comma-separated
MICRO_BROKER=<type>
MICRO_BROKER_ADDRESS=<addrs>
MICRO_TRANSPORT=<type>
MICRO_TRANSPORT_ADDRESS=<addrs>
MICRO_STORE=<type>
MICRO_STORE_ADDRESS=<addrs>
MICRO_STORE_DATABASE=<name>
MICRO_STORE_TABLE=<name>

Plugin-specific vars:

ETCD_USERNAME=user
ETCD_PASSWORD=pass
CONSUL_TOKEN=secret

Consequences

Positive

Negative

Mitigations

Validation Example

func (s *service) Init() error {
    if s.opts.Name == "" {
        return errors.New("service name required")
    }
    
    // Warn about development defaults in production
    if isProduction() && usingDefaults() {
        log.Warn("Using development defaults in production")
    }
    
    return nil
}