Spring Boot Code Review Best Practices | Sinhala Guide | Code Quality

Spring Boot Code Review Best Practices | Sinhala Guide | Code Quality

කොහොමද යාලුවනේ! අද අපි කතා කරන්න යන්නේ ඔයාලගේ Spring Boot project එකේ code quality එක වැඩි කරගන්න පුළුවන් ගොඩක් වැදගත් දෙයක් ගැන. ඒ තමයි Code Reviews! Software development වලදී, හොඳ code review එකක් කියන්නේ bugs අඩු කරන්න, maintainability වැඩි කරන්න, සහ මුළු team එකේම දැනුම වැඩි කරන්න පුළුවන් මාරම powerful tool එකක්.

අපි හැමෝටම සමහර වෙලාවට code ලියද්දි පොඩි පොඩි වැරදි වෙන්න පුළුවන්, නැත්නම් හොඳම practice එක මොකක්ද කියලා මගහැරෙන්න පුළුවන්. අන්න ඒ වෙලාවට තමයි අනිත් developer කෙනෙක්ගේ ඇසින් (fresh pair of eyes) අපේ code එක review කරන එක ගොඩක් වටින්නේ. විශේෂයෙන්ම Spring Boot වගේ framework එකක් එක්ක වැඩ කරද්දි, design patterns, best practices, සහ performance optimization ගැන හොඳ අවබෝධයක් තියෙන්න ඕනේ.

මේ tutorial එකෙන් අපි බලමු:

  • Code Review එකක් කියන්නේ මොකක්ද? සහ ඇයි ඒක අපිට වැදගත් වෙන්නේ?
  • Spring Boot Project එකක Code Review කරද්දි බලන්න ඕනේ මොනවද? (Key areas to focus on)
  • Practical Code Review Simulation එකක් (ප්‍රායෝගික Code Review අභ්‍යාසයක්) හරහා, common issues කිහිපයක් හඳුනගෙන ඒවා improve කරගන්න හැටි.

ඉතින්, ඔයාලගේ Spring Boot skills ටික තවත් දියුණු කරගන්න ලෑස්ති වෙලා ඉන්නවා නම්, අපි මේ journey එක පටන් ගමු!

Code Review කියන්නේ මොකක්ද?

සරලවම කිව්වොත්, Code Review එකක් කියන්නේ computer source code එකක් systemically examine කරන එකට. මේක සාමාන්‍යයෙන් කරන්නේ code එක ලියපු කෙනා හැර වෙනත් කෙනෙක්. මේකේ ප්‍රධානම අරමුණ වෙන්නේ code එකේ තියෙන potential bugs, design flaws, performance issues, security vulnerabilities වගේ දේවල් කලින්ම හඳුනගෙන ඒවා fix කරගන්න එක.

බොහෝ වෙලාවට, code review එකක් වෙන්නේ Pull Request (PR) එකක් හෝ Merge Request (MR) එකක් GitLab, GitHub, Bitbucket වගේ platform එකකට submit කරාමයි. Team එකේ අනිත් අය ඒ code එක බලලා comments දාලා, suggestions දීලා, අවසානයේදී approve කරනවා.

ඇයි Code Reviews අපිට වැදගත්?

Code reviews වලට ගොඩක් වාසි තියෙනවා, විශේෂයෙන්ම team environment එකක වැඩ කරද්දි.

  1. Bugs අඩු වෙනවා (Reduced Bugs): දෙන්නෙක්ගේ ඇස් වලින් බලනකොට, එක්කෙනෙක්ට මගහැරුණු bug එකක් තව කෙනෙක්ට හොයාගන්න ලේසියි. මේකෙන් production එකට යන bugs ගාන ගොඩක් අඩු වෙනවා.
  2. Code Quality වැඩි වෙනවා (Improved Code Quality): Code reviews වලින් consistent coding styles, best practices, design patterns වගේ දේවල් enforce කරන්න පුළුවන්. ඒ වගේම maintainability, readability, scalability වගේ දේවල් ගැනත් අවධානය යොමු කරන්න පුළුවන්.
  3. Knowledge Sharing (දැනුම බෙදාගැනීම): Team එකේ හැමෝටම අලුතින් add කරන code base එක ගැන දැනගන්න මේක හොඳ අවස්ථාවක්. Junior developers ලාට senior developers ලගෙන් ඉගෙන ගන්න පුළුවන්, ඒ වගේම senior developers ලාටත් අලුත් perspective එකකින් දේවල් බලන්න පුළුවන්.
  4. Team Cohesion (කණ්ඩායම් එකමුතුව): එකට වැඩ කරලා, constructive feedback දීලා, හොඳ code එකක් හදන එක team එකේ බැඳීම තවත් වැඩි කරනවා.
  5. Security වැඩි වෙනවා (Enhanced Security): Security best practices follow කරලා නැති තැන්, potential vulnerabilities කලින්ම හඳුනාගෙන ඒවා fix කරගන්න code reviews උපකාරී වෙනවා.

Spring Boot Project එකකට Code Review කරද්දී බලන්න ඕනේ මොනවද?

Spring Boot project එකක code review කරද්දි අපි බලන්න ඕනේ areas ගොඩක් තියෙනවා. මේවා පොදු software development principles වගේම Spring Boot specific practices වලටත් අදාළයි. අපි බලමු ප්‍රධාන areas කිහිපයක්.

1. Architecture සහ Design Patterns

  • Layering: Code එක Controller, Service, Repository layers වලට හරියට බෙදලා තියෙනවද? Business logic Controller එකේ දාලා තියෙනවද? එහෙම නම්, ඒක Service layer එකට move කරන්න ඕනේ.
  • Dependency Injection: Spring Framework එකේ core concept එකක් තමයි Dependency Injection (DI). DI හරියට පාවිච්චි කරලා තියෙනවද? Constructor Injection තමයි @Autowired Field Injection වලට වඩා recommend කරන්නේ.
  • SOLID Principles: Single Responsibility Principle, Open/Closed Principle, Liskov Substitution Principle, Interface Segregation Principle, Dependency Inversion Principle වගේ SOLID principles maintain කරලා තියෙනවද කියලා බලන්න.
  • Microservices (if applicable): Microservices architecture එකක් පාවිච්චි කරනවා නම්, service boundaries හරියට තියෙනවද? Communication patterns (REST, Kafka) හරියට implement කරලා තියෙනවද?

2. Performance සහ Scalability

  • Database Queries: N+1 query problem වගේ issues තියෙනවද? Lazy/Eager loading හරියට configure කරලා තියෙනවද? Indexes use කරලා තියෙනවද?
  • Async Operations: Long-running tasks, external API calls වගේ දේවල් සඳහා @Async වගේ Spring features හරියට පාවිච්චි කරලා තියෙනවද? Thread pool configurations ගැනත් බලන්න.
  • Caching: @Cacheable, @CachePut, @CacheEvict වගේ annotations හරහා Caching implement කරලා තියෙනවද?
  • Resource Management: Database connections, file handles වගේ resources හරියට close කරනවද?

3. Security

  • Input Validation: User input sanitize කරලා, validate කරලා තියෙනවද? SQL Injection, XSS (Cross-Site Scripting) වගේ attacks වලින් ආරක්ෂා වෙන්න measures අරන් තියෙනවද? @Valid, @Validated වගේ annotations සහ JSR 303 Bean Validation APIs පාවිච්චි කරනවද?
  • Authentication/Authorization: Spring Security හරියට configure කරලා තියෙනවද? Access control (@PreAuthorize, @PostAuthorize) හරියට implement කරලා තියෙනවද? Default passwords, insecure API keys වගේ දේවල් තියෙනවද?
  • Sensitive Data Handling: Passwords, API keys, Personally Identifiable Information (PII) වගේ sensitive data logs වල publish වෙන්නේ නැති වෙන්න වග බලාගන්න. Encryption භාවිතය.
  • CORS Configuration: Cross-Origin Resource Sharing (CORS) හරියට configure කරලා තියෙනවද?

4. Error Handling සහ Logging

  • Global Exception Handling: @ControllerAdvice සහ @ExceptionHandler හරහා centralized exception handling implement කරලා තියෙනවද? User friendly error messages දෙනවද?
  • Logging: Application එකේ flow එක, වැදගත් events, errors වගේ දේවල් identify කරන්න පුළුවන් විදියට meaningful log messages add කරලා තියෙනවද? Logger API (SLF4J, Logback) හරියට පාවිච්චි කරනවද? Production environment එකක sensitive data log වෙන්නේ නැති වෙන්න වග බලාගන්න.
  • Appropriate Exception Types: නිවැරදි exception types පාවිච්චි කරනවද? Generic exceptions (e.g., Exception) අල්ලාගෙන (catching) ඒවා handle කරනවද?

5. Code Readability සහ Maintainability

  • Naming Conventions: Variables, methods, classes වලට meaningful, consistent names පාවිච්චි කරනවද? (e.g., camelCase for variables/methods, PascalCase for classes).
  • Code Duplication (DRY Principle): එකම code block එක පාරක් තියෙනවද? Common functionality methods වලට Extract කරලා තියෙනවද?
  • Comments: Code එකේ complex parts වලට විතරක් clear, concise comments දාලා තියෙනවද? Over-commenting වලින් code එක cluttered වෙලා තියෙනවද? Good code is self-documenting.
  • Formatting: Code formatter එකක් (e.g., Prettier, Checkstyle, spotless-maven-plugin) පාවිච්චි කරලා code එක consistently format කරලා තියෙනවද?
  • Unit/Integration Tests: Business logic cover කරන unit tests, integration tests ලියලා තියෙනවද? Test coverage එක ගැනත් බලන්න.

ප්‍රායෝගික Code Review අභ්‍යාසය (Practical Code Review Simulation)

හරි, දැන් අපි theoretical දේවල් ගැන කතා කරානේ. අපි දැන් පොඩි Spring Boot code snippet එකක් අරගෙන, ඒක review කරලා, improve කරගමු.

Scenario: User details manage කරන්න හදපු සරල REST API එකක්. මෙන්න ඒකේ code එකේ කොටසක්:

ආරම්භක Code එක (Problematic Code)

// User.java (Entity class, assume it has id, username, email fields and standard getters/setters)
// (omitted for brevity)

// UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
}

// UserController.java
@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserRepository userRepository; // Field Injection - Not recommended

    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        // Issue 1: Directly calling .get() on Optional without checking presence
        // Issue 2: No proper error handling for user not found
        return userRepository.findById(id).get();
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        // Issue 3: No input validation for the user object
        // Issue 4: Business logic (even simple save) directly in Controller
        return userRepository.save(user);
    }
}

Code Review Comments සහ Problems

  1. @Autowired Field Injection: userRepository එකට @Autowired Field Injection එකක් පාවිච්චි කරලා තියෙනවා. මේක Spring Boot වලදී recommend කරන්නේ නෑ. Constructor Injection තමයි හොඳම practice එක. ඒකෙන් immutable dependencies හදන්න පුළුවන්, testability වැඩි වෙනවා, සහ dependencies මොනවද කියලා code එක කියවද්දිම පැහැදිලියි.
  2. Optional.get() එක කෙලින්ම පාවිච්චි කිරීම: userRepository.findById(id).get() කියලා කෙලින්ම .get() එක පාවිච්චි කරලා තියෙනවා. findById() එක Optional එකක් return කරන්නේ user කෙනෙක් ඉන්නවද නැද්ද කියලා check කරන්න. User කෙනෙක් නැති වුනොත් NoSuchElementException එකක් throw වෙන්න පුළුවන්. ඒක හරියට handle කරලා නෑ.
  3. Error Handling නොමැති වීම: User කෙනෙක් නැත්නම් 404 Not Found error එකක් send කරනවා වෙනුවට application එක crash වෙන්න පුළුවන්. Global Exception Handling වගේ එකක් මෙතනට වටිනවා.
  4. Input Validation නොමැති වීම: createUser method එකේ @RequestBody User user object එකට කිසිම validation එකක් දාලා නෑ. User name එක හිස්ද, email format එක හරියට තියෙනවද වගේ දේවල් check කරන්නේ නෑ. මේක security risk එකක්.
  5. Business Logic Controller එකේ තැබීම: userRepository.save(user) වගේ direct database operation එකක් Controller එකේ තියෙනවා. Controller එකක ප්‍රධාන කාර්යය වෙන්නේ incoming requests handle කරලා, Service layer එකට delegate කරන එකයි. Business logic, data transformation වගේ දේවල් Service layer එකේ තියෙන්න ඕනේ.

Improved Code එක (හොඳම Practice අනුව)

මෙන්න අපි කලින් බලපු issues ටික fix කරලා improved version එක:

// User.java (Entity class - same as before)
// (omitted for brevity)

// UserRepository.java (same as before)
public interface UserRepository extends JpaRepository<User, Long> {
}

// UserNotFoundException.java (Custom Exception for better error handling)
@ResponseStatus(HttpStatus.NOT_FOUND) // This ensures a 404 status code when this exception is thrown
public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(String message) {
        super(message);
    }
}

// UserService.java (Introducing a Service Layer for business logic)
@Service
public class UserService {

    private static final Logger logger = LoggerFactory.getLogger(UserService.class); // For logging
    private final UserRepository userRepository;

    // Constructor Injection (Recommended way to inject dependencies)
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUserById(Long id) {
        logger.info("Attempting to fetch user with ID: {}", id);
        return userRepository.findById(id)
                             .orElseThrow(() -> new UserNotFoundException("User not found with ID: " + id));
    }

    public User createUser(User user) {
        logger.info("Creating new user with username: {}", user.getUsername());
        // Here you can add more business logic before saving, e.g., password encryption, unique username check
        return userRepository.save(user);
    }
}

// UserController.java (Now cleaner and focused on request handling)
@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService; // Dependency on Service layer

    // Constructor Injection
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        return ResponseEntity.ok(user);
    }

    @PostMapping
    public ResponseEntity<User> createUser(@Valid @RequestBody User user) { // @Valid for input validation
        User createdUser = userService.createUser(user);
        return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
    }
}

// GlobalExceptionHandler.java (Optional but highly recommended for centralized error handling)
@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFoundException(UserNotFoundException ex) {
        logger.error("User not found exception: {}", ex.getMessage());
        ErrorResponse error = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage());
        return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
    }

    // You can add more @ExceptionHandler methods for other types of exceptions
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidationExceptions(MethodArgumentNotValidException ex) {
        logger.error("Validation exception: {}", ex.getMessage());
        List<String> errors = ex.getBindingResult()
                                .getFieldErrors()
                                .stream()
                                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                                .collect(Collectors.toList());
        ErrorResponse error = new ErrorResponse(HttpStatus.BAD_REQUEST.value(), "Validation Failed", errors);
        return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
    }

    // Generic exception handler
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
        logger.error("An unexpected error occurred: ", ex);
        ErrorResponse error = new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "An unexpected error occurred.");
        return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

// ErrorResponse.java (A simple class to structure error responses)
public class ErrorResponse {
    private int status;
    private String message;
    private List<String> details;

    public ErrorResponse(int status, String message) {
        this.status = status;
        this.message = message;
    }

    public ErrorResponse(int status, String message, List<String> details) {
        this.status = status;
        this.message = message;
        this.details = details;
    }

    // Getters and setters (omitted for brevity)
}

Improved Code එකේදී කරපු දේවල්:

  • Constructor Injection: @Autowired Field Injection වෙනුවට Constructor Injection පාවිච්චි කළා.
  • Service Layer: UserService එකක් හදලා, business logic එක එතනට දැම්මා. Controller එක දැන් request handling වලට විතරයි focus කරන්නේ.
  • Proper Error Handling: UserNotFoundException කියන custom exception එකක් හදලා, Optional.orElseThrow() පාවිච්චි කරලා user කෙනෙක් නැත්නම් 404 error එකක් return වෙන විදියට හැදුවා.
  • Global Exception Handler: @ControllerAdvice එකක් පාවිච්චි කරලා centralized error handling implement කළා. මේකෙන් application එකේ හැම තැනකම එකම විදියට errors handle කරන්න පුළුවන්.
  • Input Validation: createUser method එකේ @Valid annotation එක දාලා Bean Validation APIs පාවිච්චි කළා. මේකට User entity එකට @NotNull, @Email වගේ validation annotations දාන්න ඕනේ. (ඒ code එක මෙතන දැම්මේ නෑ, නමුත් ඒක අවශ්‍යයි).
  • Logging: UserService එකට logger එකක් add කරලා, වැදගත් operations log කරන විදියට හැදුවා.
  • ResponseEntity: HTTP status codes හරියට control කරන්න ResponseEntity පාවිච්චි කළා.

අවසානය (Conclusion)

ඉතින් යාලුවනේ, ඔයාලට දැන් තේරෙනවා ඇති Code Reviews කියන්නේ software development process එකේ කොච්චර වැදගත් කොටසක්ද කියලා. ඒකෙන් code quality එක වැඩි වෙනවා වගේම, team එකේ දැනුම බෙදාගෙන, අන්තිමට හොඳ, maintainable, scalable product එකක් හදන්න උදව් වෙනවා.

අපි මේ guide එකෙන් Spring Boot project එකකට code review එකක් කරද්දි බලන්න ඕනේ ප්‍රධාන areas මොනවද කියලා කතා කරා. Architecture, performance, security, error handling, සහ readability වගේ හැම පැත්තකින්ම code එක analyze කරන එක වැදගත්.

අපේ practical example එකෙන් ඔයාලට පේන්න ඇති, පොඩි වෙනස්කම් ටිකකින් වුණත් code එකේ robustness එකයි, maintainability එකයි කොච්චර වැඩි කරන්න පුළුවන්ද කියලා. මේවා තමයි හොඳ developer කෙනෙක් විදියට ඔයාලට අත්‍යවශ්‍ය skills.

දැන් ඔයාලට කරන්න තියෙන්නේ මේ tips ඔයාලගේ project වලට apply කරන එක තමයි. ඊලඟ පාර ඔයාලගේ team එකේ කවුරුහරි Pull Request එකක් දැම්මොත්, මේ points ටික මතක තියාගෙන review කරන්න. ඒ වගේම ඔයාලගේ code එක ලියද්දිත් මේ ගැන හිතලා ලියන්න පුළුවන් නම්, code quality එක අනිවාර්යයෙන්ම වැඩි වෙනවා.

ඔයාලගේ අදහස්, මේ ගැන තියෙන ප්‍රශ්න පහලින් Comment කරන්න. ඔයාලගේ අත්දැකීම් මොනවද? Spring Boot Code Review එකකදී ඔයාලට හම්බවෙලා තියෙන funniest/trickiest bug එක මොකක්ද? අපි ඒ ගැන කතා කරමු!