1
0
mirror of https://github.com/go-micro/go-micro.git synced 2025-11-23 21:44:41 +02:00
Files
go-micro/internal/website/docs/architecture/adr-009-progressive-configuration.md
2025-11-13 18:59:34 +00:00

3.5 KiB

layout
layout
default

ADR-009: Progressive Configuration

Status

Accepted

Context

Microservices frameworks face a paradox:

  • Beginners want "Hello World" to work immediately
  • Production needs sophisticated configuration

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:

  • mDNS registry (local)
  • HTTP transport
  • Random available port
  • Memory broker/store

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

  • Fast start: Beginners productive immediately
  • Easy deployment: Env vars for different environments
  • Power when needed: Full programmatic control available
  • Learn incrementally: Complexity introduced as required

Negative

  • Three config sources: Environment, code, and CLI flags can conflict
  • Documentation: Must explain all levels clearly
  • Testing: Need to test all configuration methods

Mitigations

  • Clear precedence: Code options > Environment > Defaults
  • Comprehensive examples for each level
  • Validation and helpful error messages

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
}