top of page
Search

Error Handling like a master

  • Writer: Mark Kendall
    Mark Kendall
  • 5 days ago
  • 3 min read

To do really good error checking — whether you’re using Java, Spring Boot, or any modern programming language — you need a comprehensive, layered approach.


It’s not just about try-catch or logging. The best systems combine validation, structured exception handling, centralized error processing, observability, and recovery mechanisms.



THE BEST WAY TO DO ERROR CHECKING (GENERALIZED + SPRING BOOT FOCUSED)



🔹 1. Prevent Errors Early (Validation First)


Avoid problems by validating inputs and states before they cause errors.

• In Spring Boot:

• Use @Valid and Bean Validation (javax.validation.constraints) in your DTOs and method parameters.

• Example:


public class UserDTO {

    @NotNull

    @Email

    private String email;

}


@PostMapping("/register")

public ResponseEntity<?> register(@Valid @RequestBody UserDTO dto) {

    ...

}



• At service or domain level, validate invariants before doing anything critical.



🔹 2. Throw Specific Exceptions


Use meaningful, custom exceptions to represent real problems in your domain.

• Avoid generic exceptions like RuntimeException unless you’re rethrowing or wrapping.

• Example:


if (!userExists) {

    throw new UserNotFoundException("User not found for id " + id);

}



• Bonus: include enough context in the exception (e.g., user ID, payload) but not PII.



🔹 3. Use try-catch Where It Matters


Don’t catch everything. Only catch exceptions you can handle.

• Example:


try {

    paymentService.chargeCard(cardDetails);

} catch (CardDeclinedException e) {

    log.warn("Card was declined for user {}", userId);

    return ResponseEntity.status(402).body("Card declined");

} catch (Exception e) {

    log.error("Unexpected error during payment", e);

    throw new PaymentProcessingException("Unknown payment error");

}




Tip: Let Spring handle what you can’t — don’t catch just to suppress or log silently.



🔹 4. Centralize Error Handling with @ControllerAdvice


Catch everything once, centrally, and return appropriate HTTP responses or fallback behavior.

• Example:


@RestControllerAdvice

public class ErrorHandler {


    @ExceptionHandler(UserNotFoundException.class)

    public ResponseEntity<?> handleNotFound(UserNotFoundException ex) {

        return ResponseEntity.status(HttpStatus.NOT_FOUND)

                             .body(Map.of("error", ex.getMessage()));

    }


    @ExceptionHandler(Exception.class)

    public ResponseEntity<?> handleGeneric(Exception ex) {

        log.error("Unhandled error", ex);

        return ResponseEntity.status(500).body("Internal server error");

    }

}





🔹 5. Log Strategically and Structurally


Good logging ≠ lots of logging. Log with structure and purpose.

• Use:

INFO for major actions

WARN for recoverable problems

ERROR for things that require attention

• Include:

• Correlation/request IDs

• Contextual info (not sensitive data)

• Stack traces only when useful

• Example:


log.warn("Invalid payment attempt by user {} with card ending in {}", userId, lastFour);





🔹 6. Observability: Metrics + Tracing


Hook into monitoring systems for early warning and traceability.

• Tools:

• Spring Boot Actuator

• Micrometer + Prometheus/Grafana

• OpenTelemetry (tracing exceptions across services)



🔹 7. Resilience & Graceful Degradation

• Use libraries like:

• Resilience4j for retries, circuit breakers, rate limiting

• Spring Retry for automatic retry on transient failures


@Retryable(value = RemoteServiceException.class, maxAttempts = 3)

public void callRemoteService() { ... }




TL;DR: Cheat Sheet for Robust Error Handling


Layer Tool/Approach Purpose

Input validation @Valid, custom checks Prevent garbage in

Specific exceptions Custom extends RuntimeException Clear semantics and traceability

Smart try/catch Catch what you can recover from Avoid unnecessary noise

Central handling @ControllerAdvice DRY and consistent error responses

Structured logging SLF4J + MDC Troubleshoot and correlate

Observability Metrics + Tracing Detect problems before users do

Resilience Retry, CircuitBreaker Keep system running despite failures




If you’re doing all the above, you’re not just doing “error checking” — you’re engineering resilience, clarity, and maintainability.


Want an example repo or code structure for all this bundled together?

 
 
 

Recent Posts

See All
Try resilience4j

📦 Recommended Downloadable Project • resilience4j-spring-boot-demo: A curated Spring Boot demo showcasing CircuitBreaker, Retry,...

 
 
 

Comments


Post: Blog2_Post

Subscribe Form

Thanks for submitting!

©2020 by LearnTeachMaster DevOps. Proudly created with Wix.com

bottom of page