Environment profiling — configuring the application separately for development, testing, and production.
Simple analogy: Like a Swiss Army knife. One button opens the blade (dev), another opens the saw (prod). One tool — different modes. Or think of it like clothes: you wear pajamas at home (dev), casual clothes at a coffee shop (test), and a suit at work (prod). Same person, different outfits for different situations.
Why it matters: Running your production database credentials in development, or having DEBUG logging in production, are two common disasters. Profiles prevent these by keeping configurations isolated. Your dev team can see verbose SQL logs and use fake email senders, while production runs with tight security, optimized connection pools, and minimal logging.
application.yml (common):
1spring:2 profiles:3 active: dev4 application:5 name: my-app
application-dev.yml:
1spring:2 datasource:3 url: jdbc:postgresql://localhost:5432/devdb4 username: dev_user5 password: dev_password6 jpa:7 show-sql: true8 hibernate:9 ddl-auto: create-drop10logging:11 level:12 root: DEBUG13 com.example: TRACE14management:15 endpoints:16 web:17 exposure:18 include: "*" # All endpoints in dev
application-prod.yml:
1spring:2 datasource:3 url: jdbc:postgresql://${DB_HOST}:5432/proddb4 hikari:5 maximum-pool-size: 206 minimum-idle: 57 connection-timeout: 300008 idle-timeout: 6000009 jpa:10 show-sql: false11 hibernate:12 ddl-auto: validate13logging:14 level:15 root: WARN16 com.example: INFO17management:18 endpoints:19 web:20 exposure:21 include: health,info,metrics22 metrics:23 export:24 prometheus:25 enabled: true
application-test.yml:
1spring:2 datasource:3 url: jdbc:h2:mem:testdb4 jpa:5 hibernate:6 ddl-auto: create-drop7 mail:8 host: localhost9 port: 1025 # Mailhog or similar
Programmatic beans by profile:
1@Configuration2public class BeansConfig {3 @Bean4 @Profile("dev")5 public MailSender devMailSender() { return new FakeMailSender(); }67 @Bean8 @Profile("prod")9 public MailSender prodMailSender() { return new SmtpMailSender(host, port); }1011 @Bean12 @Profile("!prod")13 public Clock testClock() {14 return Clock.fixed(Instant.parse("2024-01-15T00:00:00Z"), ZoneId.of("UTC"));15 }16}
Environment Variables (12-factor app):
1SPRING_PROFILES_ACTIVE=prod java -jar app.jar2SPRING_DATASOURCE_URL=jdbc:postgresql://... java -jar app.jar
Common mistakes:
ddl-auto: update in production — Hibernate migrations can corrupt data. Use Flyway or Liquibase for controlled schema evolution.spring.profiles.active.@Profile("prod") means the bean only loads when the "prod" profile is active. If you run with no profile, it is ignored.