Quản Lý Technical Debt trong Java

1. Khái Niệm Technical Debt

1.1 Định Nghĩa

  • Nợ kỹ thuật là gì?
  • Tại sao nó phát sinh?
  • Ảnh hưởng đến dự án
  • Chi phí của technical debt
  • Lợi ích ngắn hạn vs dài hạn

1.2 Các Loại Technical Debt

  • Code Debt
  • Design Debt
  • Architecture Debt
  • Test Debt
  • Documentation Debt
  • Infrastructure Debt
  • People Debt

2. Nhận Diện Technical Debt

2.1 Code Smells

// 1. Duplicate Code
public class OrderService {
    public void processOrder(Order order) {
        // Duplicate validation logic
        if (order == null || order.getItems() == null) {
            throw new ValidationException("Invalid order");
        }
        // Process order
    }

    public void updateOrder(Order order) {
        // Same validation logic duplicated
        if (order == null || order.getItems() == null) {
            throw new ValidationException("Invalid order");
        }
        // Update order
    }
}

// 2. Long Method
public class ReportGenerator {
    public void generateReport() {
        // 200+ lines of code doing multiple things
        // - Load data
        // - Process data
        // - Format data
        // - Generate PDF
        // - Send email
    }
}

// 3. God Class
public class SystemManager {
    // Too many responsibilities
    public void handleOrders() {}
    public void manageInventory() {}
    public void processPayments() {}
    public void generateReports() {}
    public void manageUsers() {}
}

2.2 Design Issues

// 1. Tight Coupling
public class OrderProcessor {
    private final PaymentService paymentService;
    private final InventoryService inventoryService;
    private final ShippingService shippingService;

    public void process(Order order) {
        // Direct dependencies make changes difficult
        paymentService.process(order);
        inventoryService.update(order);
        shippingService.ship(order);
    }
}

// 2. Poor Abstraction
public class DataAccess {
    public void saveToDatabase(Object data) {
        // Direct SQL queries
        String sql = "INSERT INTO " + data.getClass().getSimpleName() +
                    " VALUES (" + buildValues(data) + ")";
        // Execute SQL
    }
}

3. Đo Lường Technical Debt

3.1 Metrics

public class CodeMetrics {
    // 1. Cyclomatic Complexity
    public int calculateRisk(Order order) {
        if (order == null) return -1;
        if (order.getItems() == null) return -2;
        if (order.getCustomer() == null) return -3;
        if (order.getPayment() == null) return -4;
        // More conditions...
        return 0;
    }

    // 2. Method Length
    public void processOrder(Order order) {
        // 100+ lines of code
    }

    // 3. Class Coupling
    public class OrderService {
        private CustomerService customerService;
        private InventoryService inventoryService;
        private PaymentService paymentService;
        private ShippingService shippingService;
        private NotificationService notificationService;
        // More dependencies...
    }
}

3.2 Tools

<!-- pom.xml -->
<plugin>
    <groupId>org.sonarsource.scanner.maven</groupId>
    <artifactId>sonar-maven-plugin</artifactId>
    <version>${sonar.version}</version>
    <configuration>
        <properties>
            <!-- Technical Debt Configuration -->
            <sonar.technicalDebt.ratingGrid>
                0.1,0.2,0.5,1
            </sonar.technicalDebt.ratingGrid>
            <sonar.technicalDebt.developmentCost>
                30
            </sonar.technicalDebt.developmentCost>
        </properties>
    </configuration>
</plugin>

4. Giải Quyết Technical Debt

4.1 Refactoring Strategies

// Before: Duplicate Validation
public class OrderService {
    public void processOrder(Order order) {
        if (order == null || order.getItems() == null) {
            throw new ValidationException("Invalid order");
        }
        // Process order
    }

    public void updateOrder(Order order) {
        if (order == null || order.getItems() == null) {
            throw new ValidationException("Invalid order");
        }
        // Update order
    }
}

// After: Extracted Validation
public class OrderService {
    private final OrderValidator validator;

    public void processOrder(Order order) {
        validator.validate(order);
        // Process order
    }

    public void updateOrder(Order order) {
        validator.validate(order);
        // Update order
    }
}

public class OrderValidator {
    public void validate(Order order) {
        if (order == null || order.getItems() == null) {
            throw new ValidationException("Invalid order");
        }
    }
}

4.2 Architecture Improvements

// Before: Monolithic Design
public class OrderProcessor {
    public void process(Order order) {
        // Everything in one place
        validateOrder(order);
        calculateTotal(order);
        processPayment(order);
        updateInventory(order);
        generateInvoice(order);
        sendNotification(order);
    }
}

// After: Event-Driven Design
public class OrderProcessor {
    private final EventPublisher eventPublisher;

    public void process(Order order) {
        // Publish events for different aspects
        eventPublisher.publish(new OrderCreatedEvent(order));
    }
}

@Service
public class PaymentHandler {
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        // Handle payment processing
    }
}

@Service
public class InventoryHandler {
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        // Handle inventory update
    }
}

5. Phòng Ngừa Technical Debt

5.1 Code Standards

// 1. Naming Conventions
public class CustomerOrderProcessor {
    private static final int MAX_RETRY_ATTEMPTS = 3;
    private static final String ERROR_MESSAGE_TEMPLATE = 
        "Failed to process order: %s";

    public OrderResult processOrder(
            CustomerOrder order, 
            ProcessingOptions options) {
        // Implementation
    }
}

// 2. Documentation
/**
 * Processes customer orders with validation and notification.
 * 
 * @param order The order to process
 * @param options Processing options and configurations
 * @return OrderResult containing status and details
 * @throws ValidationException if order is invalid
 * @throws ProcessingException if processing fails
 */
public OrderResult processOrder(
        CustomerOrder order, 
        ProcessingOptions options) {
    // Implementation
}

5.2 Architecture Guidelines

// 1. Dependency Injection
@Configuration
public class ServiceConfig {
    @Bean
    public OrderService orderService(
            OrderRepository repository,
            OrderValidator validator,
            OrderProcessor processor) {
        return new OrderService(repository, validator, processor);
    }
}

// 2. Interface Segregation
public interface OrderProcessor {
    void process(Order order);
}

public interface OrderValidator {
    void validate(Order order);
}

public interface OrderRepository {
    Order save(Order order);
    Optional<Order> findById(Long id);
}

6. Monitoring và Maintenance

6.1 Monitoring Tools

@Configuration
public class MonitoringConfig {
    @Bean
    public MeterRegistry meterRegistry() {
        return new SimpleMeterRegistry();
    }

    @Bean
    public Timer orderProcessingTimer(MeterRegistry registry) {
        return Timer.builder("order.processing")
            .description("Order processing time")
            .register(registry);
    }
}

@Service
public class MonitoredOrderService {
    private final Timer orderProcessingTimer;

    public void processOrder(Order order) {
        orderProcessingTimer.record(() -> {
            // Process order
        });
    }
}

6.2 Maintenance Strategies

// 1. Regular Health Checks
@Component
public class SystemHealthCheck {
    @Scheduled(fixedRate = 300000) // 5 minutes
    public void checkSystemHealth() {
        checkDatabaseConnections();
        checkExternalServices();
        checkMemoryUsage();
        checkResponseTimes();
    }
}

// 2. Performance Monitoring
@Aspect
@Component
public class PerformanceMonitor {
    private final MeterRegistry registry;

    @Around("@annotation(Monitored)")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) 
            throws Throwable {
        Timer.Sample sample = Timer.start(registry);
        try {
            return joinPoint.proceed();
        } finally {
            sample.stop(registry.timer("method.execution",
                "class", joinPoint.getSignature().getDeclaringTypeName(),
                "method", joinPoint.getSignature().getName()));
        }
    }
}

7. Best Practices

7.1 Development Guidelines

// 1. SOLID Principles
public interface PaymentProcessor {
    void process(Payment payment);
}

@Service
public class CreditCardProcessor implements PaymentProcessor {
    @Override
    public void process(Payment payment) {
        // Process credit card payment
    }
}

@Service
public class PayPalProcessor implements PaymentProcessor {
    @Override
    public void process(Payment payment) {
        // Process PayPal payment
    }
}

// 2. Design Patterns
public class OrderProcessorFactory {
    private final Map<String, OrderProcessor> processors;

    public OrderProcessor getProcessor(String type) {
        return processors.getOrDefault(type,
            new DefaultOrderProcessor());
    }
}

7.2 Review Process

// Code Review Checklist
public class ReviewChecklist {
    public void checkTechnicalDebt() {
        // 1. Code Quality
        checkCodeDuplication();
        checkComplexity();
        checkCoverage();

        // 2. Architecture
        checkDependencies();
        checkLayering();
        checkScalability();

        // 3. Performance
        checkMemoryUsage();
        checkResponseTimes();
        checkResourceUtilization();

        // 4. Maintainability
        checkDocumentation();
        checkNaming();
        checkTestability();
    }
}

8. Case Studies

8.1 Legacy System Modernization

// Before: Legacy Code
public class LegacyOrderSystem {
    public void processOrder(String orderData) {
        // Parse CSV string
        String[] data = orderData.split(",");
        // Direct database calls
        Connection conn = DriverManager.getConnection(URL);
        Statement stmt = conn.createStatement();
        // Raw SQL
        stmt.execute("INSERT INTO ORDERS ...");
    }
}

// After: Modern Implementation
@Service
public class ModernOrderSystem {
    private final OrderRepository repository;
    private final OrderMapper mapper;

    public Order processOrder(OrderDTO orderDTO) {
        Order order = mapper.toEntity(orderDTO);
        return repository.save(order);
    }
}

8.2 Microservices Migration

// Before: Monolithic Application
@Service
public class MonolithicOrderService {
    public Order processOrder(Order order) {
        validateOrder(order);
        processPayment(order);
        updateInventory(order);
        notifyCustomer(order);
        return order;
    }
}

// After: Microservices
@Service
public class OrderService {
    private final OrderValidator validator;
    private final EventPublisher eventPublisher;

    public Order processOrder(Order order) {
        validator.validate(order);
        Order savedOrder = repository.save(order);
        eventPublisher.publish(new OrderCreatedEvent(savedOrder));
        return savedOrder;
    }
}

// Separate Payment Microservice
@Service
public class PaymentService {
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        processPayment(event.getOrder());
    }
}

// Separate Inventory Microservice
@Service
public class InventoryService {
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        updateInventory(event.getOrder());
    }
}

9. References và Further Reading

9.1 Books

  • Working Effectively with Legacy Code (Michael Feathers)
  • Refactoring (Martin Fowler)
  • Clean Architecture (Robert C. Martin)
  • Domain-Driven Design (Eric Evans)
  • Building Evolutionary Architectures (Neal Ford)

9.2 Online Resources