Какими принципами вы руководствуетесь при проведении code review?

Ответ

Основные принципы и проверяемые аспекты:

1. Принципы проектирования (SOLID):

  • SRP (Single Responsibility): Класс/метод должен иметь одну причину для изменения.
  • OCP (Open/Closed): Код должен быть открыт для расширения, но закрыт для модификации.
  • LSP (Liskov Substitution): Объекты подклассов должны быть заменяемы объектами родительского класса.
  • ISP (Interface Segregation): Много специализированных интерфейсов лучше одного общего.
  • DIP (Dependency Inversion): Зависимости должны строиться на абстракциях, а не на деталях.

2. Практические принципы:

  • KISS (Keep It Simple, Stupid) / YAGNI (You Ain't Gonna Need It): Простота и отсутствие избыточной функциональности.
  • DRY (Don't Repeat Yourself): Отсутствие дублирования логики.

3. Качество кода:

  • Читаемость: Понятные имена переменных/методов, разумная длина методов и классов, отсутствие "магических чисел".
  • Тестируемость: Код должен легко покрываться unit-тестами (минимум side effects, зависимости, внедряемые через интерфейсы).
  • Обработка ошибок: Корректное использование исключений, их логирование.
  • Производительность: Отсутствие очевидных bottlenecks (напр., N+1 запрос в цикле).

Пример рефакторинга:

До (нарушает SRP, DRY, читаемость):

void processOrder(Order order) {
    // Валидация (50 строк)
    if (order.getItems().isEmpty()) { throw ... }
    // ...
    // Логика расчета (30 строк)
    // ...
    // Сохранение в БД и отправка email (20 строк)
    // ...
}

После (разделение ответственностей):

@Service
@RequiredArgsConstructor // Lombok для внедрения зависимостей
public class OrderProcessor {
    private final OrderValidator validator;
    private final OrderCalculator calculator;
    private final OrderRepository repository;
    private final NotificationService notifier;

    public void process(Order order) {
        validator.validate(order);          // Ответственность 1
        calculator.calculateTotals(order);  // Ответственность 2
        repository.save(order);             // Ответственность 3
        notifier.sendConfirmation(order);   // Ответственность 4
    }
}

Ответ 18+ 🔞

А, ну вот, смотри, опять про эти ваши принципы, блядь. SOLID, DRY, KISS... Как будто без них код не пишется, ёпта! Но, сука, если вдуматься — они реально спасают от пиздеца в проекте, вот честно.

1. SOLID — это типа священная корова, блядь, но не все её доят правильно.

  • SRP (Единственная Ответственность): Это когда твой класс не пытается быть швецом, жнецом и на дуде игрецом одновременно. Вот смотри, если у тебя метод processOrder и валидирует, и считает, и в базу пишет, и письма шлёт — это уже не метод, а ёбаный цирк с конями. Он должен делать что-то одно, пиздец! Иначе, когда надо будет поменять логику отправки писем, ты полезешь в этот монстр-метод и навернёшь кучу всего другого. Чистая головная боль, блядь.
  • OCP (Открыт/Закрыт): Ну, типа, ты написал класс, он работает. Потом приходит новая фича. Ты не должен лезть внутрь этого класса и костылять его, рискуя всё сломать. Ты должен его расширить — через наследование, композицию, хуле. Как будто к машине прикрутил новый глушитель, а не перепаивал всю проводку, блядь.
  • LSP (Подстановка Лисков): Вообще, гениальная хуйня, если понять. Если у тебя есть Утка и РезиноваяУтка, то везде, где работает код с Уткой, должна спокойно работать и РезиноваяУтка. Если твоя резиновая утка при вызове метода крякнуть() вдруг взрывается — это пиздец, нарушение принципа. Наследник не должен ломать логику родителя, вот и всё.
  • ISP (Разделение интерфейсов): Представь, тебе дали пульт от всего нахуй: и телевизор включить, и микроволновку, и машину завести. И на нём 150 кнопок. Ты чтобы чайник включить, должен ещё и таймер на духовке выставить? Бред. Лучше много маленьких интерфейсов (Включатель, Грелка, Таймер), чем один большой УниверсальныйБог. Не заставляй класс реализовывать методы, которые ему нахуй не сдались.
  • DIP (Инверсия зависимостей): Это про то, чтобы не зависеть от конкретной хуйни. Не пиши new PostgresRepository() внутри сервиса. Объяви зависимость от интерфейса Repository, а конкретную реализацию тебе подсунут снаружи (через конструктор, например). Тогда завтра вместо Постгреса захочешь MongoDB — просто подменишь реализацию в одном месте, а не будешь по всему коду ползать. Красота, ёпта!

2. KISS/YAGNI и DRY — это вообще основа выживания, блядь.

  • KISS/YAGNI: Не выёбывайся, пиши проще. Не делай навороченную систему отчётов с AI, если сейчас нужен просто CSV-файл. Не предсказывай будущее, оно всегда приходит другим, блядь. Сделал просто — и спи спокойно.
  • DRY (Не Повторяйся): Видишь одинаковый кусок кода в двух местах? Выноси в отдельный метод, ёб твою мать! Иначе придёшь через месяц фикс делать и забудешь, что в одном из десяти мест эту логику тоже надо поправить. И будет тебе счастье — баги на ровном месте.

3. Качество кода — это не для галочки, а чтобы не стреляться потом.

  • Читаемость: Называй переменные не a1, tmp2, data, а userOrders, validationResult, isProcessingEnabled. Метод calculate() — это пиздец какой плохо. calculateOrderDiscountWithPromo() — уже лучше. И да, убери нахуй эти магические числа 86400 и 7. Вынеси в константы SECONDS_IN_DAY и MAX_RETRY_ATTEMPTS. Твои коллеги тебе спасибо скажут, а не в глаз дадут.
  • Тестируемость: Если твой класс как паук — тянет за собой зависимости на весь проект (сам создаёт базу, файлы, ходит в апишки), как ты его протестируешь? Никак. Отсюда и side effects, и баги. Зависимости через интерфейсы, логика чистая — вот тогда и тесты писать в кайф.
  • Обработка ошибок: Молча проглотить исключение — это высший пилотаж долбоёбства. try {...} catch (Exception e) { /* TODO */ } — ядрёна вошь, да ты просто маньяк! Либо обработай адекватно, либо пробрось выше, но хотя бы залогируй, что за хуйня произошла.
  • Производительность: Самый классический пиздец — N+1 запрос в цикле. Получил список юзеров, а потом для каждого в цикле лезешь в базу за его заказами. База ебётся, приложение тормозит. Сразу думай, как собрать всё за один-два запроса. Голова же не только для шапки, блядь.

Ну и смотри, как было и как стало:

Было (пиздец и хаос):

void processOrder(Order order) {
    // Валидация (50 строк)
    if (order.getItems().isEmpty()) { throw ... }
    // ...
    // Логика расчета (30 строк)
    // ...
    // Сохранение в БД и отправка email (20 строк)
    // ...
}

Это ж метод-комбайн, ёпта! Он и валидирует, и считает, и пишет, и шлёт. Попробуй его протестировать или изменить что-то одно. Овердохуища проблем.

Стало (красота, блядь):

@Service
@RequiredArgsConstructor // Lombok для внедрения зависимостей
public class OrderProcessor {
    private final OrderValidator validator;
    private final OrderCalculator calculator;
    private final OrderRepository repository;
    private final NotificationService notifier;

    public void process(Order order) {
        validator.validate(order);          // Отдельный специалист по валидации
        calculator.calculateTotals(order);  // Отдельный бухгалтер
        repository.save(order);             // Отдельный кладовщик
        notifier.sendConfirmation(order);   // Отдельный почтальон
    }
}

Каждый занимается своим делом. Захотел поменять валидацию — идёшь в OrderValidator и не боишься, что случайно сломаешь расчёт скидок. Тестировать — одно удовольствие: подсунул заглушки (mock) для зависимостей и проверяй свою логику. Вот оно, счастье-то!

Короче, все эти принципы — они не для того, чтобы умничать на собеседованиях. Они чтобы код не превращался в неподдерживаемое говно, в котором через полгода разберётся только тот, кто его писал, да и то, если не забудет. А так — всё по полочкам, как в аптеке.