Профессиональные привычки программиста

Оглавление

  1. Введение
  2. Доводить задачу до конца
  3. Следить за единым стилем кода
  4. Оставлять комментарии к объемным решениям
  5. Обсуждать новые решения на ранних этапах
  6. Сделать рефакторинг – частью повседневной работы
  7. Закладывать возможность изменений
  8. Изучать и внедрять DevOps и CI/CD
  9. Передавать знания
  10. Относиться к TDD взвешенно
  11. Работать в команде
  12. Заключение

Введение

Опытный разработчик отличается не только знанием языков или фреймворков, но и набором “правильных” рабочих привычек. В этой статье мы попробуем обсудить довольно простые практики, которые помогают опытным разработчикам добиваться высокого качества кода и эффективности. Приведем примеры кода и некоторые иллюстрации, показывающие идеи архитектуры и взаимодействия.

Доводить задачу до конца

Профессионал не бросает работу на полдороге. Он трезво оценивает, все ли сделано, и не переключается на новую задачу, пока не завершил текущую, даже если этого никто не заметит. Личная ответственности и отказ от переключения между задачами позволяют меньше уставать и делать свою работу более качественной.

Например, не стоит оставлять в коде пустые места с //TODO и ожидать, что кто-то потом доделает. Вместо незаконченного:

public void processUserData(UserData userData) {
    // TODO: добавить валидацию
    userRepository.save(userData);
    // TODO: добавить обработку ошибок
    // TODO: отправка уведомления
}

опытный разработчик сразу пишет готовый код с проверками и обработкой ошибок:

public void processUserData(UserData userData) {
    if (userData == null) {
        throw new IllegalArgumentException("userData is null");
    }
    if (userData.getEmail() == null || userData.getEmail().isEmpty()) {
        throw new IllegalArgumentException("Email is required");
    }
    try {
        userRepository.save(userData);
        emailService.notifyUserCreated(userData);
    } catch (SQLException ex) {
        logger.error("Failed to save user", ex);
        throw new RuntimeException("Unable to process user data at this time", ex);
    }
}

Таким образом код сразу становится пригодным для продакшена и не требует “переделок” позже.

Следить за единым стилем кода

Не важно, какой стиль выбрали вы на проекте – гораздо важнее, чтобы он строго соблюдался на проекте. Когда все разработчики используют одни и те же соглашения, код легче читать и поддерживать. Последовательное выдерживание стиля намного важнее, чем выбор “самого правильного и эффективного”. Это означает, что не стоит называть одни и те же вещи разными именами, менять подход к тестам “под настроение” и т.д.

Подключение статического анализатора (Checkstyle, SonarLint и др.), который автоматически проверяет стиль и грамотная работа с ним в контексте CI/CD – способно существенно повысить качество кода. Вы экономите время на бесконечных комментариях к пул реквестам и все внимание высвобождается на работу с логикой системы, а не на правильных отступах, именовании переменных и т.д.

Оставлять комментарии к объемным решениям

Опытный специалист понимает: если вы ввели в проект паттерн, архитектурный прием, абстракцию, которая унифицирует часть логики – крайне важно не держать это в себе и рассказать команде. Это также относится к методам со сложными формулами расчета или сложным процессам. Например, при использовании Repository-паттерна хорошо бы в документе или комментарии объяснить его цель и когда его применять:

/**
 * Repository pattern: абстрагирует доступ к данным.
 * Цель: отделить слой доменной модели от конкретной СУБД.
 * Когда использовать: для CRUD-операций над сущностью, чтобы проще делать мок-объекты в тестах.
 * Когда не использовать: для простых read-only запросов без доменной логики.
 */
public class GenericRepository<T> { ... }

Таким образом другие разработчики сразу поймут идею. Грамотно задокументированные решения позволяют новым членам команды быстро понять суть без долгих объяснений. Главное преимущество – знание о том, как и почему ваш код работает именно так, остается в коде, а не в мыслях отдельных людей, которые часто могу “уйти в закат”.

Обсуждать новые решения на ранних этапах

Когда мы хотим ввести новый подход или паттерн (например, CQRS или стратегию кеширования), не стоит делать это в одиночку. Опытный инженер сначала всегда сделает “дизайн доку” или минимальный прототип и показать его команде, а не сразу начнет переписывать большой кусок системы или реализовывать новую функциональность. Например, мы можем:

  1. Подоготовить дизайн. доку.
  2. Имплементировать небольшой PoC – один “Command” и “Query” обработчик (CQRS).
  3. Показать коллегам эту идею, объяснить плюсы и минусы.
  4. Собрать обратную связь и скорректировать модель вместе.

Это предотвращает ситуации, когда по ходу работы вы директивно или по факту навязываете команде что-то новое, а потом оказывается, что решение не устроило всех или оказалось не самым оптимальным. Совместная работа на ранних этапах помогает нам не только избежать принятие плохого решения, но часто существенно улучшить изначальное решение за счет опыта коллег.

Рис. 1. Пример реализации CQRS.
// Минимальный пример: команда создания пользователя в стиле CQRS
public class CreateUserCommand { 
    public String name; public String email;
}

public class CreateUserHandler {
    private final UserRepository repository;
    public CreateUserHandler(UserRepository repository) {
        this.repository = repository;
    }
    public long handle(CreateUserCommand cmd) {
        User user = new User(cmd.name, cmd.email);
        return repository.save(user);
    }
}

Сделать рефакторинг – частью повседневной работы

Специалист с опытом старается не откладывать улучшение кода “на потом”. Вместо создания отдельных тасок в джире на рефакторинг, разработчики включают рефакторинг в свою текущую задачу. То есть, если мы меняем функциональность в каком-то месте, сразу улучшайте там же кодовую базу.

Например, до рефакторинга код может выглядеть так (все обязанности в одном методе):

public void processOrder(Order order) {
    if (order == null) throw new IllegalArgumentException("Order is null");
    double subtotal = 0;
    for (OrderItem item : order.getItems()) {
        subtotal += item.getPrice() * item.getQuantity();
    }
    order.setSubtotal(subtotal);
    order.setTax(subtotal * 0.08);
    order.setTotal(order.getSubtotal() + order.getTax());
    OrderDAO.save(order);
    EmailService.sendOrderConfirmation(order.getId());
}

После разумного рефакторинга тот же функционал разбивается на отдельные методы:

public void processOrder(Order order) {
    validateOrder(order);
    calculateTotals(order);
    saveOrder(order);
    notifyCustomer(order);
}

private void validateOrder(Order order) {
    if (order == null) throw new IllegalArgumentException("Order is null");
    if (order.getItems().isEmpty()) throw new IllegalArgumentException("Order has no items");
}

private void calculateTotals(Order order) {
    double subtotal = order.getItems().stream()
        .mapToDouble(i -> i.getPrice() * i.getQuantity()).sum();
    order.setSubtotal(subtotal);
    order.setTax(subtotal * 0.08);
    order.setTotal(order.getSubtotal() + order.getTax());
}

private void saveOrder(Order order) {
    OrderDAO.save(order);
}

private void notifyCustomer(Order order) {
    EmailService.sendOrderConfirmation(order.getId());
}

Такой подход позволяет нам с каждой новой задачей постепенно улучшать качество кода, не выделяя на это отдельный артефакт. Интеграция рефакторинга в обычную работу гарантирует, что код постоянно становится лучше, а не ждет специального разрешения от менеджмента.

Закладывать возможность изменений

Никогда, ни один проект не идет строго по плану – всегда есть неожиданные изменения в требованиях или внешние сложности. Опытный инженер всегда готов к подобным ситуациям. Он строит свою архитектуру так, чтобы новая функциональность могла быть реализована без глобальной переработки. Например, вместо жесткого класса PaymentProcessor с разными методами для кредиток, можно сделать интерфейс IPaymentMethod, а конкретные реализации (CreditCardPayment, PayPalPayment и т.д.) уже привязывать динамически.

Рис. 2. Тривиальная реализация логики обработки платежей

Проще говоря, “опытные инженеры знают, что требования постоянно меняются, и заранее закладывают гибкость в дизайн”. Это значит не просто знать, а использовать принципы SOLID, конфигурируемость и разделение ответственности, чтобы в будущем не переписывать приложение заново при любом изменении задачи.

Рис. 3. Реализация логики обработки платежей с применением SOLID принципов.

Изучать и внедрять DevOps и CI/CD

Современная разработка – это не только код. Нельзя закрывать глаза на процессы доставки и развертывания. Автоматизация сборки, тестирования и деплоя (CI/CD) – это важнейшая часть рабочего процесса. Система непрерывной интеграции помогает “выявлять баги до того, как они попадут в продакшн” и ускоряет выпуск релизов. Например, быстрая сборка и тесты при каждом git push значительно снижают риск конфликтов и проблем при релизе.

Рис. 4. Пример serverless архитектуры.

На диаграмме выше показан пример серверлесс-архитектуры (AWS), которая включает автоматизацию процессов. Внедряя и используя преимущества CI/CD, мы можем существенно увеличить скорость доставки новой функциональности и оптимизировать процесс разработки,В целом. Игнорируя эти практики, мы рискует получить медленные релизы и частые регрессы, а опытный программист всегда стремится минимизировать такие риски.

Передавать знания

Инженер с высокой квалификацией – не одиночка, который заперт в кабинете. Обмен знаниями внутри команды – очень ценная привычка. Лучшие инженеры не только пишут код, но и регулярно делятся опытом: проводят код-ревью, сессии парного программирования, выступают с доклады на внутренних семинарах и конференциях. Очень часто самые востребованных специалисты известны, как публицисты, спикеры на конференциях и как люди, которые строят процесс обучения в командах. Люди ценят их не только за код, но и за готовность помогать сообществу.

Вы можете взять за правило периодически объяснять коллегам сложные моменты и всегда подсказывать пути решения. Участие в обучении других не только повышает силу команды, но и стимулирует вас самого учиться новым коммуникационным навыкам и лидерству и восполнять пробелы в технических знаниях.

Относиться к TDD взвешенно

Тестирование – это крайне важный этап. Но слепое следование методологиям может нанести больше вреда, чем пользы. TDD и BDD хороши при четких требованиях, но не подходят абсолютно для каждого сценария. Жесткий подход может замедлить разработку. На практике процесс TDD в первый раз дается тяжеловато так как, на старте мы теряем и время, и возможность “свободно” реализовать решение. Поддерживаемый набор тестов требует существенного времени, особенно на этапе старта проекта.

Поэтому лучше использовать тесты разумно: в некоторых ситуациях, мы можем проверять ключевые сценарии, но не превращать каждый метод в отдельный тест-завод. Понимайте, зачем вы пишете тест: чтобы фиксировать требования и ловить регрессии, а не просто ради “галочки”. Принципы TDD хороши как инструмент для обучения и повышения качества, но не стоит жить по ним в ущерб здра¬вому смыслу.

Работать в команде

Разработка – это командная игра. Не стоит работать в изоляции, забывая о дизайнерах, аналитиках, менеджерах о продаже и тестировщиках. Активное взаимодействие с ними приводит позволяет создавать более качественный продукт. Когда вся команда активно взаимодействует, мы получаем решение, которое ориентировано на пользователя, при этом сохраняется эффективность и качество.

Рис. 5. Пример взаимодействия команды

Например, на диаграмме показано упрощенное взаимодействие команды.

Заключение

Каждый из рассмотренных пунктов – это не догма и не чеклист, а ориентир. Хороший разработчик не просто знает, что и как делать, – он понимает, зачем. В его фокусе всегда находятся:

  • простота решений, а не демонстрация “архитектурного величия”;
  • практичность и здравый смысл, а не слепое следование модным практикам;
  • ответственность за последствия, а не перекладывание вины на архитекторов, тестировщиков или менеджеров;
  • развитие команды, а не защита “своего кода”.

Важно не просто уметь писать качественный код, а строить понятные, прозрачные системы, в которые легко встраиваться другим. Делать это в команде, вместе с аналитиками, дизайнерами, DevOps-инженерами и тестировщиками – потому что хороший разработчик всегда работает не в одиночку.

Оптыные разработчики становятся лидерами не благодаря титулу, а благодаря экспертизе и коммуникационным навыкам.

Не бойтесь упрощать, задавать вопросы, помогать другим и защищать качество. Это и есть то, что делает нас не просто программистами, а инженерами.

Loading