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.