Контекст
Когда я был младшим разработчиком, мне повезло начать разработку в среде, где я мог получать отзывы о коде. Поначалу я даже толком не знал, какими были критерии проверки. Я считал, что разработка завершена, если функциональность работает правильно, а замечания, высказанные во время проверок, поначалу казались незначительными.
Мне часто задавали вопросы об областях, которые, на первый взгляд, не имели прямого отношения к функциональности, например о порядке импорта или именах переменных.
-
«Действительно ли это значение состояния необходимо?»
-
«Вы обработали это с помощью watch, но разве здесь не уместнее было бы использовать событие?»
-
«Разве у этого компонента не слишком много обязанностей?»
-
«Правильно ли обрабатываются null, undefined и пустые массивы?»
Поначалу исправлять каждый пункт по очереди было утомительно. Однако после того, как я поработал над командной разработкой и эксплуатацией сервисов в продакшене, моя точка зрения постепенно изменилась. Вместо того чтобы ограничиваться вопросом «Работает ли функциональность прямо сейчас?», я начал спрашивать себя: «Если кому-то снова понадобится изменить этот код, сможет ли он легко в нём разобраться?»
В рабочем окружении сложно придерживаться установки «если позже возникнет проблема, другой разработчик её исправит». Даже при командной разработке кому-то ещё может понадобиться изменить написанный мной код, а через несколько месяцев мне самому, возможно, снова придётся с ним работать.
По мере того как я снова и снова сталкивался с подобными ситуациями, я естественным образом начал проверять во время разработки некоторые из тех аспектов, на которые раньше обращал внимание при проверке кода.
Конечно, на практике по-прежнему сложно тщательно проверять каждую часть кода. Особенно когда сроки разработки сжаты. Поэтому под «самопроверкой кода» я подразумеваю нечто ближе к минимальной проверке: при быстрой разработке ещё раз взглянуть на проблемы, которые легко упустить, вместо проведения исчерпывающего анализа.
1. Почему необходима самопроверка кода
При быстрой разработке легко перейти к следующей задаче после реализации функции и подтверждения того, что она работает правильно. Если рассматривать ситуацию исключительно с точки зрения скорости разработки, это может быть эффективным подходом. Однако чем быстрее вы разрабатываете, тем меньше у вас возможностей взглянуть на код объективно. Поскольку во время написания кода вы понимаете весь контекст, легко не заметить ненужный код или неоднозначные структуры.
2. Проверка не заканчивается только потому, что функциональность работает
Если экран отображается нормально, а нажатие кнопки приводит к ожидаемому результату, разработка может показаться завершённой. Однако реальные сервисы состоят не только из штатных ситуаций (Happy Path). Даже при рассмотрении одного API-запроса необходимо учитывать различные сценарии.
-
Штатный ответ → Отобразить данные
-
Ошибка API → Обработать ошибку
-
Нет данных в ответе → Пустое состояние
-
undefined / null → Проверить наличие данных и безопасно их обработать
-
Повторные нажатия → Предотвратить дублирование запросов или несогласованное состояние
При самопроверке не ограничивайтесь простым вопросом «Работает ли всё правильно?». Также проверьте: «Сохраняет ли экран работоспособность даже в нештатных ситуациях?»
3. Первая проверка: поиск ненужного кода
Первое, на что стоит обратить внимание, на удивление просто: «Действительно ли этот код необходим?»
① Неиспользуемые переменные и импорты
Проверьте, не остались ли console.log() для тестирования, переменные, которые больше не используются, или импортированные модули, которые не применяются. Хорошей идеей будет автоматизировать эту проверку с помощью инструментов статического анализа, таких как ESLint.
② Дублирующийся код
Если одинаковые вызовы API и обработка ошибок повторяются на нескольких экранах, проверьте, нужно ли вынести их в общее место. Однако бездумная консолидация кода не всегда является правильным решением. Принимайте решение, исходя из вопроса: «Действительно ли сопровождение станет проще, если сделать этот код общим?»
③ Избыточные значения состояния
// ❌ AS-IS
const dataList = ref([]);
const isEmpty = ref(false);
const hasData = ref(false);
// ⭕ TO-BE
const dataList = ref([]);
const isEmpty = computed(() => dataList.value.length === 0);한 상태값
Как показано выше, сначала можно подумать, действительно ли такие значения, как isEmpty или hasData, которые можно вычислить исключительно на основе dataList, необходимо хранить в отдельном состоянии (ref).
Если управлять состоянием отдельно, необходимо вручную обновлять isEmpty или hasData вместе с данными при каждом их изменении. Если пропустить хотя бы одну часть логики обновления состояния, может возникнуть ошибка, при которой фактические данные существуют, но на экране отображается «Нет данных». Используя computed, чтобы оно зависело от исходных данных, можно уменьшить необходимость вручную синхронизировать значения состояния и снизить вероятность того, что разработчики забудут обновить состояние.
4. Вторая проверка: анализ обязанностей компонента
Даже изначально простой компонент по мере развития проекта может получить множество обязанностей, включая поиск пользователей, аутентификацию, валидацию формы, модальные окна и пагинацию.
Изучая большой компонент, разделите его на области по функциональным возможностям и проверьте, можно ли отделить эти области друг от друга. Однако бессистемное разделение кода только потому, что он длинный, лишь увеличивает затраты на навигацию по файлам. Важно не «сделать его маленьким», а «чётко определить его обязанности».
Props и Emits: Проверьте, не контролирует ли родитель напрямую слишком много состояния и можно ли понять назначение события, просто взглянув на его имя.
5. Третья проверка: проверка состояния и исключительных ситуаций
Лично я считаю эту часть самой важной в самопроверке. Экраны гораздо чаще ломаются в исключительных ситуациях, чем в штатных.
① Состояния загрузки и ошибки
Получает ли пользователь обратную связь во время загрузки данных? (Предотвращение повторных нажатий)
Как обрабатывается экран при сбое API? (Не застрянет ли пользователь на пустом экране?)
Однажды во время проверки кода проекта я обнаружил проблему: при возникновении определённой ошибки во время аутентификации отсутствовала обработка исключения, из-за чего весь экран становился белым и пользователь не мог продолжить работу. Поскольку в штатном процессе аутентификации проблем не было, эту ошибку было легко не заметить, если проверять только обычные случаи. После этого, просматривая код вызовов API, я проверяю не только результат успешного выполнения, но и спрашиваю себя: «Если здесь произойдёт ошибка, какой экран увидит пользователь?»
② Пустые значения и Null / Undefined
Ситуация, когда данные представлены пустым массивом ([]) (пустое состояние), отличается от ситуации, когда произошла ошибка API. Кроме того, если не учитывать возможность отсутствия profile в такой цепочке, как user.profile.name, может возникнуть ошибка времени выполнения, из-за которой экран не сможет корректно отобразиться.
6. Четвёртая проверка: анализ асинхронных операций и вызовов API
Дублирующиеся вызовы: Проверьте, не вызывается ли один и тот же API без необходимости при входе на страницу, выполнении watch или возникновении пользовательского события.
Порядок асинхронной обработки (Race Condition): Проверьте, может ли при быстром повторном выполнении пользователем одного действия сначала прийти ответ на более поздний запрос, а затем ответ на более ранний запрос, который перезапишет актуальное состояние.
7. Пятая проверка: повторный взгляд с точки зрения сопровождаемости
Наконец, приведите код в порядок с точки зрения человека, который видит его впервые.
Именование и семантика HTML: Используйте имена, раскрывающие назначение, вместо таких имён, как const temp, и проверяйте, используется ли <button>, а не <div>, для кнопок, требующих обработки событий клика.
Комментарии и мёртвый код: Не оставляйте неиспользуемый код закомментированным построчно. Git хранит историю. Комментарии должны объяснять не то, «что» делает код, а «почему» он написан именно так.
Структурная согласованность: Проверяйте, соответствует ли расположение импортов, props, состояния, методов и других элементов внутри файла существующим соглашениям проекта.
Если в проекте уже используется общий подход к работе с API, утилиты или UI-компоненты, сначала изучите существующие реализации, прежде чем создавать новый подход.
Даже если недавно написанный код кажется технически более качественным, использование шаблона, отличающегося от существующих шаблонов проекта, на самом деле может увеличить затраты на сопровождение. Поэтому при применении нового подхода безопаснее сначала проверить, работает ли он согласованно во всём проекте, а не поспешно менять существующий подход только потому, что «этот метод кажется лучше».
Например, если вызовы API и обработка ошибок уже стандартизированы, проверьте, не обрабатывает ли их отдельный экран по-своему.
8. Самопроверка на основе фактических изменений
Проверка всех этих аспектов во время написания кода может замедлить разработку. Я предпочитаю сначала завершить реализацию функциональности, а затем ещё раз проверить её в представлении, где виден только фактически изменённый код, например в Git Diff или в списке изменений PR.
При написании кода я сосредотачиваюсь на вопросе «Как это реализовать?». Во время проверки я меняю перспективу на вопрос «Если бы я увидел этот код впервые, смог бы я его понять?»
[Минимальный чек-лист самопроверки]
-
Функциональность
-
Работает ли это так, как задумано, при корректных входных данных?
-
Обработаны ли состояния загрузки, ошибки и отсутствия данных?
-
Проверили ли вы возможные значения null / undefined?
-
Не влияет ли это на существующую функциональность?
-
Код
-
Есть ли неиспользуемые переменные / импорты / операторы console.log?
-
Избегали ли вы создания ненужных значений состояния?
-
Проверили ли вы наличие дублирующейся логики?
-
Не слишком ли широка зона ответственности компонента?
-
API / Асинхронные операции
-
Не вызываются ли одинаковые API избыточно?
-
Согласована ли обработка ошибок API?
-
Может ли последовательное выполнение запросов привести к рассогласованию состояния?
-
Сопровождаемость
-
Можно ли понять назначение переменных и функций только по их именам?
-
Есть ли мёртвый код или устаревшие комментарии?
-
Соответствует ли это существующим шаблонам проекта?
-
Сможет ли разработчик, впервые увидевший этот код, разобраться в нём?
9. Практический опыт проверки кода и реалистичные компромиссы
То, чему я научился, получая замечания на проверках кода в качестве младшего разработчика, позже помогло мне, когда я стал отвечать за проверки кода в проектах, улучшавших уже работающие сервисы. Я начал не просто искать проблемы в коде, но и учитывать, как они могут повлиять на будущие изменения и сопровождение. На самом деле благодаря этому процессу мне удалось выявить и исправить проблемы, которые могли возникнуть в предыдущем проекте, ещё до того, как они проявились.
Однако применять идеальные стандарты к каждому проекту невозможно. Я столкнулся с этим, поддерживая проект с жёсткими сроками. Поскольку я также отвечал за разработку функциональности, у меня не было достаточно времени, чтобы привести в порядок весь существующий код. В такой ситуации я расставил приоритеты и сначала улучшил необходимые части.
-
Критические проблемы, влияющие на функциональность
-
Области, напрямую затрудняющие сопровождение
-
Области, где стандартизация принесла бы очевидную пользу
-
Простая очистка стиля кода (минимизация таких задач, как удаление операторов console и упорядочивание комментариев)
Игнорирование каждой проблемы из-за жёстких сроков в конечном итоге приведёт к техническому долгу, но попытка достичь совершенства сразу и срыв сроков — тоже проблема. Поэтому я расставляю приоритеты в зависимости от ситуации и стараюсь сначала улучшить необходимые части.
Заключение
Самопроверка кода не обязательно должна быть подробным архитектурным анализом. Особенно это верно, когда требуется быстрая разработка. Для меня самопроверка кода в конечном счёте означает «ещё раз критически взглянуть на код после реализации функциональности».
Будучи младшим разработчиком, я учился, поочерёдно исправляя замечания, полученные на проверках кода. Даже сейчас, сталкиваясь с похожими ситуациями, я стараюсь вспоминать вопросы, которые мне задавали во время тех проверок.
Когда нужно быстро разработать функциональность, не обязательно пытаться идеально проверить каждую часть — можно начать хотя бы с пятиминутной повторной проверки, чтобы убедиться, что вы ничего не упустили.
Code_Latte