Как писать код для «будущего себя»

0
1077
views

Перевод статьи «Writing code for your future self».

Пишите код для себя в будущем, а не в настоящем

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

Проблема в том, что вы пишете код для себя в настоящем. Вместо этого следует писать для себя в будущем. Обязательно задавайте себе вопрос: «Поймет ли «будущий я» предназначение этого блока кода?».

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

Не пытайтесь произвести на себя впечатление

Мне нравится писать код изобретательно. Так я чувствую себя умным. То есть, чувствую себя умным, пока не вернусь к этому коду год спустя, когда буду тщетно пытаться понять, что этот код делает и почему я не сделал все более просто и стандартно.

Поэтому, если хотите сделать что-то действительно впечатляющее, напишите читабельный код.

Используйте осмысленные имена

Мне сложно придумывать имена для своих переменных, функций, модулей и т. д. На этот счет даже есть популярная цитата:

«В информатике есть только две сложные вещи: аннулирование кэша и выбор имен»,

– Фил Карлтон.

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

  • Держаться подальше от слишком общих имен, таких как container или data.
  • Использовать префиксы для булевых выражений (даже в типизированных языках), например, is или has.
  • Использовать такие префиксы как get или create для функций – чтобы указать на действие.
  • Использовать префиксы вроде min или total для лучшего описания чисел.
  • Использовать правильное множественное число при создании массивов, например, users.
  • Избегать однобуквенных обозначений переменных, таких как «е». Вместо этого лучше использовать event или error.
  • Не бояться длинных многословных имен вроде getTotalDaysSinceLastLogin.

И, самое главное, следует по возможности уменьшать потенциальную путаницу.

Разделяйте свои условия

Ядром многих приложений является логика, которая на самом деле просто сводится к if-предложениям. Условия при этом могут стать довольно сложными.

Сколько времени у вас уйдет, чтобы понять логику этого кода?

if (users[0] && posts.find(post => post.userId === users[0].id)) {
  showUserPost();
}

Время имеет большое значение. Безусловно, в конечном итоге я пойму смысл этого отрывка. Но если так будет написана вся кодовая база, то любой человек, который будет заниматься ее поддержкой (включая вас), вырвет себе все волосы, пытаясь ее понять.

Можно ограничиться написанием комментария, но вместо этого давайте улучшим сам код путем выноса условия в понятную переменную.

const isUserPostCreated = users[0] && posts.find(post => post.userId === users[0].id);

if (isUserPostCreated) {
  showUserPost();
}

А если добавилось еще одно условие? Создадим еще одну переменную.

const isUserPostCreated = users[0] && posts.find(post => post.userId === users[0].id)
const isReaderLoggedIn = getReaderFromDatabase().isLoggedIn();

if (isUserPostCreated && isReaderLoggedIn) {
  showUserPost();
}

Прочитав этот код в будущем, вы сразу поймете, что в нем происходит.

Создание функций с единственной ответственностью

Каюсь, я виновен в написании функций init() с сотнями строк кода, делающих множество вещей. Такие функции просто создавать, но, к сожалению, в будущем такой код будет совершенно не гибким.

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

Давайте рассмотрим пример проверки имени пользователя.

function validateUsername(username) {
  // Invalid if username is over 20 characters.
  if (username.length > 20) {
    return false;
  }

  // Invalid if username has non-alphanumeric characters.
  if (/[^a-z0-9]/gi.test(username)) {
    return false;
  }

  // Invalid if user already exists in database.
  if (db.query('SELECT id FROM users WHERE username = ', username)) {
    return false;
  }

  // Otherwise valid!
  return true;
}

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

Мы можем разбить эту функцию на более мелкие.

function validateUsernameLength(username) {
  return username.length <= 20;
}

function validateAlphanumeric(string) {
  return !/[^a-z0-9]/gi.test(string);
}

function checkUsernameExists(username) {
  return db.query('SELECT id FROM users WHERE username = ', username);
}

function validateUsername(username) {
  const isLengthValid = validateUsernameLength(username);
  const isAlphanumeric = validateAlphanumeric(username);
  const isUsernameTaken = checkUsernameExists(username);
  return isLengthValid && isAlphanumeric && !isUsernameTaken;
}

Теперь эти функции легче менять, перемещать и тестировать.

Вы будете благодарны себе за все это

А также вам будет благодарен каждый, кому придется работать с написанным вами кодом.

ОСТАВЬТЕ ОТВЕТ

Please enter your comment!
Please enter your name here