Ревью кода на Java: что ищут senior-ы и как не провалить код-ревью
Вы написали код, проверили его локально, запустили тесты — всё зелёное. Открываете Pull Request и через пару часов видите десяток комментариев от старшего коллеги. Знакомо? Ревью кода на Java — это не бюрократическая процедура, а настоящий экзамен на зрелость разработчика. Senior-ы видят код иначе: они не просто проверяют, работает ли он, а смотрят, как долго он будет работать и как сложно его будет менять.
В этой статье мы разберём самые частые замечания, которые оставляют опытные Java-разработчики на код-ревью. Вы узнаете, как писать чистый, поддерживаемый код и проходить ревью с первого раза.
1. Игнорирование принципов SOLID и чистой архитектуры
Это, пожалуй, самое частое замечание senior-ов. Когда вы пишете метод на 200 строк или класс, который делает всё на свете — это сразу бросается в глаза. Senior не будет тратить время на разбор такого кода, он просто попросит его разбить.
Пример плохого кода:
public class OrderService {
public void processOrder(Order order) {
// 1. Валидация
if (order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order must have items");
}
// 2. Проверка пользователя
User user = userRepository.findById(order.getUserId());
if (user == null || user.isBlocked()) {
throw new SecurityException("User not valid");
}
// 3. Расчёт скидки
double discount = 0;
if (order.getTotal() > 1000) {
discount = 0.1;
}
// 4. Сохранение
order.setDiscount(discount);
orderRepository.save(order);
// 5. Отправка email
emailService.sendOrderConfirmation(order);
}
}
Что скажет senior: «Этот метод нарушает Single Responsibility Principle. Он отвечает за валидацию, расчёт, сохранение и отправку уведомлений. Раздели на отдельные методы или вынеси в разные сервисы.»
Как исправить:
public class OrderService {
private final OrderValidator validator;
private final DiscountCalculator discountCalculator;
private final OrderRepository orderRepository;
private final NotificationService notificationService;
public void processOrder(Order order) {
validator.validate(order);
double discount = discountCalculator.calculate(order);
order.setDiscount(discount);
orderRepository.save(order);
notificationService.sendOrderConfirmation(order);
}
}
В Java-разработке принцип единственной ответственности — это база. Если вы видите в методе слово «and» (проверить и сохранить и отправить), значит, пора рефакторить.
2. Работа с null и NPE (NullPointerException)
NullPointerException — бич Java-разработчиков. Senior-ы ненавидят неожиданные NPE и всегда обращают внимание на то, как вы работаете с null. Типичное замечание: «А что будет, если метод вернёт null?»
Пример:
public String getUserEmail(Long userId) {
User user = userRepository.findById(userId);
return user.getEmail().toLowerCase();
}
Замечание senior-а: «Если пользователь не найден, user будет null — получим NPE. Используй Optional или хотя бы проверку.»
Исправленный вариант:
public String getUserEmail(Long userId) {
return userRepository.findById(userId)
.map(User::getEmail)
.map(String::toLowerCase)
.orElseThrow(() -> new UserNotFoundException("User not found: " + userId));
}
Использование Optional — это стандарт в современной Java. Senior-ы также советуют не передавать Optional в параметрах методов и не хранить его в полях класса. Он создан только для возвращаемых значений.
3. Магические числа и строки
Ещё один пункт, который мгновенно привлекает внимание на код-ревью. Если вы пишете в коде числа вроде 1000, 60 * 60 * 24 или строки типа "ACTIVE" — ждите комментарий.
Пример:
if (order.getTotal() > 1000) {
discount = 0.1;
}
if (user.getStatus().equals("ACTIVE")) {
// ...
}
Замечание: «Что такое 1000 и 0.1? Сделай константы или вынеси в конфигурацию. Строку 'ACTIVE' лучше заменить на enum.»
Исправление:
private static final BigDecimal DISCOUNT_THRESHOLD = BigDecimal.valueOf(1000);
private static final BigDecimal DISCOUNT_RATE = BigDecimal.valueOf(0.1);
// ...
if (order.getTotal().compareTo(DISCOUNT_THRESHOLD) > 0) {
discount = DISCOUNT_RATE;
}
// И для статуса:
if (user.getStatus() == UserStatus.ACTIVE) { ... }
Магические числа делают код непонятным и сложным в поддержке. Через месяц вы сами не вспомните, что значит 1000. Senior-ы ценят читаемость превыше всего.
4. Неправильная работа с исключениями
Ошибки в Java — это неотъемлемая часть. Но есть большая разница между тем, как junior и senior обрабатывают исключения. Типичные ошибки:
- Проглатывание исключений: пустой
catchблок — это антипаттерн. - Слишком общие исключения: ловить
ExceptionилиThrowable— плохая практика. - Бросание
RuntimeExceptionбез смысла: лучше использовать конкретные исключения.
Плохой пример:
try {
// какой-то код
} catch (Exception e) {
// ничего не делаем
}
Замечание senior-а: «Ты только что проглотил ошибку. Мы никогда не узнаем, что пошло не так. Минимум — залогируй, лучше — пробрось дальше или обработай осмысленно.»
Хороший пример:
try {
paymentService.processPayment(order);
} catch (PaymentDeclinedException e) {
log.warn("Payment declined for order {}: {}", order.getId(), e.getMessage());
throw new OrderProcessingException("Unable to process order", e);
}
Помните: код-ревью Java часто проваливается именно из-за плохой обработки ошибок. Senior смотрит: «А что будет, если внешний сервис упадёт? А если БД недоступна?»
5. Отсутствие тестов или плохие тесты
Senior-разработчик никогда не одобрит Pull Request без тестов. Но даже если тесты есть, они могут быть плохими. Типичные замечания:
- Тесты, которые зависят от внешних ресурсов (БД, сеть) — должны быть изолированы.
- Тесты без assert — они ничего не провер