В школе я никогда не был хорош в английском. Назовите это тем, что я не интересовался написанием правильного эссе или не интересовался Шекспиром. Урок английского был скорее возможностью вздремнуть, чем учиться. Тем не менее, одна вещь, которую я запомнил на уроках английского, не покидала меня на протяжении всей моей карьеры. Keep It Simple Stupid (или сокращенно KISS). KISS и Don’t Repeat Yourself (DRY) идут рука об руку при программировании. Если вы не повторяетесь, все становится проще. Я поделюсь несколькими вещами, которые стараюсь делать при написании кода, и помечаю их в PR-обзорах, чтобы улучшить качество кода.

Правда не ложь

В начале моей карьеры старший разработчик сказал мне:

"Не проверяйте, ложны ли данные. Это требует дополнительных размышлений по сравнению с проверкой правильности вещей».

Когда у вас есть утверждение if..else или простая логика, вы всегда должны проверять истинность на ложность.

// Don't do
const visibleItems = !showVisibleItems = [] : ['foo', 'bar'];
// Instead check truthy
const visibleItems = showVisibleItems = ['foo', 'bar'] : [];

Это тривиальный пример, но что происходит, когда ваша тройка является возвратом компонента React?

const TextLoader = ({ isLoading, text }) => {
  // Some other component logic...
  return !isLoading
    ? <span>{text}</span>
    : null;
}

Аргумент, который я слышал в пользу этого подхода, заключается в том, чтобы сначала вернуть компонент, но я думаю, что есть лучший способ справиться с этим, чтобы сделать его более читабельным. (Что делать вместо этого, смотрите в следующем разделе)

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

// Stop being negative
const isNonBlocker = false

if (!isNonBlock) {
    return;
}

// Instead stay positive
const isBlocker = true
if (isBlocker) {
    return;
}

Вернуться раньше

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

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

// Don't return a falsey ternary 
const TextLoader = ({ isLoading, text }) => {
  // Some other component logic...
  return !isLoading
    ? <span>{text}</span>
    : <TextSkeleton />;
}

// Early return the loading state
const TextLoader = ({ isLoading, text }) => {
  if (isLoading) {
    return <TextSkeleton />;
  }
  return <span>{text}</span>
}

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

Вспомогательные функции или filter обратные вызовы — еще одно отличное место для упрощения кода с ранним возвратом. Эти типы функций работают хорошо, потому что у вас обычно есть if..else if...else для многократной проверки логики и возврата разных значений. Давайте посмотрим на функцию в моем приложении Remix, которая получает пользовательские данные из токена или перенаправляет на логин.

export const getUserDataOrRedirect = async (request: Request) => {
  const token = await getUserToken(request);
  if (token) {
    return axios.get("/user", { token });
  } else {
    return redirect("/auth/login");
  }
};

Этот фрагмент выглядит не так уж плохо, относительно читаем, и вы можете понять, что происходит с первого взгляда. Пока это нормально, но что происходит, когда нам нужно добавить к этому дополнительную логику? Может быть, мы хотим проверить разрешения. Для этого нам нужно добавить вложенные операторы if, или мы можем использовать ранний возврат, чтобы избежать этого.

// Nested if statements are hard to read...
export const getUserDataOrRedirect = async (request: Request) => {
  const token = await getUserToken(request);
  if (token) {
    const user = await axios.get("/user", { token });
    if (!user) {
     return redirect("/auth/login");
    } else {
      if (user.permissions !== 'owner') {
        return redirect("/error/permissions");
      } else {
        return user
      }
    }
  } else {
    return redirect("/auth/login");
  }
};

// With early returns you can see a clear logic flow
export const getUserDataOrRedirect = async (request: Request) => {
  const token = await getUserToken(request);
  if (!token) {
    return redirect("/auth/login");
  }
  const user = await axios.get("/user", { token });
  if (!user) {
    return redirect("/auth/login");
  }
  if (user.permissions !== 'owner') {
    return redirect("/error/permissions");
  }
  return user
};

Что касается меня, то я трачу намного меньше времени на размышления и отслеживание того, где я нахожусь в примере с ранним возвратом, а не на вложенные обратные вызовы и if..else поток.

Упорядочить свойства в алфавитном порядке

Хранение свойств в алфавитном порядке поможет вам быстрее находить вещи, когда у вас есть большой объект. Это особенно верно для деконструктивных реквизитов в компонентах React и объектов константного типа, используемых для поиска.

Объект ниже содержит всю информацию о дизайне моего магазина Shopify Code Bender.

export const DESIGN_DATA: Record<ProductDesigns, ProductDesignData> = {
  [GITHUB_C_SHARP]: {
    previewDesignPctWidth: 0.6,
    designPctWidth: 0.8,
    phrase: 'c#',
  },
  [GITHUB_GO]: {
    previewDesignPctWidth: 0.6,
    designPctWidth: 0.8,
    phrase: 'go',
  },
  [GITHUB_GOAT]: {
    phrase: 'goat',
  },
  [GITHUB_HTML]: {
    phrase: 'html',
  },
  [GITHUB_JAVA]: {
    phrase: 'java',
  },
  [GITHUB_JS]: {
    previewDesignPctWidth: 0.6,
    designPctWidth: 0.8,
    phrase: 'js',
  },
  [GITHUB_NERD]: {
    phrase: 'nerd',
  },
  [GITHUB_PYTHON]: {
    phrase: 'python',
  },
};

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

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