Hook: Interviewers love Spring Profiles because they reveal whether you can keep one codebase safe across dev, test, and prod without hiding environment logic inside if statements.
Question: Spring Profiles.
Answer: Spring Profiles let you turn beans and configuration on or off depending on the active environment, like dev, test, or prod. They are a clean way to swap things such as databases, log levels, mocks, or security settings without changing your source code. Spring checks the active profiles during startup, and only the matching beans and properties are loaded.
Interview-Ready Answer: I use Spring Profiles to make one application behave differently in different environments. For example, I can run a mock payment client in dev, a real client in prod, and separate config files like application-dev.yml and application-prod.yml. Spring evaluates the active profiles at startup, so the right beans are created and the wrong ones are skipped, which keeps environment-specific code simple and safer.
Detailed Explanation: A profile is just a named environment bucket. Think of it as a label that says, "load these beans and these settings only when this environment is active." In Spring Boot, profiles are most often used for environment separation: local development, automated tests, staging, and production.
Environment, which is the place where configuration values and active profile names are stored.--spring.profiles.active=prod, the SPRING_PROFILES_ACTIVE environment variable, JVM system properties, or programmatic setup.default profile is used.@Bean method has @Profile, Spring evaluates the profile expression before the bean is registered.application-dev.yml, application-prod.properties, or multi-document config using spring.config.activate.on-profile.@Primary or @Qualifier to remove ambiguity.Use profiles when the environment changes the wiring, not the business rule. Great examples are a fake email sender in development, a different database vendor in production, debug logging locally, or turning off noisy tools in staging. Do not use profiles for per-user behavior or request-level feature flags; that is a different problem.
| Approach | Best for | Main risk |
|---|---|---|
| Spring Profiles | Whole environments | Too coarse for features |
@ConditionalOnProperty | Single flags | Can become scattered |
if/else in code | Rare quick checks | Hard to test |
Performance and edge cases: Profile checks happen at startup, so the runtime cost is basically zero after the app is running. The work is roughly proportional to the number of bean definitions and config documents, so in a medium app with a few hundred beans the overhead is tiny compared with classpath scanning and bean construction. A big gotcha is that multiple active profiles can activate multiple beans of the same type, which causes startup failures unless you disambiguate. Another useful detail: since Spring Boot 2.4, the modern way to activate a document inside one file is spring.config.activate.on-profile, while separate files like application-dev.yml still work fine.
Memory model: A profile is not a switch you flip after startup; it is a filter that decides what is allowed into the container before the app starts serving requests.
Real-world story: Imagine a checkout service used by an online store. In dev, the team wants H2, fake payment calls, and verbose SQL logs. In prod, they need PostgreSQL, the real payment gateway, and safer logging. One day, Kubernetes starts the service without SPRING_PROFILES_ACTIVE=prod, so the app comes up in the default profile and quietly uses the wrong database.
The lesson is simple: profiles do not just change convenience settings; they can decide whether your app talks to a real system or a fake one. That is why teams treat the active profile as part of deployment configuration, not as a developer preference.
package com.example.profilesdemo;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
@SpringBootApplication
public class SpringProfilesApplication {
public static void main(String[] args) {
SpringApplication.run(SpringProfilesApplication.class, args);
}
@Bean
CommandLineRunner demoRunner(MessageService messageService, Environment environment) {
return args -> {
// The Environment tells us what Spring decided before any request is handled.
System.out.println("Active profiles: " + Arrays.toString(environment.getActiveProfiles()));
System.out.println("Default profiles: " + Arrays.toString(environment.getDefaultProfiles()));
System.out.println("Message: " + messageService.message());
System.out.println("Run with --spring.profiles.active=dev or prod to switch behavior.");
};
}
interface MessageService {
String message();
}
@Configuration
static class MessageConfiguration {
@Bean
@Profile("dev")
MessageService devMessageService() {
// Dev is intentionally friendly for local work: cheap, noisy, and safe to reset.
return () -> "DEV profile: using mock integrations and verbose output.";
}
@Bean
@Profile("prod")
MessageService prodMessageService() {
// Prod should prefer real behavior and safer defaults.
return () -> "PROD profile: using real integrations and production settings.";
}
@Bean
@Profile("default")
MessageService defaultMessageService() {
// This bean exists only when no explicit profile was set.
return () -> "DEFAULT profile: no active profile was configured.";
}
}
@Configuration
@Profile("broken")
static class BrokenProfileConfiguration {
@Bean
String brokenBean() {
// Edge case: if someone activates the broken profile, startup fails fast.
// That is useful for proving that profile-specific wiring is really happening.
throw new IllegalStateException("Broken profile intentionally fails during startup.");
}
}
}
Follow-up & Tricky Questions:
--spring.profiles.active=prod, the SPRING_PROFILES_ACTIVE environment variable, or programmatic setup. In Boot, command-line arguments usually have very high precedence, so they are common in deployments.default profile. Once you explicitly activate another profile, default is no longer the fallback.@ConditionalOnProperty? Use profiles for broad environment choices such as dev or prod. Use @ConditionalOnProperty for a precise toggle like feature.payments.mock=true.application-dev.yml or application-prod.properties are loaded when their profile is active, and they are merged with the rest of the configuration using Spring Boot's normal precedence rules.@Profile on a method or a config class? Yes. On a configuration class, all beans inside that class are controlled together; on a @Bean method, only that bean is controlled.@Profile change behavior at runtime? No. It is decided during startup. If you want behavior that changes while the app is running, use a different mechanism such as feature flags or configuration refresh.@Primary, @Qualifier, or by redesigning the bean split.dev, test, and prod. Teams usually keep them lowercase to avoid confusion.default mean the app is in production? No. default just means "no explicit profile was chosen," which is often dangerous for production deployments.@Profile("prod"), is it created lazily? No. It is either registered during startup or skipped entirely; profile matching is not a lazy-load feature.Common Mistakes:
SPRING_PROFILES_ACTIVE or an equivalent deployment setting part of your release checklist.@Primary or @Qualifier.Memory Hook: Think of profiles as modes on a camera: portrait, night, and video. You do not use all modes at once; you pick the one that matches the situation, and the camera loads the right behavior before you shoot.
Cheat Sheet:
dev, test, and prod.@Profile can be placed on a config class or on a @Bean method.default profile applies.application-dev.yml are a standard way to override config.Practice Tasks:
dev and prod profiles and print a different greeting for each.application-dev.yml and application-prod.yml with different datasource URLs, then verify which one loads.@Qualifier.