Когда лучшие практики программного обеспечения нужно выбросить в окно

На днях я написал код, чтобы ответить на вопрос о данных.

Но я сделал все, что в моих силах, чтобы не писать ни строчки кода до тех пор, пока это не стало абсолютно необходимым.

Во-первых, я исчерпал все варианты, не связанные с кодом. Может ли кто-нибудь сказать мне ответ? Есть ли инструмент, который уже существует? Могу ли я вычислить его вручную быстрее, чем это требуется для написания какого-либо автоматизированного кода?

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

На какой вопрос мне нужно было ответить?

Мне нужно было знать: «Сколько из последних 1000 пулл-реквестов содержали только изменения кода Javascript?»

Ответ был важен для меня, потому что я мог проверить проект, который позволил бы нашей команде разработчиков мобильных приложений быстрее отправлять пользователям изменения кода, содержащие только Javascript. Так что, если ответ был только 100/1000 запросов на включение, то у нас не было хорошего варианта использования. Если бы это было 750/1000 пулл-реквестов, то мы говорим!

Я начал с решений без кода

Код — это инструмент, а не подход по умолчанию, который должен использовать инженер при решении проблем.

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

Поскольку в нашей папке src/ был весь наш код Javascript, запрос, который я хотел построить в одном предложении, звучит так: «все запросы на вытягивание, где каждый файл, измененный в PR, находится только в папке src/».

К сожалению, даже с более продвинутым синтаксисом фильтрации Github я не смог найти способ написать запрос, чтобы проверить, все ли файлы в PR находятся в папке src/.

Я спросил коллег о других вариантах

Я начал расспрашивать нескольких коллег о других вариантах. Вот два основных предложения:

  1. использовать API GitHub
  2. использовать интерфейс командной строки GitHub (CLI)

Последний вариант был более привлекательным, поскольку потенциально я мог избежать написания кода!

Но в этот день мне не повезло. Даже с интерфейсом командной строки Github не было очевидной команды для запроса запроса на вытягивание на основе содержимого его изменений в файле.

Время писать код: каков алгоритм?

Мои варианты без кода не сработали. Так что, к сожалению, единственным оставшимся вариантом было написать сценарий!

Я начал с разработки алгоритма. С помощью коллег я перечислил основные шаги, которые мне нужно было автоматизировать:

  1. Запрос последних 1000 запросов на вытягивание с использованием интерфейса командной строки GitHub.
  2. Прокрутите каждый запрос на вытягивание и запустите другой запрос, чтобы получить список всех файлов, измененных в каждом запросе на вытягивание.
  3. Убедитесь, что каждое изменение файла в каждом запросе на вытягивание было только в каталоге src/ (он же файлы только для Javascript).
  4. Поддерживайте счетчик того, сколько запросов на включение только Javascript было найдено, чтобы получить ваш ответ!

Насколько «хорошим» должен быть код?

Неопытные разработчики часто переусердствуют с разработкой решений, когда на самом деле решаемая проблема не оправдывает затраченных усилий.

Чтобы оценить качество сценария, который мне нужно было написать, и избежать чрезмерной инженерии, я задал себе несколько вопросов:

  1. Буду ли я запускать этот скрипт больше одного раза? Нет.
  2. Нужно ли это читать другим инженерам? Нет.
  3. Потребуются ли какие-либо другие инженеры для этого? Нет.
  4. Должен ли он быть достаточно быстрым, чтобы анализировать 10 000 или даже 100 000 запросов на вытягивание? Нет.

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

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

Я собирался написать слепленный сценарий, который мог бы заставить плакать чистых технологов.

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

Я принял эти «архитектурные» решения для своего сценария, потому что что-то более хитрое сделало бы его «плохим» кодом и было бы плохой тратой моего времени. Хороший и плохой код определяется не объемом реализованных передовых практик, а проблемами, которые решает ваш код.

Сценарий

На самом деле оказалось не так уж и плохо. Я потратил на это около 45 минут. Я оставил все операторы отладки, чтобы сохранить то, что я считал «конечным» состоянием.

Для тех, кто заинтересован, две основные команды:

  1. gh pr list --state merged --limit=1000 | awk '{print $1 }' — вы получите список последних 1000 названий PR. Передайте результаты в awk, чтобы получить номера идентификаторов PR (например, PR #125).
  2. gh pr diff ${prNumber} --name-only — на основе номера PR из предыдущего запроса получить список всех имен файлов в PR
import util from "util";
import pLimit from "p-limit";
import minimatch from "minimatch";
import childProcess from "child_process";
const exec = util.promisify(childProcess.exec);

const limit = pLimit(3);
const command = "gh pr list --state merged --limit=1000 | awk '{ print $1 }'";

export const calculate = async () => {
  const { stdout: prString } = await exec(command);
  const prList = prString.trim().split("\n");
  console.log(prList);

  const prFiles = await Promise.all(
    prList.map((prNumber) =>
      limit(() => exec(`gh pr diff ${prNumber} --name-only`))
    )
  );

  const prsWithFiles = prFiles.map(({ stdout }) => stdout.trim().split("\n"));
  // console.log(prsWithFiles);
  const nonNativePrs = prsWithFiles.filter((files) => {
    const isNonNative = files.every((file) => minimatch(file, "src/**/**"));
    console.log(files, isNonNative);

    return isNonNative;
  });

  console.log("count", nonNativePrs.length);
};

Он не очень читабельный, довольно медленный, и я могу выделить 10–20 вещей, которые я бы обновил, если бы требования к этому скрипту когда-либо изменились.

Вы знаете более простой способ ответить на мой вопрос? Может быть, даже тот, который не требует кода? Дайте мне знать!

Заключение

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

Напомним, вот как я определил, сколько кода мне нужно написать в этом примере:

  1. На какой вопрос мне нужно ответить?
  2. Могу ли я найти решения без кода?
  3. Могут ли коллеги помочь мне найти другие решения?
  4. Хорошо, мы должны написать код. Оцените, сколько лучших практик необходимо для сценария.
  5. Стройте без чрезмерной инженерии!

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

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

Иногда нулевой код — лучший код!