Среди множества задач, решаемых программистом, одной из самых частых является подумать и добавить новый функционал в приложение. И там уже может быть много похожего функционала, он может быть хорошо написан, хорошо документирован. Искушение заключается в том, поддаться своей лени и задействовав специфические мышцы левой руки поместить чужой код в "новый" функционал. Другими словами, очень хорошо спроектированный код (равно как и очень плохой) готовит к разработке методом копипасты последующие поколения разработчиков. То есть мы находим в нашем коде что-то похожее на то, что мы хотим добавить, копируем это и вставляем, меняя небольшие детали, специфичные для нашей новой функции. Или находим что-то похожее на stackoverflow. Я отнюдь не против поиска и даже отдельных моментов разработки методом анализа чужих решений, в любом случае код придется понять, принести, адаптировать. Да чего греха таить, я тоже не пишу весь код из головы.
Намного хуже, когда это входит в привычку, которая отключает мозг, на автомате выполняя мелкие изменения. Есть очень хороший принцип DRY (Don't Repeat Yourself), то есть в разработке стараемся избегать дублирования кода, по крайней мере, когда это разумно. Много раз обсуждали этот момент с коллегами, и сошлись на том, что это больше про самодисциплину в разработке, чем про кодинг.
Если приходится копировать логику больше двух раз, хорошо трех, значит мы где-то накосячили с архитектурой, значит это будет техдолгом, который когда-то придется оплачивать, значит ошибка в этом месте будет продублирована еще два или три раза. Скорее всего в проектах, где функциональность стоит на первом месте, а разработке ведется по дендро-фекальным технологиям трудно избежать копирования, стоит ли бороться с копипастой каждый решает сам, только легкость копипасты существующего кода все сильнее будет вас затягивать в ловушки легкой разработки.
Ловушка легких решений
Нас попросили добавить новое поле в объект, или свойство. К счастью, в объекте много полей, много свойств, и все они в какой-то степени похожи, за исключением некоторых специфических частей новой логики. Вы расширяли функционал таким способом?
Ловушка заключается в том, чтобы просто скопировать и вставить существующий код, как сделали другие разработчики до нас, игнорировать документацию и не пытаться понять структуру модуля. Если нет четкого понимания вносимых изменений, уверенность что этот код работал до нас, не дает нормально разобраться как оно работает. Время поджимает и надо побыстрее закрыть таску и взять другую. Работа сделана, код работает, тесты проходят.
Ловушка мертвого кода
С кодом всегда сложно разбираться, с чужим кодом особенно. Даже если это три строки, даже если вы пишите print("Hello world"). Вы задумывались, как это выводится на экран? Там в асме колстек из 20 вызовов. Это отлично, что я не вижу эти 20 вызовов, что от меня скрыли сложность работы с ОС, что я могу одной строкой кода сделать то, что мне действительно надо.
Но это не снимает с меня необходимости в общих чертах понимать колстек вызовов и код, который скопипастил. А код надо понимать, потому что чужой код может быть мертвым. Мертвый код, одна из причин избегать копипасты, скопированное может содержать часть, которая не нужна в вашем случае, но код цепляется друг за друга, вы не можете скопировать часть слов, и выбросить другую часть, оно просто не будет работать.
Самое забавное, что это осознание обычно приходит сильно позже внедрения каких то зависимостей, так было и с моей задачей, которая требовала подключения библиотеки
eigen для выполнения специфичных операций с матрицами. Из всей библиотеки, а там их более полутора тысяч, нам нужно было 4 функции. Но зависимость классов не позволяла их просто выдернуть и использовать. Более того, немного пошерстив нашу библиотеку математики я нашел похожую реализацию, которую надо было немного поправить. А ведь все могло закончиться втаскиванием ненужного фреймворка на 10к файлов. Более того, я потом полез смотреть реализацию в eigen и оказалось, что и там функция разложения Якоби (Jacobi SVD) содержит лишние проверки, которых можно избежать, тем самым улучшив перф.
Добавление мертвого или бесполезного кода ненужно, если не сказать вредно. Это делает код в общем более тяжелым. Следующему сотруднику придется больше понимать и дольше разбираться, или он забьет и попадет в ловушку легких решений. К тому же мертвый код не дает ничего, кроме бесполезной траты тактов.
С кодом всегда сложно разбираться, но понимание того, что вы копируете, даже если это смутное понимание для чего используется каждая часть, сделает изменения простыми, а может и вообще заставит отказаться от копипасты.
Ловушка отложенных зависимостей
Скорее всего, вам всё равно придется понять код, который вы скопипастили. У меня еще не было случая, чтобы новая функция в старом коде заработала без ошибок с первого раза, наверное я просто не умею программировать. Почти всегда нужно внести изменения, чтобы они работали и охватывали все кейсы. Но если нет понимания, как работает код, нельзя сказать какие кейсы могут быть. В этом случае тесты ограничены нашим незнанием, и не помогут устранить проблемы нового функционала до их возникновения.
Самая неприятная ситуация - это когда код "вроде как" работает, а потом QA присылают баг, причем падает оно в 90% в другом месте. Тогда приходится возвращаться к скопированному коду, но код уже встроен в основную логику и имеет новые зависимости, приходится разбираться и с ними. Вобщем копипаста это всегда плохо, не думайте что чужой код будет работать у вас без ошибок. Любая написанная строка содержит ошибку, пока не доказано обратное. Не спасают даже юниттесты.
А раз так, если придется разбираться в коде в любом случае, то лучше делать это с самого начала и писать правильно тоже с самого начала. Смотрите на пример, но код пишите руками, не прибегая к комбинациям из трех кнопок. Потратив время на понимание нового кода или модуля, вы расширяете свою экспертизу, а умение писать код без гугла сегодня многого стоит, знание - это сила.
Ловушка чужого решения
Я стал разработчиком, потому что мне нравится решать задачи. Но копипаста это не решение задачи, задачу уже решили до меня, не получится придумать свое решение. Вы можете думать, что подсмотрите чужое решение и напишите свое, но это не так. Вы отравлены знанием чужого решения, это как посмотреть в конец учебника и потом подгонять свое под ответ - довольно глупое занятие. Конечно, не найдя своего, пусть не полностью правильного решения, все равно придется идти в гугл за ответом, но это будет позже, когда серые клетки поработают.
Мне нравится программировать, особенно нравится понимать как работают сложные системы. Копипаста лишает меня главного - процесса понимания как устроена система. Тренирует мышечную память, а не серые клеточки. Если вы понимаете код, который вы копируете, то лучше напишите его сами, так вы сможете лучше понять функциональные возможности нового кода. Это также дает вам возможности для поиска общего кода в рамках всего проекта. Код, который вы написали сами откладывается в памяти. Код, который скопипастили забудется через пару дней. В какой-то момент объем знаний/памяти будет достаточным, чтобы видеть общие паттерны в рамках всего фреймворка. Если вы копируете код, этого не происходит, и вы остаетесь на уровне пользователя фреймворка, движка, редактора и т.д.
В следующий раз попробуйте "переписать" код, а не копировать его. Почему бы не использовать мозг на полную мощность для понимания кода, а не только мышечную память расположения кнопок C + V.
О чем это я...
В отделе появилась пара ребят, нормальные по знаниям (в теории да), прошли собес, довольно неплохо написали тестовое (всё на удаленке было). Первые месяц-два, как обычно притирка к проекту, к друг-другу, таски-обучалки, мелкий ресерч. Кривая обучения растет, ребята получают все более сложные задачи, пока не начинают их фейлить. Первому задача вроде несложная попалась, собрать вектор скоростей во время движения монстра на уровне, найти усреднение и выдать предсказание на дельту времени из текущей точки. Вторым проходом загнать те же точки в фильтр Калмана, посмотреть какое из предсказаний точнее и отдать команду на стрельбу с упреждением. Проходит 10 ревью, вижу что человек не справляется, вроде и код тот что нужен, но все время какие-то огрехи, которые не позволяют его пустить дальше в репо. Сажусь рядом, говорю - "Рисуй на листочке как бегает монстр, рисуй стрелки куда направлена скорость, пиши рядом псевдокод алгоритма". Человек впал в ступор, т.е. ручку то он взял, но дальше рисования пары точек дело не шло. Сидим два часа, разговариваем, пытаюсь получить хоть какие-то наброски алгоритма, идею написать на листочке уже оставил как несбыточную, думаю ну ладно, я сам нечасто на листке что-то решаю уже, что же я от молодых требую? Пока это "чудо" не спрашивает - "А можно я в гугле посмотрю решение?". В первых двух нормальных ссылках я вижу код, который мне приходил на ревью: Здравствуйте, приехали. До меня дошло почему не справлялся конкретно этот коллега, решения этой задачи нет, надо собирать по частям. Но с таким еще можно жить, ведь надо сформулировать вопрос, отсеять лишнее, найти решение и адаптировать, т.е. работы хватает, пусть не свое решение, но потребовалось время и усилия. Надеюсь со временем, количество перейдет в качество и человек начнем думать сам, а не через гугл.
Пошел ко второму, даю туже задачу, даю листок и ручку, говорю: "Пиши, Федя!". Не написал, даже гугл не помог. Со вторым еще хуже, это оказался gpt-программист, думаю дальше рассказывать смысла особо нет. Последней каплей были слова, "А че реджект, работает же".
Ну ок, сегодня работает, а завтра луна не в той фазе будет. Ребята неплохие, но уехали в QA отдел на стажировку и наподумать, думаю не вернутся. И я зря, чтоли, два месяца обучал их получается?
Не подумайте, я не динозавр и пользуюсь chatGPT для автоматизации рутинных задач. Если не можешь объяснить код, который написал, не отдавай его на ревью. Просто кто потом баги в том коде дебажить будет, тоже chatGPT?
Хочу оставить тут ссылку на хорошую статью почему надо понимать, что пишешь. Может кто из переводчиков доберется.
https://philippe.bourgau.net/you-should-refuse-to-develop-what-you-dont-understand/