Как стать автором
Поиск
Написать публикацию
Обновить

Ассиметрия в ревью

Время на прочтение5 мин
Количество просмотров393

Как-то в одной известной компании решили упразднить процесс под названием "код-ревью". Упразднили не на уровне компании, а на уровне отдела, но в итоге упразднили не процесс, а того самого менеджера (по офицальный версии причины были другие, но мы-то знаем). Процесс (код-ревью), как видно, полезный, если способен пережить одного менеджера, но видно, вопросы к нему имеются. Код-ревью представляет собой взаимодейсвие автора пулл реквеста и ревьюера, что-то типа игры с ненулевой суммой, где та самая сумма аккумулируется между участниками процесса.

Фикс критической ошибки (не архитектурной или по дизайну системы, а именно ошибки/баги) не добавляет профита ревьюеру. При удачной фиче похвала и бонус пойдут автору. То есть действия ревьюрера приносят ноль ему и что-то автору. Хотя ревьюер может не только помочь, но и навредить - заблочить пул реквест (и иногда действительно такое случается), бизнес невилирует отрицательные последствия - вмержат только в путь, когда бабло начнет течь мимо кассы. Не беря во внимание совесть ревьюера, получить максимальный профит можно на дейликах (ведь для этого утренние созвоны и нужны, так ведь?) - аккурат перед мержем пулл реквеста упомянуть о критической неисправности.

Ревью это про чтение кода - выбор золотой середины между мыслями автора и доходчивостью ревьюера. Последний может попросить скомпоновать код попроще, что скорее всего заставит автора выгрузить из оперативной памяти смысловые блоки под запись в файл. Данную аргументацию дополняет еще и условие: пишем раз, читаем много раз. Если ревьюеру хочется сделать фичу самому - лучше сделать ее самому, а если уже поздно - лучше не бурчать, а понять и простить. Но никто же не просит устранять неэффективность (говнокод) без удовольствия, так? Предлагаю рассмотреть примеры кода на языке "тайпскрипт", хотя идеи ниже могут быть применимы к любому языку программирования.

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

Кого не спроси, каждый за иммутабельность, каждая книга вдалбливает идеальные неизменяемые модели. Так и есть? Да. Но вот порой код ну пишется так - прямо, четко, последовательно: создали обьект и добавили поле по условию. Ведь ну создали, и добавили по условию - все просто, да?

const authResponse: AuthResponse = { principalId };
if (effect && resource) {
  authResponse.policyDocument = policyDocument;
}
authResponse.context = context;
return authResponse; 

Все ок, только перед мержем пулл реквеста убираем мутабельность, и будет супер.

return {
  principalId,
  context,
  ...(effect && resource ? {policyDocument} : {}),
}

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

// merge articles and galleries into a single array and organise by date
const contents = [...(articles ? articles : []), ...(galleries ? galleries : [])]
  .sort((a, b) => (b.displayDate || b.publishDate || new Date()).getTime() - (a.displayDate || a.publishDate || new Date()).getTime())
  .slice(0, 160); // limit to 160 items

Выкидываем комменты (включая первую строку, которая добавляет ровно ноль вэлью), упрощаем код и фиксим баг (который, если честно, не так легко заметить до рефакторинга):

const WINDOW_SIZE = 160;
const now = Date.now();
const selector = (d) => (d.displayDate ?? d.publishDate)?.getTime() ?? now;
const contents = [...(articles ?? []), ...(galleries ?? [])]
  .sort((x, y) => selector(y) - selector(x))
  .slice(0, WINDOW_SIZE);

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

// Call this after redirect from Authentik to complete login
export async function handleRedirectCallback() {
  await userManager.signinRedirectCallback();
  window.location.replace("/"); // Redirect to the home page 
}

Не рассматривая относительную ценность комментария ниже, обычно если хочется что-то написать про блок кода (for-loop), лучше вынести его (блок) в отдельную функцию и придумать ей шикарное имя.

// For every button 'select article' select an article from those available
for (let i = 0; i < params.numberOfArticles; i++) {
  await params.channelPage.channelArticlePlacementSelect.nth(i).click();
  ...
}

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

// The type of the button will be 'ButtonView', just as in the articles or galleries.
const button = makeGreatButtonAgain(id, props);

Венцом комментно-ориентированного программирования есть прецедент увеличения кол-ва инструкций из-за неброского коммента (вместо одной переменной сделать две, но не поняв какие имена им дать, воспользоваться услугой переприсвоения). Там же вопрос к имени этой переменной - немного плохой тон зашивать тип в имя (см. dateString). Аналогия как с интерфейсом и классами - лучше переменная с интерфейсом, чем с конкретным классом (Set лучше чем НashSet или TreeSet).

const GroupedContentDate: Component<IGroupedContentDateProps> = (props) => {
  const dateTime = formatISO(props.displayDate);
  let dateString = `${formatTime(props.displayDate)} Uhr`; // base date string (used also for today's articles)
  dateString = `${formatDate(props.displayDate)}, ${dateString}`;

Убираем let, заменяем имена переменным:

const GroupedContentDate: Component<IGroupedContentDateProps> = (props) => {
  const displayDate = formatISO(props.displayDate);
  const dateTime = `${formatDate(props.displayDate)}, ${formatTime(props.displayDate)} Uhr`;

Следующая зарисовка не только про window.location (которая повторяется четыре раза), но и про ошибку компоновки - очень не желательно совмещать предикат (который функция, возвращающая булевый результат) с действием (которое почти всегда "сайд-эффект"). Предикат лучше делать "чистой" функцией, так как легче тестить.

async function redirectIfFromAuthentik(): Promise<void> {
  if (
    window.location.pathname === "/oauth2/idpresponse" &&
    window.location.search.includes("code") &&
    window.location.search.includes("state")
  ) {
    await userManager.signinCallback();
    window.location.replace("/");
  }
}

Выносим условие в отдельную функцию (и пишем тесты, если захотим):

type IsFromAuthentikParams = {search: string; pathname: string};
const isFromAuthentik = ({search, pathname}: IsFromAuthentikParams): boolean => 
  pathname === "/oauth2/idpresponse" && ["code", "state"].every(x => search.includes(x));

В заключение чуть-чуть про фидбек, который получаем (мы ведь не только ревьюим, да?) на код-ревью - блин, ну какую же чушь иногда пишут люди :) - термины вида "идемпотентный" почему-то триггерят похожие фразы: "мы так сделали в другом файле два дня назад" и т.д. Удачи вам на код-ревью, и делайте хороший продукт, когда вам захочется, а когда не хочется - принимаем фразы на экране не близко к сердцу, потому что комментатор общается с буквами на экране, и в лучшем случае строчками в блокноте.

Теги:
Хабы:
-4
Комментарии2

Публикации

Ближайшие события