The Modern Spring Boot Config Blueprint

The Modern Spring Boot Config Blueprint

Most production issues don’t come from complex algorithms. They come from missing or misconfigured properties. Spring Boot makes configuration feel easy — until it isn’t. What begins as a few @Value annotations often evolves into a scattered, fragile system where missing or misconfigured properties only fail at runtime.

These patterns are simple but powerful — and applying them early is the difference between a clean system and configuration chaos. The real problem isn’t code — It’s Configuration.

⚡ TL;DR (Quick Recap)

  • Use @ConfigurationProperties with records, not @Value
  • Split config using application-{env}.yml
  • Add @Validated for fail-fast startup
  • Never commit secrets — use env vars or secret managers
  • All examples assume Spring Boot 3.x/4.x

Why Configuration Breaks at Scale

Modern applications run across multiple environments:

  • Local
  • CI
  • Staging
  • Production

Each environment introduces differences:

  • URLs
  • Credentials
  • Feature toggles

A flat, string-based configuration model cannot safely handle this complexity. Without structure and validation, configuration becomes an invisible system dependency.

Use Records for Type-Safe Configuration

@Value is convenient—but fundamentally unsafe at scale. Use @Value only when you need SpEL(Spring Expression Language) logic.

The Problem

@Value("${payment.gateway.url}")
private String url;
@Value("${payment.gateway.timeout}")
private int timeout;
  • No grouping
  • No validation
  • No refactor safety

The Modern Standard: @ConfigurationProperties + Record

payment:
gateway:
url: https://api.example.com
timeout: 5s
@ConfigurationProperties(prefix = "payment.gateway")
@Validated
public record PaymentProperties(
@NotBlank
String url,
@NotNull
@DurationMin(seconds = 1)
Duration timeout
) {}

Why Records Should Be Your Default

Immutability by design — Configuration cannot change unexpectedly at runtime.
No boilerplate — No getters, setters or Lombok.
Clear intent — This is configuration data — not behavior.
Safer systems — You eliminate bugs caused by mutable config state.

Enable Configuration Binding

@SpringBootApplication
@ConfigurationPropertiesScan
public class Application {}

Spring Boot will bind properties directly to the record constructor. Spring Boot 3+, using @Validated on a Record requires the spring-boot-starter-validation dependency.

You can easily generate your own configuration metadata file from items annotated with @ConfigurationProperties by using the spring-boot-configuration-processor .

When to Use a Class Instead

Records should be your default — but not your only tool.

Use a class if you need:

  • Mutable configuration (rare)
  • Complex transformation logic
  • Legacy Spring Boot compatibility

For most modern services, records are the right choice.

Separate Configuration by Environment

Trying to manage all environments in one file is a losing strategy.

Structure Your Config

application.yml
application-dev.yml
application-staging.yml
application-prod.yml

application.yml (shared defaults)

payment:
gateway:
timeout: 5s

application-dev.yml

payment:
gateway:
url: http://localhost:8080

application-prod.yml

payment:
gateway:
url: ${PAYMENT_GATEWAY_URL}

Why This Matters

  • Prevents accidental production misconfigurations
  • Keeps environments isolated
  • Makes deployments predictable

Profiles are recommended, not mandatory. Alternatives exist:

  • environment variables only
  • external config servers
  • container-based config injection

Fail Fast at Startup

A misconfigured application should never run.

Validation

@ConfigurationProperties(prefix = "api.gateway")
@Validated
public record ApiProperties(
@NotBlank
String url,
@NotNull
@Min(1)
Integer retries
) {}

What You Gain

  • Immediate startup failure on invalid config
  • Clear, actionable error messages
  • No hidden runtime surprises

Fail-fast turns late failures into early feedback.

Never Commit Secrets

This rule is absolute. If a secret enters git, it is permanently exposed.

The Right Pattern

order:
gateway:
api-key: ${ORDER_API_KEY}

Where Secrets Should Live

  • Local → .env (ignored)
  • CI → pipeline variables
  • Production → Vault / Secrets Manager

.gitignore

.env
.env.*
!.env.example
*.local.yml
*.local.properties

Why This Matters

Security issues in configuration are among the most common — and most damaging — failures in real systems.

Putting It All Together

A solid setup includes:

  • @ConfigurationProperties records for every config group
  • Profile-based YAML for environments
  • Validation annotations for safety
  • Externalized secrets

Minimal Setup

@SpringBootApplication
@ConfigurationPropertiesScan
public class Application {}

From here, every new feature inherits a safe configuration model automatically.

Final Takeaways

  • Default to records + @ConfigurationProperties for all configuration.
  • Eliminate @Value except for trivial, isolated cases.
  • Use profile-based YAML to separate environments cleanly.
  • Validate configuration at startup — fail early, not in production.
  • Treat secrets as toxic to version control — never commit them.

The best time to fix configuration is before it becomes a problem. That time is Day Zero.

You can find example of code on GitHub.

Originally posted on marconak-matej.medium.com.