🍃 Section 5 · Question #29

What are profiles in Spring

In enterprise development, a single compiled artifact (app.jar) must run across multiple deployment stages without modification:


🟢 Junior Level

Spring Profiles provide a core configuration mechanism to segregate parts of your application configuration and bean definitions, ensuring they are only activated in specific deployment environments (such as local, dev, stage, test, or prod).

Why Profiles are Essential

In enterprise development, a single compiled artifact (app.jar) must run across multiple deployment stages without modification:

  • Local Development (local): Uses an in-memory H2 database or local Docker container, with verbose DEBUG logging enabled.
  • Testing (test): Uses mock external payment gateways, test credentials, and isolated test databases.
  • Production (prod): Connects to a secure PostgreSQL cluster with a HikariCP pool, strict SSL certificates, and INFO/WARN log levels.

File-Based Configuration

Spring Boot automatically loads environment-specific configuration files matching the naming pattern application-{profile}.yml in addition to the base application.yml:

# application.yml (Global baseline configuration for all environments)
server:
  port: 8080

---
# application-dev.yml (Active only when 'dev' profile is enabled)
spring:
  datasource:
    url: jdbc:h2:mem:devdb

---
# application-prod.yml (Active only when 'prod' profile is enabled)
spring:
  datasource:
    url: jdbc:postgresql://prod-db.internal:5432/orders

How to Activate Profiles

  1. Via Command-Line Arguments (Executable JAR):
    java -jar app.jar --spring.profiles.active=prod
    
  2. Via OS Environment Variables (Standard in Docker & Kubernetes):
    export SPRING_PROFILES_ACTIVE=prod
    
  3. Via JVM System Properties:
    java -Dspring.profiles.active=prod -jar app.jar
    

🟡 Middle Level

The @Profile Annotation on Classes and @Bean Methods

The @Profile annotation instructs Spring to register a bean only if the specified profile is active in the environment:

package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
public class NotificationConfig {

    @Bean
    @Profile("dev") // Instantiated ONLY when 'dev' profile is active
    public EmailSender mockEmailSender() {
        return new MockEmailSender(); // Logs emails to console
    }

    @Bean
    @Profile("prod") // Instantiated ONLY when 'prod' profile is active
    public EmailSender realEmailSender() {
        return new SmtpEmailSender(); // Connects to enterprise mail gateway
    }
}

Advanced Boolean Logic in @Profile (Spring 5.1+)

Starting with Spring Framework 5.1, the @Profile annotation supports full boolean logic expressions:

  • ! — Logical NOT
  • & — Logical AND
  • | — Logical OR
  • Parentheses () for grouping precedence
@Component
@Profile("prod & !cloud") // Active in production, but ONLY when NOT running in the cloud
public class OnPremiseMonitoringService { }

@Component
@Profile("dev | test") // Active in 'dev' OR in 'test'
public class TestDataSeeder { }

@Component
@Profile("prod & (aws | gcp)") // Complex compound expression
public class CloudStorageAdapter { }

Multi-Document YAML Configuration (Spring Boot 2.4+)

Modern Spring Boot allows consolidating all profile definitions into a single application.yml file using multi-document YAML syntax (---) combined with spring.config.activate.on-profile:

server:
  port: 8080
spring:
  application:
    name: order-service

---
# Dev Profile Configuration
spring:
  config:
    activate:
      on-profile: dev
  datasource:
    url: jdbc:h2:mem:testdb

---
# Production Profile Configuration
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 443
spring:
  datasource:
    url: jdbc:postgresql://cluster.prod:5432/main

🔴 Senior Level

Profile Groups (spring.profiles.group)

In microservice architectures, activating an environment often requires coordinating multiple sub-profiles (caching, logging, metrics, cloud providers). Instead of passing an unwieldy list (--spring.profiles.active=prod,metrics,kafka,redis,aws), Spring Boot 2.4+ provides Profile Groups:

# application.yml
spring:
  profiles:
    group:
      production:
        - prod
        - metrics
        - audit
        - redis-cache
      local-dev:
        - dev
        - h2-console
        - mock-external-apis

Activating --spring.profiles.active=production automatically expands and enables all four constituent profiles simultaneously.

Property Precedence Hierarchy

When the same configuration property (e.g. server.port) is declared in multiple locations, Spring Boot evaluates configuration sources in a strict priority order (from highest to lowest):

1. Command-Line Arguments (--server.port=9090)
             │
             ▼
2. JVM System Properties (-Dserver.port=9090)
             │
             ▼
3. OS Environment Variables (SERVER_PORT=9090)
             │
             ▼
4. Profile-Specific Files OUTSIDE of packaged JAR (config/application-prod.yml)
             │
             ▼
5. Profile-Specific Files PACKAGED INSIDE JAR (application-prod.yml)
             │
             ▼
6. Default Configuration Files OUTSIDE of packaged JAR (config/application.yml)
             │
             ▼
7. Default Configuration Files PACKAGED INSIDE JAR (application.yml)

[!IMPORTANT] Overriding Rule: Properties declared in application-prod.yml always take precedence over and override matching properties declared in base application.yml.

Architectural Boundary: Profiles vs Feature Flags

A common architectural anti-pattern is using Spring Profiles to toggle business features dynamically.

Criterion Spring Profiles Feature Flags (Toggles)
Primary Scope Infrastructure & Topology (Databases, brokers, URLs) Business Logic & Capabilities (New checkout UI, discount engine)
Activation Timing Boot-time only (Requires full JVM restart) Runtime on-the-fly (Changes take effect in milliseconds without restarts)
Management Tools Spring Environment, Kubernetes ConfigMaps LaunchDarkly, Unleash, Togglz, Redis
Granularity Application-wide / Pod-level User-specific, tenant-specific, or canary percentage routing

Fail-Fast Production Guard: Preventing Default Profile Crashes

If a Kubernetes deployment omits the SPRING_PROFILES_ACTIVE environment variable, Spring Boot falls back to the default profile (which often defaults to in-memory H2 or mock configurations), resulting in silent production incidents.

Implementing Mandatory Profile Validation:

package com.example.init;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;

public class MandatoryProfileEnvironmentPostProcessor implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        String[] activeProfiles = environment.getActiveProfiles();
        
        // Ensure production containers fail fast if no active profile is provided:
        if (activeProfiles.length == 0) {
            throw new IllegalStateException(
                "CRITICAL STARTUP ERROR: Application cannot be started without an active profile! "
                + "Please configure SPRING_PROFILES_ACTIVE (e.g. dev, stage, prod)."
            );
        }
    }
}

Register this in META-INF/spring.factories (or META-INF/spring/org.springframework.boot.env.EnvironmentPostProcessor.imports in Boot 3) to enforce production safety.


4 Tricky Questions

1. How do boolean operators work in the @Profile annotation, and from which version of Spring Framework are they supported?

Answer: Starting with Spring Framework 5.1, the @Profile annotation supports full boolean logic using:

  • ! (NOT)
  • & (AND)
  • | (OR)
  • Parentheses () for operator precedence.

For example, @Profile("(dev | test) & !docker") activates the bean only when either dev or test is active, provided that docker is not active. Spring evaluates this condition using ProfileCondition during the BeanDefinitionRegistry phase; if the expression evaluates to false, the bean definition is skipped.

2. What is the exact precedence relationship between application.yml and application-prod.yml?

Answer: When the prod profile is active, Spring Boot loads the baseline application.yml first, and then overlays application-prod.yml on top of it.

If a property is defined in both files, the value in application-prod.yml overrides the value in application.yml. If a property is only defined in application.yml, that baseline value is retained.

3. What are Profile Groups in Spring Boot, and what problem do they solve in microservices?

Answer: Introduced in Spring Boot 2.4, Profile Groups (spring.profiles.group.{groupName}) allow developers to define an alias that groups multiple fine-grained profiles together.

In large microservices, an environment may need distinct profiles for database types, tracing, caching, and cloud providers. Profile groups allow operators to activate a single group (e.g. --spring.profiles.active=production), and Spring automatically expands and activates all child profiles mapped to that group (prod, metrics, redis, kafka).

4. What is the fundamental architectural difference between Spring Profiles and Feature Flags?

Answer:

  • Spring Profiles: Define static, boot-time infrastructure topology. They determine which beans exist in the ApplicationContext and are immutable once the container is refreshed. Changing an active profile requires restarting the JVM.
  • Feature Flags: Control dynamic application behavior and business logic routing. They are evaluated at runtime on every request and can be toggled on or off instantly without restarting the application, often targeting specific users, tenants, or canary percentages.

🎯 Interview Cheat Sheet

30-Second Elevator Pitch

Spring Profiles provide environment-based configuration segregation for Spring applications (dev, stage, prod).

  • Segregates configuration properties via application-{profile}.yml, which override values from base application.yml.
  • Conditionally registers beans via @Profile, supporting boolean expressions (prod & !cloud, dev | test) since Spring 5.1.
  • Multi-document YAML support allows consolidating profiles into a single application.yml using spring.config.activate.on-profile.
  • Profile Groups (spring.profiles.group) bundle multiple sub-profiles into unified alias groups.
  • Profiles configure static boot-time infrastructure (requiring restarts), whereas Feature Flags manage dynamic runtime business toggles.

Profile Mechanism Summary

Feature Syntax / Property Primary Use Case
Activation spring.profiles.active CLI argument, env var, or JVM property
Bean Guard @Profile("prod & !cloud") Conditional bean registration
Multi-Doc YAML spring.config.activate.on-profile Consolidate configs into single YAML file
Grouping spring.profiles.group Map group name to array of sub-profiles

Red Flags (DO NOT Say)

  • ❌ “You can switch Spring Profiles dynamically in runtime without restarting the application.” (Active profiles and bean definitions are frozen once context.refresh() completes).
  • ❌ “Feature flags and Spring Profiles are basically the same thing.” (Profiles configure static infrastructure; feature flags toggle runtime business logic).
  • ❌ “It is safe to commit production passwords into application-prod.yml.” (Production secrets must be externalized to Vault, AWS Secrets Manager, or Kubernetes Secrets).
  • ❌ “The base application.yml has higher priority than application-prod.yml.” (Profile-specific files always override default files).