Apex Class

Apex Class & Business Logic Practice Problems

47 free Salesforce Apex challenges · Full problem statements, best-practice notes, and step-by-step hints · Solve live on ApexArena

This guide walks through 47 general-purpose Apex classes — collections, string handling, business logic, and service-layer design. Each entry below includes the full problem statement, the governor-limit and best-practice constraints it's testing, and a numbered approach to solving it — then you can open the same problem in ApexArena's browser-based Apex editor and get instant pass/fail feedback against real test cases.

On this page
  1. 1. Temperature Converter Utility Class
  2. 2. Opportunity Stage Helper
  3. 3. String Utilities Class
  4. 4. Math Helper Class
  5. 5. Account Service Class
  6. 6. Tiered Discount Calculator
  7. 7. RecordType ID Finder
  8. 8. JSON Response Parser
  9. 9. HTTP Callout with Retry Logic
  10. 10. Batch Apex: Deactivate Old Contacts
  11. 11. Custom Exception Classes
  12. 12. Queueable Apex: Send Welcome Email
  13. 13. Selector Layer: Contact Selector
  14. 14. Lead Score Calculator
  15. 15. Opportunity Stage Transition Validator
  16. 16. Territory Region Mapper
  17. 17. Apex REST Account Service
  18. 18. HTTP Callout Handler with Mock Support
  19. 19. Custom Exception Hierarchy
  20. 20. String Utility: Reverse Words in a Sentence
  21. 21. Word Frequency Counter
  22. 22. Factorial Calculator
  23. 23. FizzBuzz: Apex Edition
  24. 24. AccountTierService: Classify Account Revenue Tier
  25. 25. DiscountApprovalService: Route Discount Approval by Level
  26. 26. AccountHealthService: Calculate Multi-Factor Health Score
  27. 27. Apex Class: Lead Qualifier — Grade by Employees & Budget
  28. 28. Apex Class: Invoice Calculator — Discount & Tax
  29. 29. Apex Class: Phone Formatter — US Format (XXX) XXX-XXXX
  30. 30. Apex Class: SLA Checker — Is Case SLA Breached?
  31. 31. Apex Class: Name Formatter — Salutation + Full Name
  32. 32. Apex Service Class: Merge Duplicate Contacts by Account
  33. 33. Apex Service Class: Account Health Score (Bulk-Safe)
  34. 34. Calculate Customer Loyalty Points on Closed Won
  35. 35. Determine Customer Discount by Account Type and Revenue
  36. 36. Validate Product Stock Before Creating an Order
  37. 37. Create Utility Methods to Calculate Shipping Charges by Region
  38. 38. Generate Monthly Sales Summaries for Management Dashboards
  39. 39. Calculate SLA Due Dates While Excluding Weekends
  40. 40. Create a Reusable Tax Calculation Engine Supporting Multiple Countries
  41. 41. Apply Dynamic Pricing Rules Based on Customer Segments
  42. 42. Calculate Employee Bonuses Using Configurable Business Rules
  43. 43. Build a Reusable Address Validation Service Class
  44. 44. Calculate Subscription Renewal Amounts With Discounts
  45. 45. Implement Dynamic Product Recommendation Logic
  46. 46. Generate Financial Reports Grouped by Fiscal Quarter
  47. 47. Build a Generic Notification Service for Multiple Objects
Easy Apex Class

1. Temperature Converter Utility Class

Problem #21 · Salesforce Apex Coding Challenge

Problem Statement

Create a utility class TemperatureConverter with two static methods:

  • celsiusToFahrenheit(Decimal c) → returns Decimal
  • fahrenheitToCelsius(Decimal f) → returns Decimal

Formulas: F = C × 9/5 + 32  |  C = (F − 32) × 5/9

Constraints

  • Handle null input — return null if input is null
  • Round to 2 decimal places
Approach
  • 1Use Decimal.setScale(2, RoundingMode.HALF_UP) or .setScale(2) to round.
  • 2Guard against null: if (c == null) return null;
Easy Apex Class

2. Opportunity Stage Helper

Problem #23 · Salesforce Apex Coding Challenge

Problem Statement

Create a class OpportunityHelper with a static method isWon(String stage) that returns true if the stage equals "Closed Won" (case-insensitive), otherwise false.

Also add isLost(String stage) that returns true for "Closed Lost".

Approach
  • 1Use equalsIgnoreCase() for case-insensitive comparison.
  • 2Guard null: if (stage == null) return false;
Easy Apex Class

3. String Utilities Class

Problem #26 · Salesforce Apex Coding Challenge

Problem Statement

Create a class StringUtils with these static methods:

  • isPalindrome(String s) — returns true if s reads the same forwards and backwards (ignore case)
  • countWords(String s) — returns the number of words (split on whitespace)
  • capitalizeFirst(String s) — capitalizes the first letter of every word
Approach
  • 1For palindrome: reverse the string with new StringBuilder(s).reverse().toString() or reverse the chars manually.
  • 2For words: s.split('\\s+').size() after trimming.
  • 3For capitalize: s.split(' '), capitalize each word, String.join.
Easy Apex Class

4. Math Helper Class

Problem #29 · Salesforce Apex Coding Challenge

Problem Statement

Create a class MathHelper with these static methods:

  • factorial(Integer n) — returns n! (use recursion or iteration)
  • isPrime(Integer n) — returns true if n is prime
  • fibonacci(Integer n) — returns the nth Fibonacci number (0-indexed)

Constraints

  • Handle n ≤ 0 gracefully (return 0 for fibonacci, 1 for factorial, false for isPrime)
Approach
  • 1Factorial: for (Long result = 1; n > 1; n--) result *= n;
  • 2isPrime: check divisibility from 2 to sqrt(n).
  • 3Fibonacci: iterative with two variables is simplest.
Medium Apex ClassSOQL

5. Account Service Class

Problem #32 · Salesforce Apex Coding Challenge

Problem Statement

Create a class AccountService with:

  • getHotAccounts() — returns a List<Account> where Rating = 'Hot', ordered by Name
  • deactivateSmallAccounts(Decimal maxRevenue) — sets Active__c = false on all Accounts with AnnualRevenue < maxRevenue, updates them, returns the count updated
Approach
  • 1Use WHERE Rating = 'Hot' ORDER BY Name in your SOQL.
  • 2Collect records to update in a List, then call update once outside the loop.
Easy Apex Class

6. Tiered Discount Calculator

Problem #34 · Salesforce Apex Coding Challenge

Problem Statement

Create a class DiscountCalculator with a static method getDiscountedPrice(Decimal originalPrice, Integer quantity):

  • Quantity 1–9 → 0% discount
  • Quantity 10–49 → 10% discount
  • Quantity 50–99 → 20% discount
  • Quantity ≥ 100 → 30% discount

Returns the final price per unit after discount, rounded to 2 decimal places.

Approach
  • 1Calculate discount rate, then: originalPrice * (1 - discountRate)
  • 2.setScale(2) to round.
Medium Apex ClassSOQL

7. RecordType ID Finder

Problem #36 · Salesforce Apex Coding Challenge

Problem Statement

Create a class RecordTypeHelper with a static method:

getId(String objectName, String developerName)

Returns the Id of the RecordType matching the given sObject name and developer name, or null if not found.

Must cache results in a static Map so subsequent calls for the same object don't trigger more SOQL.

Approach
  • 1Query: SELECT Id FROM RecordType WHERE SobjectType = :objectName AND DeveloperName = :developerName LIMIT 1
  • 2Put result in cache before returning.
Medium Apex Class

8. JSON Response Parser

Problem #38 · Salesforce Apex Coding Challenge

Problem Statement

Create a class JsonParser with a static method parseContacts(String jsonStr) that parses a JSON array of objects like:

[{"name":"Alice","email":"alice@x.com"},{"name":"Bob","email":"bob@x.com"}]

Returns a List<Contact> with LastName set from name and Email set from email.

Approach
  • 1Define an inner class: class ContactJson { public String name; public String email; }
  • 2Use JSON.deserialize(jsonStr, List.class) to parse.
  • 3Map name → LastName, email → Email.
Hard Apex Class

9. HTTP Callout with Retry Logic

Problem #41 · Salesforce Apex Coding Challenge

Problem Statement

Create a class HttpCalloutService with a method callWithRetry(String endpoint, Integer maxRetries).

It should make an HTTP GET request, and if the response status is not 200, retry up to maxRetries times with a 1-second delay between attempts.

Return the response body on success, or throw a CalloutException after all retries are exhausted.

Approach
  • 1HttpRequest req = new HttpRequest(); req.setEndpoint(endpoint); req.setMethod('GET');
  • 2HttpResponse res = new Http().send(req);
  • 3if (res.getStatusCode() == 200) return res.getBody();
Hard Apex ClassBatch

10. Batch Apex: Deactivate Old Contacts

Problem #43 · Salesforce Apex Coding Challenge

Problem Statement

Create a Batch Apex class DeactivateOldContactsBatch that:

  • Implements Database.Batchable<SObject>
  • Queries all Contacts where LastActivityDate < 365 days ago
  • Sets Active__c = false on each Contact in the execute method
  • Has a finish method that sends an email summary
Approach
  • 1Date cutoff = Date.today().addDays(-365);
  • 2Query: SELECT Id, Active__c FROM Contact WHERE LastActivityDate < :cutoff
  • 3In finish: use Messaging.sendEmail() or just log.
Easy Apex Class

11. Custom Exception Classes

Problem #45 · Salesforce Apex Coding Challenge

Problem Statement

Create a class AccountValidator with a custom exception InvalidAccountException (extends Exception).

Add a static method validate(Account acc) that throws InvalidAccountException if:

  • Account Name is blank
  • AnnualRevenue is negative

Returns true if valid.

Approach
  • 1throw new InvalidAccountException('Name cannot be blank');
  • 2if (acc.AnnualRevenue != null && acc.AnnualRevenue < 0) throw ...
Hard Apex ClassAsync

12. Queueable Apex: Send Welcome Email

Problem #48 · Salesforce Apex Coding Challenge

Problem Statement

Create a Queueable Apex class WelcomeEmailQueueable that:

  • Implements Queueable
  • Accepts a List<Id> of Contact IDs in its constructor
  • In execute(): queries the contacts, sends a welcome email to each
  • Enqueued with System.enqueueJob()
Approach
  • 1Query: SELECT Id, Email, FirstName FROM Contact WHERE Id IN :contactIds
  • 2Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
  • 3Messaging.sendEmail(new List{ mail });
Medium Apex ClassSOQL

13. Selector Layer: Contact Selector

Problem #49 · Salesforce Apex Coding Challenge

Problem Statement

Implement a selector class ContactSelector following enterprise patterns:

  • getByIds(Set<Id> ids) — returns Contacts by ID with standard fields
  • getByAccountId(Id accountId) — returns Contacts for an Account
  • getByEmail(String email) — returns a Contact matching the email

All queries must select: Id, FirstName, LastName, Email, Phone, AccountId

Approach
  • 1Use Database.query(BASE_FIELDS + ' WHERE Id IN :ids') for dynamic queries.
  • 2For getByEmail use LIMIT 1 and return the first result or null.
Medium Apex Classes

14. Lead Score Calculator

Problem #70 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class LeadScorer with a static method calculateScore that computes a numeric lead score based on attributes.

Scoring rules:

  • Industry: Technology = +30, Finance = +25, Healthcare = +20, other = +10
  • Annual Revenue: >= 10,000,000 = +40, >= 1,000,000 = +25, >= 100,000 = +10, else 0
  • Employee Count: >= 1000 = +20, >= 100 = +10, else 0
  • Has Phone: +5 if phone is not null/blank
  • Has Email: +5 if email is not null/blank

Maximum possible score: 100

Method Signature

static Integer calculateScore(String industry, Decimal annualRevenue, Integer employees, String phone, String email)

Constraints

  • All parameters may be null — treat null numeric fields as 0
  • Return an Integer score (0–100)
Approach
  • 1Use if/else if chains for each scoring category
  • 2Guard against null with a null check before comparing numbers
  • 3String.isBlank() checks both null and empty — but use != null && !phone.equals('') style for compatibility
Medium Apex Classes

15. Opportunity Stage Transition Validator

Problem #71 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class StageValidator with a static method isValidTransition that enforces allowed stage progressions for Opportunities.

Allowed transitions:

  • Prospecting → Qualification, Closed Lost
  • Qualification → Needs Analysis, Closed Lost
  • Needs Analysis → Value Proposition, Closed Lost
  • Value Proposition → Id. Decision Makers, Closed Lost
  • Id. Decision Makers → Perception Analysis, Closed Lost
  • Perception Analysis → Proposal/Price Quote, Closed Lost
  • Proposal/Price Quote → Negotiation/Review, Closed Lost
  • Negotiation/Review → Closed Won, Closed Lost
  • Closed Won → (no transitions allowed)
  • Closed Lost → (no transitions allowed)

Method Signature

static Boolean isValidTransition(String fromStage, String toStage)

Constraints

  • Return true only for explicitly listed transitions
  • Staying in the same stage returns false
  • Null inputs return false
Approach
  • 1Use a Map> to store allowed next stages for each current stage
  • 2Check containsKey before calling get to avoid null pointer
  • 3Use List.contains() to test if toStage is in the allowed set
Easy Apex Classes

16. Territory Region Mapper

Problem #72 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class TerritoryMapper with a static method getRegion that maps US state codes to sales regions.

Region mappings:

  • Northeast: ME, NH, VT, MA, RI, CT, NY, NJ, PA
  • Southeast: DE, MD, VA, WV, NC, SC, GA, FL, AL, MS, TN, KY, AR, LA
  • Midwest: OH, IN, IL, MI, WI, MN, IA, MO, ND, SD, NE, KS
  • Southwest: TX, OK, NM, AZ
  • West: CO, WY, MT, ID, WA, OR, CA, NV, UT, AK, HI
  • Unknown state codes return 'Unknown'

Method Signature

static String getRegion(String stateCode)

Constraints

  • State codes are uppercase 2-letter abbreviations
  • Null or blank input returns 'Unknown'
  • Case-insensitive matching (normalize to uppercase)
Approach
  • 1Use a Map where each state code maps to a region name
  • 2Alternatively, use a Set for each region and check containment
  • 3Remember to call toUpperCase() to normalize the input
Hard APIApex Classes

17. Apex REST Account Service

Problem #73 · Salesforce Apex Coding Challenge

Problem Statement

Create an Apex REST service that exposes Account data via HTTP endpoints.

Requirements:

  • Annotate the class with @RestResource(urlMapping='/accounts/*')
  • Implement a GET method annotated with @HttpGet that queries Accounts and returns them as a JSON-serializable list
  • Implement a POST method annotated with @HttpPost that accepts an Account name in the request body and inserts a new Account
  • Implement a DELETE method annotated with @HttpDelete that deletes an Account by ID from the URL parameter
  • Use RestContext.request and RestContext.response where needed
  • Return appropriate HTTP status codes (200, 201, 400, 404)

Constraints

  • All methods must be global static
  • Handle the case where the record is not found (404)
  • Use try/catch for DML operations
Approach
  • 1Use RestContext.request.requestURI to extract the record ID from the URL
  • 2Set RestContext.response.statusCode = 201 after a successful insert
  • 3Deserialize the POST body with JSON.deserialize() or RestContext.request.requestBody.toString()
Hard APIApex Classes

18. HTTP Callout Handler with Mock Support

Problem #74 · Salesforce Apex Coding Challenge

Problem Statement

Build a robust HTTP callout handler class and its corresponding mock for testing.

Requirements for HttpCalloutHandler:

  • A method fetchExternalData(String endpoint) that makes an HTTP GET callout
  • Parse the JSON response into a wrapper class ApiResponse with fields: Integer statusCode, String body, Boolean success
  • If status code is 200, set success = true; otherwise false
  • Handle callout exceptions — set success=false, statusCode=500, body=exception message

Requirements for HttpCalloutHandlerMock:

  • Implement the HttpCalloutMock interface
  • Implement the respond method returning a mock 200 response

Constraints

  • Use Http, HttpRequest, HttpResponse classes
  • Set request timeout to 10000ms (10 seconds)
Approach
  • 1Use new Http().send(req) to make the callout
  • 2Set req.setMethod('GET') and req.setEndpoint(endpoint)
  • 3req.setTimeout(10000) sets the timeout in milliseconds
  • 4For the mock, return a new HttpResponse with setStatusCode(200) and setBody('{"ok":true}')
Medium Apex Classes

19. Custom Exception Hierarchy

Problem #76 · Salesforce Apex Coding Challenge

Problem Statement

Design a robust custom exception hierarchy for a payment processing service.

Requirements:

  • Create a base exception PaymentException extending Exception
  • Create subclasses:
    • InsufficientFundsException — includes Decimal balance and Decimal required fields
    • InvalidCardException — includes String cardType field
    • PaymentGatewayException — includes Integer errorCode and String gatewayMessage
  • Write a PaymentProcessor class with method processPayment(Decimal amount, String cardType, Decimal accountBalance)
  • Throw InvalidCardException if cardType is not in ('Visa','Mastercard','Amex')
  • Throw InsufficientFundsException if accountBalance < amount
  • Otherwise return true

Constraints

  • Each custom exception must extend the appropriate parent
  • Custom fields must be set via constructor or setter before throwing
Approach
  • 1Use a Set of valid card types and check .contains(cardType)
  • 2Throw and set fields: InvalidCardException e = new InvalidCardException(); e.cardType = cardType; throw e;
  • 3Order matters: check card type before balance to match expected exception priority
Easy Apex ClassesStrings

20. String Utility: Reverse Words in a Sentence

Problem #87 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class StringUtils with a static method reverseWords(String sentence) that reverses the order of words in a sentence while preserving each word's characters.

Examples

  • "Hello World Apex""Apex World Hello"
  • "I love Salesforce""Salesforce love I"
  • null or blank → ""

Method Signature

public static String reverseWords(String sentence)
Approach
  • 1Use String.split(' ') to break into words.
  • 2Reverse the list with a simple loop or by building a new list.
  • 3Rejoin with String.join(list, ' ').
Easy Apex ClassesCollections

21. Word Frequency Counter

Problem #88 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class WordCounter with a static method countWords(List<String> words) that returns a Map<String, Integer> counting the frequency of each word (case-insensitive).

Examples

  • ['apex','java','apex','APEX']{apex:3, java:1}
  • Empty list → empty map

Method Signature

public static Map<String, Integer> countWords(List<String> words)
Approach
  • 1Normalise each word with .toLowerCase() before counting.
  • 2Use map.containsKey() to check existing entries.
  • 3Increment with map.get(word) + 1.
Easy Apex ClassesRecursion

22. Factorial Calculator

Problem #89 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class MathUtils with a static method factorial(Integer n) that returns n!.

Rules

  • 0! = 1 and 1! = 1
  • 5! = 120
  • Return -1 for negative inputs

Method Signature

public static Integer factorial(Integer n)
Approach
  • 10! = 1 is the base case.
  • 2Use a loop: result *= i for i from 2 to n.
  • 3Or use recursion: n * factorial(n-1).
Easy Apex ClassesControl Flow

23. FizzBuzz: Apex Edition

Problem #90 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class FizzBuzz with a static method generate(Integer n) that returns a List<String> of 1 through n with:

  • Multiples of 3 → "Fizz"
  • Multiples of 5 → "Buzz"
  • Multiples of both → "FizzBuzz"
  • Otherwise → the number as a String

Method Signature

public static List<String> generate(Integer n)
Approach
  • 1Check divisibility with Math.mod(i, 3) == 0.
  • 2Check FizzBuzz first (divisible by both 3 and 5).
  • 3Use String.valueOf(i) to convert the number.
Easy Apex ClassesBusiness Logic

24. AccountTierService: Classify Account Revenue Tier

Problem #145 · Salesforce Apex Coding Challenge

Problem Statement

Your sales operations team needs to automatically classify accounts into revenue tiers to determine service level agreements and pricing models.

Write an Apex class AccountTierService with a static method getRevenueTier(Decimal annualRevenue) that returns a tier label based on the account's annual revenue.

Tier Definitions

Annual RevenueTier
< $50,000 or null'Bronze'
$50,000 – $249,999'Silver'
$250,000 – $999,999'Gold'
≥ $1,000,000'Platinum'

Method Signature

public static String getRevenueTier(Decimal annualRevenue)

Best Practices

  • Always handle null input gracefully — null revenue maps to 'Bronze'.
  • Use a single return per branch for clarity.
Approach
  • 1Check for null first — null revenue should default to Bronze.
  • 2Use if/else-if chains: < 50K → Bronze, < 250K → Silver, < 1M → Gold, else → Platinum.
  • 3Boundary values: 50,000 is the first Silver value; 250,000 is the first Gold value.

Halfway there — solve them live

Every problem above runs in a real in-browser Apex editor with instant pass/fail feedback and best-practice linting. No Salesforce org needed.

Create Free Account →
Easy Apex ClassesBusiness Logic

25. DiscountApprovalService: Route Discount Approval by Level

Problem #146 · Salesforce Apex Coding Challenge

Problem Statement

Your finance team requires that discount approvals are routed to the correct authority level based on the percentage discount being offered on an opportunity.

Write an Apex class DiscountApprovalService with a static method getApprovalLevel(Decimal discountPercent) that returns the required approver.

Approval Matrix

Discount %Approval Level
null or < 0'Invalid'
0% – 5%'Rep'
>5% – 15%'Manager'
>15% – 25%'Director'
>25%'VP'

Method Signature

public static String getApprovalLevel(Decimal discountPercent)
Approach
  • 1Check null and negative values first — return "Invalid".
  • 20–5 → Rep; 5–15 → Manager; 15–25 → Director; above 25 → VP.
  • 3Use <= 5 for the Rep boundary so exactly 5% still routes to Rep.
Medium Apex ClassesBusiness LogicScoring

26. AccountHealthService: Calculate Multi-Factor Health Score

Problem #147 · Salesforce Apex Coding Challenge

Problem Statement

Your account management team needs a single numeric health score (0–100) per account to prioritise outreach. The score is calculated from five weighted factors.

Write an Apex class AccountHealthService with a static method getHealthScore(String industry, Decimal annualRevenue, String rating, Integer contactCount, String billingState) that returns an Integer score.

Scoring Factors

  • Annual Revenue (0–30 pts): ≥ $1M = 30 | ≥ $500K = 20 | ≥ $100K = 10 | else 0
  • Rating (0–25 pts): 'Hot' = 25 | 'Warm' = 15 | 'Cold' = 5 | else 0
  • Industry (10–20 pts): 'Technology' or 'Finance' = 20 | else 10
  • Contact Count (0–15 pts): ≥ 10 = 15 | ≥ 5 = 10 | ≥ 1 = 5 | else 0
  • Billing State (0–10 pts): non-null/non-blank = 10 | else 0

Method Signature

public static Integer getHealthScore(String industry, Decimal annualRevenue, String rating, Integer contactCount, String billingState)
Approach
  • 1Start with revenue: if null → 0 pts; >= 1M → 30; >= 500K → 20; >= 100K → 10.
  • 2For industry, always award at least 10 pts; bump to 20 for Technology or Finance.
  • 3Guard contactCount and billingState against null before comparing.
  • 4Maximum possible score is 30 + 25 + 20 + 15 + 10 = 100.
Easy Apex Class

27. Apex Class: Lead Qualifier — Grade by Employees & Budget

Problem #280 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class LeadQualifier with a static method qualify(String source, Integer numEmployees, Decimal budget) that returns a letter grade based on company size and budget.

Grading Rules

  • A — employees ≥ 1,000 OR budget ≥ 500,000
  • B — employees ≥ 200 OR budget ≥ 100,000
  • C — employees ≥ 50
  • Unqualified — everything else

Requirements

  • Null-safe: treat null employees or budget as 0
  • Define thresholds as named constants (not magic numbers)
  • Use with sharing on the class
Approach
  • 1Treat null numEmployees and budget as 0 using the ternary operator.
  • 2Check the highest grade first (A), then B, C — use early returns.
  • 3Store thresholds in private static final constants to avoid magic numbers.
Easy Apex Class

28. Apex Class: Invoice Calculator — Discount & Tax

Problem #281 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class InvoiceCalculator with a static method calculateTotal(List<Decimal> lineItems, Decimal taxRate, Decimal discountPct) that computes the final invoice total.

Formula

subtotal     = sum of all positive line items
afterDiscount = subtotal × (1 − discountPct ÷ 100)
total         = afterDiscount × (1 + taxRate ÷ 100)
Return total rounded to 2 decimal places.

Requirements

  • Skip null or negative line items
  • Treat null taxRate or discountPct as 0
  • Return 0 for null or empty line-item list
  • Round the result to 2 decimal places using .setScale(2)
Approach
  • 1Sum only items that are non-null and > 0.
  • 2Discount formula: subtotal * (1 - discountPct/100).
  • 3Tax formula: afterDiscount * (1 + taxRate/100).
  • 4Use .setScale(2) on a Decimal to round to 2 decimal places.
Easy Apex Class

29. Apex Class: Phone Formatter — US Format (XXX) XXX-XXXX

Problem #282 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class PhoneFormatter with a static method formatPhone(String rawPhone) that formats any 10-digit US phone number into the standard (XXX) XXX-XXXX pattern.

Rules

  • Strip all non-digit characters first (dashes, dots, spaces, parens)
  • If the result is exactly 10 digits, format as (XXX) XXX-XXXX
  • If not 10 digits, return the original input unchanged
  • If blank or null, return ""

Examples

  • "415-555-1234""(415) 555-1234"
  • "4155551234""(415) 555-1234"
  • "415.555.1234""(415) 555-1234"
  • "12345""12345" (not 10 digits)
Approach
  • 1Use String.replaceAll('[^0-9]', '') to strip all non-digit characters.
  • 2String.substring(start, end) extracts a portion of the string (end is exclusive).
  • 3If digits.length() != 10, return the original rawPhone unchanged.
Easy Apex Class

30. Apex Class: SLA Checker — Is Case SLA Breached?

Problem #283 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class SLAChecker with a static method isBreached(String priority, Integer hoursOpen) that returns true when a case has exceeded its SLA time limit.

SLA Thresholds

PriorityMax Hours
Critical2
High4
Medium8
Low24

Requirements

  • Return false for null/blank priority or null hoursOpen
  • Return false for unknown priority values
  • Store thresholds in a Map constant — no if-else chains
Approach
  • 1A static Map constant elegantly replaces if-else chains.
  • 2Use map.get(priority) — if it returns null, the priority is unknown.
  • 3Guard against null inputs before doing any comparison.
Easy Apex Class

31. Apex Class: Name Formatter — Salutation + Full Name

Problem #284 · Salesforce Apex Coding Challenge

Problem Statement

Write an Apex class NameFormatter with two static methods:

Method 1 — fullName

public static String fullName(String salutation, String firstName, String lastName)

Concatenates non-blank parts separated by a single space.
e.g. fullName("Mr.", "John", "Smith")"Mr. John Smith"
e.g. fullName(null, "John", "Smith")"John Smith"

Method 2 — lastFirst

public static String lastFirst(String firstName, String lastName)

Returns "Last, First" format.
e.g. lastFirst("John", "Smith")"Smith, John"
e.g. lastFirst(null, "Smith")"Smith"

Requirements

  • Skip blank/null parts — do not produce leading/trailing spaces
  • Use String.isBlank() for null-safe blank checks
Approach
  • 1Build a List, add only non-blank parts, then String.join(list, ' ').
  • 2String.isBlank() returns true for null, empty, and whitespace-only strings.
  • 3For lastFirst, handle each missing-part edge case separately.
Medium Apex Classes

32. Apex Service Class: Merge Duplicate Contacts by Account

Problem #357 · Salesforce Apex Coding Challenge

Problem Statement

Write a service class ContactMergeService with a static method mergeContactsByAccount(Id accountId) that merges duplicate Contacts under the same Account.

Two Contacts are duplicates when they share the same full name (case-insensitive). The oldest Contact (earliest CreatedDate) is kept as the master record.

Requirements

  • public with sharing class.
  • Throw IllegalArgumentException when accountId is null.
  • Query Contacts ordered by CreatedDate ASC; group by full name (case-insensitive).
  • Call Database.merge(master, List<Id>) with up to 2 IDs per call.
  • System.debug the merged count — visible in the Output tab.
  • Return the total number of merged records.
Approach
  • 1Full name key: String key = ((c.FirstName != null ? c.FirstName : '') + ' ' + c.LastName).trim().toLowerCase();
  • 2Sorting the query oldest-first means the first Contact seen for each name key becomes the master, at index 0.
  • 3Database.merge(master, chunk) accepts a List with up to 2 entries. Use a while loop with idx += chunk.size().
  • 4Service classes should enforce field-level security on their SOQL — check what clause does that.
Medium Apex Classes

33. Apex Service Class: Account Health Score (Bulk-Safe)

Problem #358 · Salesforce Apex Coding Challenge

Problem Statement

Write AccountHealthScoreService with two static methods:

  • calculateHealthScore(Id accountId) — single-account wrapper that System.debugs the score.
  • calculateHealthScores(Set<Id> accountIds) — bulk-safe: exactly 4 SOQL queries total.

Scoring Rules (0–100, clamped)

  • +40 — open Opportunity
  • +30 — Closed Won in last 12 months
  • +20 — LastActivityDate within 90 days
  • −30 — open Case
Approach
  • 1For each signal, run an aggregate SOQL query with GROUP BY AccountId against the relevant object, and collect the AccountIds returned into a Set — that Set membership is your true/false flag for that signal.
  • 2Clamp: score = Math.max(0, Math.min(100, score)); — ensures the result is always in [0, 100].
  • 3The single-account method delegates: Map r = calculateHealthScores(new Set{ accountId }); return r.containsKey(accountId) ? r.get(accountId) : 0;
  • 4Use System.debug to print the score so it shows in the Console/Output tab after running.
Easy Apex Class

34. Calculate Customer Loyalty Points on Closed Won

Problem #455 · Salesforce Apex Coding Challenge

Business Scenario

Marketing wants every customer's Account to accumulate loyalty points whenever one of their Opportunities closes as Closed Won. Points are earned at a rate of 1 point per $100 of the won Amount, stored on a custom field Loyalty_Points__c (Number) on Account.

Requirements

  • Build a reusable service method awardPoints(List<Opportunity> closedWonOpps) that a trigger can call with the Opportunities that just became Closed Won.
  • Aggregate points per Account in memory first — one Opportunity may not be the only one closing for that Account in the same transaction.
  • Query the affected Accounts in a single SOQL call and update all of them in a single DML call.

Best Practices

  • No SOQL or DML inside a loop — collect Account Ids into a Set first, then query and update once outside any loop.
  • WITH SECURITY_ENFORCED and a LIMIT on the query respect the running user's field/object-level security and cap the result size.
  • Wrap the update in try/catch(DmlException) so a partial failure never throws an unhandled exception back to the caller.
Approach
  • 1Guard against null AccountId and null Amount before doing any math.
  • 2Use a Map keyed by AccountId to sum points before touching the database at all.
  • 3Query once with WHERE Id IN :accountIds, then loop the query results (not the opportunities) to build the update list.
Easy Apex Class

35. Determine Customer Discount by Account Type and Revenue

Problem #456 · Salesforce Apex Coding Challenge

Business Scenario

Sales wants a single, reusable place to compute the discount percentage a customer qualifies for, based on their Account.Type (standard picklist values such as Technology Partner and Reseller count as "partner" accounts) and AnnualRevenue.

Requirements

  • Partner accounts (Technology Partner or Reseller) with revenue ≥ 1,000,000 get 20%; ≥ 250,000 get 15%; otherwise 10%.
  • Non-partner accounts with revenue ≥ 1,000,000 get 10%; ≥ 250,000 get 5%; otherwise 0%.
  • Null AnnualRevenue must be treated as 0, never throw an error.

Best Practices

  • Use named constants for the revenue thresholds instead of bare numbers, so tuning the business rule later doesn't require hunting through comparison logic.
  • Check the highest-value tiers first with early returns to keep the logic easy to read and avoid overlapping conditions.
Approach
  • 1Treat a null annualRevenue as 0 with a ternary before any comparison.
  • 2isPartner should be true for both 'Technology Partner' and 'Reseller'.
  • 3Order your checks from the highest discount down to the lowest, using early returns.
Medium Apex Class

36. Validate Product Stock Before Creating an Order

Problem #457 · Salesforce Apex Coding Challenge

Business Scenario

Operations wants Orders blocked before they're created if the requested quantity exceeds available inventory. Orders live on a custom object Order__c with a lookup Product__c to Product2 and a Quantity__c number field. Product2 has a custom Stock_Quantity__c field tracking units on hand.

Requirements

  • Build validateStock(List<Order__c> ordersToInsert) that throws a custom exception when any product's total requested quantity across the batch exceeds its available stock.
  • If the same product appears on multiple orders in the same batch, their quantities must be summed before comparing against stock.
  • Look up current stock for every referenced product in a single query.

Best Practices

  • No SOQL inside a loop — gather all Product Ids first, then query stock once.
  • A custom Exception subclass communicates the specific failure reason clearly to the caller (e.g. a trigger) instead of a generic error.
  • WITH SECURITY_ENFORCED and a LIMIT protect the query and respect field-level security on Stock_Quantity__c.
Approach
  • 1Build a Map that sums Quantity__c per Product__c before querying anything.
  • 2Use Map constructed directly from the SOQL query for O(1) lookups by Id.
  • 3Compare the summed requested quantity against Stock_Quantity__c and throw your custom exception with a clear message.
Medium Apex Class

37. Create Utility Methods to Calculate Shipping Charges by Region

Problem #458 · Salesforce Apex Coding Challenge

Business Scenario

Fulfillment needs a reusable utility to compute shipping charges consistently across the org. Orders live on the custom object Order__c, which carries Ship_Region__c (Text), Ship_Country__c (Text), and Weight_Kg__c (Number).

Requirements

  • calculateShippingCharge(region, country, weightKg) returns a base rate (domestic USA vs. international) plus a per-kilogram charge, plus a surcharge for designated remote regions (Alaska, Hawaii, Remote Islands).
  • Provide a bulk-friendly wrapper calculateShippingChargesForOrders(List<Order__c>) that returns a Map<Id, Decimal> of Order Id to computed charge.
  • Treat missing weight or a null/blank country as safe defaults rather than throwing.

Best Practices

  • All rates and the remote-region list are named constants, so Finance can adjust the pricing model in one place.
  • The bulk method loops purely in memory (no SOQL/DML at all) — it is safe to call from inside a trigger for up to 200 records without any governor-limit risk.
Approach
  • 1Use a Set of remote region names so the "is remote" check is a simple .contains() call.
  • 2country == "USA" (or null, defaulting to domestic) determines the base rate.
  • 3The bulk method should just call the single-record method in a loop — no separate logic to duplicate.
Medium Apex Class

38. Generate Monthly Sales Summaries for Management Dashboards

Problem #459 · Salesforce Apex Coding Challenge

Business Scenario

Management wants a dashboard-ready breakdown of Closed Won Opportunities by calendar month for a given date range — total count and total dollar amount per month, sorted chronologically.

Requirements

  • Build getMonthlySummaries(Date startDate, Date endDate) returning a list of MonthSummary inner-class instances (month label, won count, total amount), sorted oldest month first.
  • Only include Opportunities where StageName = 'Closed Won' and CloseDate falls within the given range (inclusive).
  • Return an empty list for an invalid range (null dates or start after end) instead of throwing.

Best Practices

  • A single SOQL query pulls all relevant Opportunities; monthly grouping is done in memory with a Map, avoiding any per-month query.
  • WITH SECURITY_ENFORCED and a LIMIT keep the query safe and bounded for a reporting-style read.
  • Sorting the month keys (formatted as YYYY-MM) guarantees a stable, chronological order regardless of the order records come back in.
Approach
  • 1Build the month key as opp.CloseDate.year() + "-" + a zero-padded month, e.g. leftPad(2, "0").
  • 2Keep a separate List of month keys in first-seen order, then call .sort() on it before building the final list.
  • 3Guard: if startDate == null || endDate == null || startDate > endDate, return an empty list immediately.
Medium Apex Class

39. Calculate SLA Due Dates While Excluding Weekends

Problem #460 · Salesforce Apex Coding Challenge

Business Scenario

Support's SLA policy grants agents a number of business days (Monday through Friday) to resolve a Case, starting from when it was opened. Weekends must never count toward the SLA clock, or agents effectively lose time they were promised.

Requirements

  • Build calculateDueDate(Datetime startDateTime, Integer businessDays) that advances one calendar day at a time, only counting weekdays, until businessDays weekdays have been added.
  • Saturday and Sunday must be skipped entirely — they never count and the due date itself should never land on one (advancing day-by-day naturally guarantees this).
  • Invalid input (null start, null/zero/negative businessDays) should return the original startDateTime unchanged rather than throwing.

Best Practices

  • Datetime.format('u') returns the ISO day-of-week ('1' Monday through '7' Sunday) without any manual modulo-arithmetic date math, which is easy to get off-by-one.
  • Isolating the weekend check into its own isWeekend helper keeps the day-counting loop readable and makes the rule easy to unit test in isolation.
Approach
  • 1Datetime.format('u') returns a String: '1' for Monday up through '7' for Sunday — compare against '6' and '7' for the weekend check.
  • 2Advance current = current.addDays(1) inside the loop, then only increment your counter when the new day is not a weekend.
  • 3Guard clause first: null startDateTime, null businessDays, or businessDays <= 0 should all just return startDateTime as-is.
Hard Apex Class

40. Create a Reusable Tax Calculation Engine Supporting Multiple Countries

Problem #461 · Salesforce Apex Coding Challenge

Business Scenario

Finance needs a single Apex service that computes tax for an order's taxable amount, but the calculation rules differ per country: some use a flat percentage, others use progressive tiers based on the amount. New countries are added periodically, and the rules should not require rewriting a giant if/else chain each time.

Requirements

  • Define a TaxStrategy interface with one method, calculateTax(Decimal taxableAmount).
  • Implement at least FlatRateTaxStrategy (a single percentage) and TieredTaxStrategy (rate depends on which threshold the amount clears).
  • Expose a single entry point calculateTax(String countryCode, Decimal taxableAmount) that looks up the right strategy for the country and delegates to it, falling back to a default strategy for unrecognized countries.

Best Practices

  • The Strategy pattern (an interface + a Map of country to implementation) lets new tax rules be added by registering a new strategy instance, without touching existing logic — this is the reusability the scenario calls for.
  • Every strategy is null-safe on its input and rounds the result with setScale(2) for currency-correct output.
Approach
  • 1The interface is the key to extensibility — calculateTax(String countryCode, Decimal amount) should never need an if/else per country.
  • 2For TieredTaxStrategy, loop thresholds in ascending order and keep overwriting "rate" whenever amount >= thresholds[i] — the last one that matches wins.
  • 3Build STRATEGY_BY_COUNTRY as a static final Map so adding a country is a one-line change.
Hard Apex Class

41. Apply Dynamic Pricing Rules Based on Customer Segments

Problem #462 · Salesforce Apex Coding Challenge

Business Scenario

Revenue operations wants product pricing to automatically flex based on the purchasing Account's customer segment (Enterprise, Mid-Market, or SMB, tracked on a custom Account field Customer_Segment__c) and a seasonal peak surcharge that applies during November and December.

Requirements

  • Build calculatePrice(Decimal listPrice, String customerSegment, Date effectiveDate).
  • Apply a segment discount first (Enterprise 20%, Mid-Market 10%, SMB 5%, unrecognized segments get no discount), then apply an 8% peak-season surcharge if effectiveDate falls in November or December.
  • Null listPrice must be treated as 0 rather than throwing a null pointer exception.

Best Practices

  • Segment discount lookup is isolated into its own private helper method, keeping calculatePrice focused on the overall pricing sequence (discount, then surcharge) rather than a long nested conditional.
  • All percentages are named constants so Finance-driven rate changes are a one-line edit, not a hunt through arithmetic expressions.
Approach
  • 1Compute the discount first, then apply the surcharge on the already-discounted price — order matters here.
  • 2effectiveDate.month() returns 1-12; check it against a Set{11, 12} for peak months.
  • 3getDiscountForSegment should return 0 (not throw) for any segment string it does not recognize.
Hard Apex Class

42. Calculate Employee Bonuses Using Configurable Business Rules

Problem #463 · Salesforce Apex Coding Challenge

Business Scenario

HR wants a configurable bonus calculation for the custom object Employee__c (fields Salary__c, Performance_Rating__c 1-5, and Years_Of_Service__c). Bonuses combine a performance component and a capped loyalty component, but only employees who meet a minimum performance bar qualify at all.

Requirements

  • Employees rated below 3 receive no bonus at all.
  • Performance bonus: 2% of salary per rating point (e.g. rating 4 → 8%).
  • Loyalty bonus: 0.5% of salary per year of service, capped at 5% regardless of tenure.
  • Total bonus = (performance % + loyalty %) × salary, and provide a bulk wrapper returning a Map<Id, Decimal> for a list of employees.

Best Practices

  • Every rate and threshold (rating cutoff, per-point/per-year percentages, loyalty cap) is a named constant, since HR routinely renegotiates these business rules.
  • The single-employee method is pure and side-effect free, so it can be reused directly by both a bulk wrapper and, e.g., a preview UI — no SOQL or DML lives inside it.
Approach
  • 1Return 0 immediately when rating < MIN_QUALIFYING_RATING — no further calculation needed.
  • 2Cap the loyalty bonus with a simple if check after computing years * LOYALTY_BONUS_PCT_PER_YEAR.
  • 3The bulk wrapper should not duplicate any math — just call calculateBonus once per employee.
Hard Apex Class

43. Build a Reusable Address Validation Service Class

Problem #464 · Salesforce Apex Coding Challenge

Business Scenario

Multiple objects (Account billing address, Contact mailing address, a future Order shipping address) all need the same address-quality checks before being saved. Build one reusable service so the validation rules live in exactly one place.

Requirements

  • validateAddress(street, city, state, postalCode, country) returns a ValidationResult inner class holding isValid and a list of human-readable errors.
  • Street and city are always required. For US addresses (country is USA/US/blank), the state must be a valid 2-letter code and the postal code must match US ZIP format (12345 or 12345-6789).
  • Non-US addresses only require a non-blank postal code.
  • Provide a bulk wrapper validateAccounts(List<Account>) returning a Map<Id, ValidationResult> for the standard Account billing address fields.

Best Practices

  • Accepting individual field values (rather than a specific SObject type) is what makes validateAddress reusable across Account, Contact, and any future object — the bulk wrapper adapts a specific SObject's fields to that generic signature.
  • Collecting every failure into a list (instead of stopping at the first error) gives the caller the full picture in one pass, which is friendlier for a UI or bulk import.
Approach
  • 1ValidationResult.addError should both flip isValid to false and append to the errors list — keep it in one method so callers never forget one half.
  • 2Pattern.compile('^\\d{5}(-\\d{4})?$') matches both 5-digit and ZIP+4 formats — remember Apex string literals need \\\\ for a single regex backslash.
  • 3Treat a blank/null country as US — most Salesforce orgs default new records to the org-level default country.
Expert Apex Class

44. Calculate Subscription Renewal Amounts With Discounts

Problem #465 · Salesforce Apex Coding Challenge

Business Scenario

Customer Success wants renewal quotes computed automatically for the custom object Subscription__c (Base_Amount__c, Consecutive_Renewals__c, Auto_Renew__c checkbox, Contract_Term_Months__c). Multiple discounts can stack: loyalty (more consecutive renewals = bigger discount, capped), auto-renew enrollment, and committing to an annual-or-longer term.

Requirements

  • Loyalty discount: 2% per consecutive renewal, capped at 10% total.
  • Auto-renew discount: flat additional 5% if Auto_Renew__c is true.
  • Annual term discount: flat additional 8% if Contract_Term_Months__c >= 12.
  • All applicable discounts stack additively against the base amount, then apply once. Provide both a single-subscription method and a bulk Map<Id, Decimal> wrapper.

Best Practices

  • Math.min() enforces the loyalty discount cap cleanly instead of a manual if-check, and keeps the cap logic in one expression.
  • Discounts are summed into a single totalDiscountPct and applied once at the end — avoids compounding rounding errors from applying each discount sequentially to an already-discounted amount.
  • All rates and caps are named constants for easy business-rule tuning.
Approach
  • 1Math.min(renewals * LOYALTY_DISCOUNT_PCT_PER_RENEWAL, MAX_LOYALTY_DISCOUNT_PCT) caps the loyalty discount in one line.
  • 2Sum every applicable discount percentage into a single total before doing the final multiplication — do not apply discounts one after another to a shrinking amount.
  • 3autoRenew == true treats a null Boolean safely as false without throwing.
Expert Apex Class

45. Implement Dynamic Product Recommendation Logic

Problem #466 · Salesforce Apex Coding Challenge

Business Scenario

Sales wants a "customers who bought this also bought that" recommendation feature for an Account's product page: given an Account, suggest up to 5 Products that similar customers purchased but this Account has not, ranked by how often those similar customers bought each candidate product.

Requirements

  • Build getRecommendations(Id accountId) returning up to 5 Product2 records.
  • Step 1: find products this Account has already purchased (Closed Won OpportunityLineItems).
  • Step 2: find other Accounts ("similar accounts") that purchased at least one of the same products.
  • Step 3: among those similar accounts' other purchases (excluding products this Account already owns), rank candidate products by purchase frequency and return the top 5.

Best Practices

  • Each stage of the algorithm is a single aggregate SOQL query — no per-record queries inside a loop, even though the logic spans three distinct steps.
  • WITH SECURITY_ENFORCED and a LIMIT on every query keeps each stage safe and bounded regardless of how large the customer base grows.
  • Early-return guard clauses after each stage (no purchases, no similar accounts, no co-occurring products) avoid unnecessary downstream queries when there is nothing left to compute.
Approach
  • 1GROUP BY PricebookEntry.Product2Id on OpportunityLineItem gives distinct purchased product Ids for an Account.
  • 2Chain the three aggregate queries: purchased products → similar accounts → co-occurring products, returning early whenever a Set comes back empty.
  • 3ORDER BY COUNT(Id) DESC with LIMIT :MAX_RECOMMENDATIONS on the final aggregate ranks candidates by frequency in one query.
Expert Apex Class

46. Generate Financial Reports Grouped by Fiscal Quarter

Problem #467 · Salesforce Apex Coding Challenge

Business Scenario

Finance wants a Closed Won revenue report broken down by fiscal quarter, where the fiscal year does not necessarily start in January — the org's fiscal year start month is a configurable input (e.g. 2 means the fiscal year runs February through January).

Requirements

  • Build getQuarterlyReport(Integer year, Integer fiscalYearStartMonth) returning exactly four QuarterSummary entries (Q1-Q4) — total revenue and deal count of Closed Won Opportunities closing in that fiscal quarter.
  • Compute the fiscal year's date boundaries from fiscalYearStartMonth, then bucket each Opportunity's CloseDate into one of the four 3-month quarters relative to that start.
  • Default fiscalYearStartMonth to January (1) when not supplied.

Best Practices

  • A single SOQL query pulls every Closed Won Opportunity in the fiscal year; quarter bucketing is pure in-memory arithmetic, avoiding four separate per-quarter queries.
  • WITH SECURITY_ENFORCED and a LIMIT keep this reporting-style query safe and bounded.
  • Pre-seeding summaryByQuarter with all four quarters before the loop guarantees the report always returns Q1-Q4 even for quarters with zero deals.
Approach
  • 1fiscalStart.addMonths(12).addDays(-1) gives the last day of the fiscal year starting at fiscalStart.
  • 2quarterIndex = monthsSinceFiscalStart / 3 (integer division) buckets any month offset 0-11 into quarter 0-3.
  • 3Pre-populate summaryByQuarter with all four QuarterSummary objects before the query loop so quarters with zero deals still appear in the result.
Expert Apex Class

47. Build a Generic Notification Service for Multiple Objects

Problem #468 · Salesforce Apex Coding Challenge

Business Scenario

Multiple triggers across the org (Case escalations, Opportunity stage changes, Contract renewals) all need to notify a user, but some notifications should be emails and others should be follow-up Tasks. Build one generic notification service so trigger code never has to know the delivery mechanism.

Requirements

  • Define a NotificationChannel interface with send(String recipientId, String subject, String body).
  • Implement EmailChannel (using Messaging.SingleEmailMessage targeted at a User/Contact Id) and TaskChannel (creating a Task owned by the recipient).
  • Expose notify(NotificationChannel channel, String recipientId, String subject, String body) as the generic entry point, plus a bulk notifyMultiple(..., List<String> recipientIds, ...) for sending the same message to many recipients.

Best Practices

  • The NotificationChannel interface decouples "who is calling" from "how the message is delivered" — a new channel (e.g. Chatter post, SMS) can be added without touching any existing trigger.
  • Each channel implementation wraps its own DML/callout in try/catch (EmailException for email, DmlException for the Task insert) so a delivery failure never bubbles up as an unhandled exception to the caller.
Approach
  • 1setTargetObjectId(recipientId) plus setSaveAsActivity(false) is the standard pattern for emailing a User/Contact by Id.
  • 2notify() and notifyMultiple() should never know whether channel is an EmailChannel or TaskChannel — that is the whole point of the interface.
  • 3Each channel handles its own failure mode: EmailException for Messaging.sendEmail, DmlException for the Task insert.

Practice All 47 Problems Free

Write and run real Apex code right in your browser — instant pass/fail feedback, best-practice linting, and governor limit monitoring. No Salesforce org needed.

Create Free Account → Explore All Problems
More Practice Topics
About ApexArena

ApexArena is a free, browser-based Salesforce Apex coding practice platform covering every major topic tested on the Salesforce Platform Developer I (PD1) and Platform Developer II (PD2) certification exams. All problems run directly in your browser with instant pass/fail feedback, best-practice linting (SOQL in loops, DML in loops, empty catch blocks), and governor limit monitoring — no Salesforce Developer Edition org required.

Related tutorials: Apex Triggers · SOQL · Batch Apex · Interview Q&A · Governor Limits