Tag: multiple email configuration

  • Ever Struggled Sending Dev Emails to Prod?

    Ever Struggled Sending Dev Emails to Prod?

    Use @Profile to Stop That Madness

    Situation

    We’ve all been there: you’re building a feature that sends emails—maybe it’s a password reset, a newsletter, or a welcome message. You test it locally, everything looks good, and then… you forget to switch something before deploying. Suddenly, real users are receiving test emails with dummy links or placeholder content. Ouch.

    Even worse? Maybe you’re in a test environment, and emails aren’t sent at all, because the SMTP server is missing, misconfigured, or the credentials expired. Now you’re debugging not your app, but infrastructure.

    In environments like dev, test, and production, the behavior of email sending should be different, but the code should stay the same.

    Task

    The task is clear: prevent email logic from leaking into the wrong environment. Your dev/test environments should not send real emails, but your prod environment must send them reliably. And you don’t want fragile if (env.equals("prod")) logic scattered across your codebase.

    You want clean, maintainable, environment-specific email behavior with zero risk of mixing environments.

    Action

    Enter Spring’s @Profile.

    Using @Profile, you can define multiple implementations of the same interface and let Spring automatically choose the right one depending on the active environment. Let’s look at an example:

    Step 1: Define an Interface

    public interface MailSender {
        void send(String to, String message);
    }
    

    Step 2: Create Environment-Specific Implementations

    @Component
    @Profile("prod")
    public class SmtpMailSender implements MailSender {
        public void send(String to, String message) {
            // Send email via SMTP
            System.out.println("SMTP: Email sent to " + to);
        }
    }
    
    @Component
    @Profile("dev")
    public class MockMailSender implements MailSender {
        public void send(String to, String message) {
            // Log email instead of sending
            System.out.println("DEV: Mock email to " + to + ": " + message);
        }
    }
    

    Now Spring will only register one of them based on your active profile.

    Step 3: Inject and Use Without Worry

    @Service
    public class AccountService {
    
        private final MailSender mailSender;
    
        public AccountService(MailSender mailSender) {
            this.mailSender = mailSender;
        }
    
        public void sendWelcomeEmail(String userEmail) {
            mailSender.send(userEmail, "Welcome to our app!");
        }
    }
    

    Step 4: Activate Profiles Easily

    • In application.properties: propertiesCopiarEditarspring.profiles.active=dev
    • Or via CLI:
    java -Dspring.profiles.active=prod -jar app.jar

    Result

    Now you can:

    • Safely test email logic in dev without ever risking real emails going out.
    • See mock output in logs to verify formatting and triggers.
    • Ensure real emails go out in production with zero changes to your business logic.
    • Avoid environment conditionals and keep your code clean and maintainable.

    Final Considerations: SOLID Principles in Action

    This approach isn’t just a trick — it’s an application of solid software design principles:

    • S – Single Responsibility: Each MailSender implementation has one job—either mock or real sending.
    • O – Open/Closed Principle: You can add new environments (test, qa, staging) without touching existing code.
    • L – Liskov Substitution: Both MockMailSender and SmtpMailSender can be used interchangeably via the MailSender interface.
    • I – Interface Segregation: The interface is simple, with just the method needed to send an email.
    • D – Dependency Inversion: AccountService depends on an abstraction (MailSender), not a concrete implementation.

    By leveraging Spring’s @Profile, you’re not just solving a practical problem — you’re applying architectural discipline that will scale with your app.