Как выявить дублирование кода?

Нет ничего плохого в том, что код дублируется. Но с небольшой оговоркой — он не должен дублировать знания. Поэтому, чтобы ответить на этот вопрос, сперва необходимо выяснить, является ли дублирование крода дублированием знаний. Это можно легко сделать проверив код на соответствие принципу единой ответственности (single-responsibility principle), который гласит (примерно): "функция должна иметь ровно одну причину для изменения".

Рассмотрим такой пример:

const validateFirstName = value => {
  notEmpty(value);
  lessThen100(value);
}

const validateLastName = value => {
  notEmpty(value);
  lessThen100(value);
}

Здесь мы видим две функции, которые, предположим, предназначены для валидации имени и фамилии при регистрации пользователя. Неопытным взглядом может показаться, что у нас тут одинаковые проверки и было бы неплохо вынести их в одно место, чтобы не дублировать код. Сказано — сделано:

// теперь код не дублируется и вынесен в отдельную функцию
const validateString = str => {
  notEmpty(str);
  lessThen100(str);
}

const validateFirstName = value => {
  validateString(value);
}

const validateLastName = value => {
  validateString(value);
}

Отлично? Сейчас выясним. Представьте, что от бизнеса пришло требование — проверять, чтобы фамилия состояла строго из одного слова. Можем ли мы внести это изменени, затронув только одну функцию? Почему бы и да:

const validateString = str => {
  notEmpty(str);
  lessThen100(str);
}

const validateFirstName = value => {
  validateString(value);
}

const validateLastName = value => {
  validateString(value);
  theOnlyWord(value); // <= новая проверка
}

Отлично? Выясняем дальше. Теперь бизнес пришёл с новой задачей — увеличить возможную длину фамилии до 150 символов. Т.к. проверка длины находится в функции validateString, которая в том числе участвует в валидации firstName, то мы уже не можем поменять код так, чтобы он затрагивал только валидациюlastName. Нам в любом случае придётся менять код так, что потребуются правки функции validateFirstName.

Соответственно, мы не смогли поменять валидацию одного поля, не "задев" — другое. Проверка на SRPне пройдена. Значит, такой код не является дублированием знаний и имеет полное право оставаться в исходном виде:

const validateFirstName = value => {
  notEmpty(value);
  lessThen100(value);
}

const validateLastName = value => {
  notEmpty(value);
  lessThen100(value);
}

Вместо заключения

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

P.S. Нередко, когда я вижу дублирование, то ловлю себя на мысли, что его не стоит куда-то выносить. Для себя я объясняю это тем, что данный код невозможно выразить в виде абстракции, поэтому он намеренно дублируется. Благодаря этой книге я теперь могу спать спокойно, потому что нашёл своим действиям логическое обоснование.