>
>
>
Вечное сияние чистого Copy-Paste

Андрей Карпов
Статей: 673

Вечное сияние чистого Copy-Paste

Давайте сегодня вспомним о старой доброй статье "Эффект последней строки", написанной более 5 лет назад. Время идёт, но ничего не меняется. В этом нет ничего удивительного. Copy-Paste всё также жесток и беспощаден. Однако, за годы у нашего блога появилось много новых читателей, которые могут быть не знакомы с упомянутой статьёй. Так что сейчас будет минутка воспоминаний и немного дополнений.

Сегодня я встретил упоминание статьи "Эффект последней строки" в твите от Jason Turner. Как видим, тема Copy-Paste жива и продолжает вызывать обсуждения. Поэтому я решил, что стоит стряхнуть пыль с этой публикации и напомнить о ней. Уверен, многие не читали эту старую статью или забыли о ней. А ведь она весьма интересна и забавна.

Суть в следующем. Люди тяготеют допустить ошибку в конце однообразных операций, так как теряют внимание. Одним из таких действий является написание кода с помощью Copy-Paste.

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

Именно эта закономерность с соответствующими примерами и описана в статье "Эффект последней строки".

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

К сожалению, статья писалась, когда анализатор PVS-Studio ещё не умел анализировать C# и Java проекты. Поэтому все приведённые в статье ошибки относятся к языку C или C++.

Сейчас я бы смог привести аналогичные примеры, относящиеся и к другим языкам. Проблематика Copy-Paste универсальна и проявляет везде себя одинаково. Чтобы не быть голословным, приведу по одному примеру для C# и Java.

Пример ошибки, найденной нами в C# проекте AWS SDK for .NET.

if (
  (this.token == JsonToken.ObjectEnd ||
  this.token == JsonToken.ArrayEnd ||
  this.token == JsonToken.String ||    // <=
  this.token == JsonToken.Boolean ||
  this.token == JsonToken.Double ||
  this.token == JsonToken.Int ||
  this.token == JsonToken.UInt ||
  this.token == JsonToken.Long ||
  this.token == JsonToken.ULong ||
  this.token == JsonToken.Null ||
  this.token == JsonToken.String       // <=
  ))
{
  ....
}

Пример ошибки, найденной нами в Java проекте Elasticsearch.

for (int i = 0; i < values.length; i++) {
    if (values[i] == null) continue;
    if (values[i] instanceof String) continue;
    if (values[i] instanceof Text) continue;
    if (values[i] instanceof Long) continue;
    if (values[i] instanceof Integer) continue;
    if (values[i] instanceof Short) continue;
    if (values[i] instanceof Byte) continue;
    if (values[i] instanceof Double) continue;
    if (values[i] instanceof Float) continue;
    if (values[i] instanceof Boolean) continue;   // <=
    if (values[i] instanceof Boolean) continue;   // <=
    throw new IllegalArgumentException(....);
}

Как видите, везде всё одно и то же :). Удивляться этому не приходится, так как опечатки и проблемы неаккуратного Copy-Paste почти не зависят от выбранного языка.

Кстати, тем, кто хочет глубже погрузиться в обсуждаемую тему, предлагаю следующую исследовательскую статью, написанную уже коллективом авторов: "Объяснение эффекта последней строки".

Что теперь делать с этим знанием? Хороший вопрос. У меня есть три соображения:

  • Знайте про этот эффект и расскажите другим. Зная про него, вы станете более аккуратным, заканчивая однотипную работу по написанию кода. Знание - сила!
  • Не ленимся писать функции, шаблонные функции или лямбда-выражения, чтобы сократить дублирование кода. А вот макросы писать не надо. Макросы, это зло.
  • Внедрите регулярное использование статического анализатора PVS-Studio, который очень хорошо находит подобные ошибки.