Pull to refresh

Comments 111

Вспоминается старая проггерская поговорка, "если ваша программа запустилась с первого раза - значит в ней куча багов".

Это волшебное слово такое?

Запомнился один случай из многолетней практики. Запускаю код и он неожиданно выдаёт правильный результат сразу. Что меня очень удивило. Начал разбираться и оказалось — правильный «результат» был выдан потому что тот код который должен был быть протестирован, до не го даже не дошла программа. Не работала та часть которая уже вроде была оттестирована и должна была работать правильно.

Напомнило историю от вузовского преподавателя по программированию. Лабораторная работа: численное вычисление синуса. На турбопаскале. Студент приносит программу, преподаватель смотрит в код и видит там очевидные ошибки. Пытаясь объяснить студенту, почему это всё ерунда, запускает программу, вводит икс — а программа выдаёт верное значение. Вводит другой икс — опять правильный ответ. Ну как так-то, в коде столько ошибок! При внимательном изучении кода оказывается: на последней строчке программы стоит в начале строки 100 пробелов (чтобы выйти за пределы экрана турбопаскаля по умолчанию), а потом написано: writeln(sin(x)).

на последней строчке программы стоит в начале строки 100 пробелов (чтобы выйти за пределы экрана турбопаскаля по умолчанию), а потом написано: writeln(sin(x)).

Это т.н. "заэкранное программирование", популярный студенческий трюк.

Есть почти обратный пример с преподавателем и студентом. На лабе по физике препод уверенно демонстрирует ход работы с измерениями, которые на самом деле показывают совсем не то, что ожидалось. Препод невозмутим и продолжает делать вид, что все идет по плану. Через какое-то время студент замечает, что провода не подцеплены к вольтметру...

Несложные программы на Хаскеле/OCaml/SML/Clean примерно так и работают — запускаются с первого раза и работают правильно. Никакого тестирования, обычно, не требуется.

Но это относится, разумеется, только к относительно несложным программам.

UFO just landed and posted this here

"На самом деле я писал его во время перелета из Чикаго в Хьюстон, с бокалом вина в руке и отказавшись от Wi-Fi. Поэтому мне оставалось только разговаривать со стюардессами (я сижу в первом ряду, так что они здесь крутятся) и вспоминать о своей карьере. " - замотивировали, пошел дальше работать

А потом самолёт тряхнуло и вино переместилось из бокала на клавиатуру...

Про "код — это пассив" — чистая правда. Но как же трудно бывает объяснить это новичкам. Что цель на самом деле — решить задачу, а не писать код. Я думаю, что частично понимаю причины такой ситуации — когда ты молод, позитивен, не "побит жизнью", когда ты всерьёз любишь кодить, когда голова свеженагружена шаблонами проектирования, бест-практиками, книжками, курсами и всем таким прочим по вкусу, то хочется быстрее применить всё это дело на практике. Но до чего это бывает гипер-выражено, это-ж словами не передать. Чем дальше, тем больше начинаю считать, что умелое удаление кода — даже более важный навык, чем его написание.

когда ты всерьёз любишь кодить

В этом и проблема. Лучше любить решать задачи, требующие написания кода, а не кодить ради того чтобы кодить без оглядки на результат

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

Назовем это парадоксом Тетриса )

В Тетрисе как-раз цель - удалять кирпичи :)

Или класть кирпичи так долго, как это возможно. Как посмотреть на это :)

У меня был коллега, которые в штыки воспринимал любые предложения что то изменить, переписать, даже если этого требует задача, долго не понимали почему, пока он не проговорился "ну я же написал этот код, столько времени на него потратил, как же я могу его взять и удалить!".

А я со временем понял, что бОльшее удовольствие получаю, когда выкидываю целые куски своего кода, нежели когда пишу новый ))

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

Тут по разному бывает. Иногда оно правда под запас и правда нужно.

Допустим есть какой-то модуль http клиента. Там есть метод PATCH со своими особенностями реализации. Используется он? Вероятно нет. Нужен? Точно да. Удалять не надо.

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

Нет, это была не внешняя api, это был внутренний сервис с кучей неиспользуемых нигде методов, написанных "про запас". Был бы это API, то дело другое, хотя и тут бы стоило подчистить его от неиспользуемых методов, но их необходимость в этом случае просто сложнее определить, в то время как с внутренним сервисом это очевидно - IDE такие методы подсвечивает и разумно предлагает от них избавиться.

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

Еще и перед начальством за строки кода в день отчитаться можно будет. Плохо что ли? Хорошо!

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

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

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

История гит это слабая отмазка. Там его найти примерно невозможно в реальной жизни.

Жалко расставаться с кодом да? Мертвый код - смело удаляем в отдельном PR с говорящим названием. Не нужен этот код про запас, слабая отмазка)

Я уже много повидал легаси-ужаса, с описанными вами "внутренними библиотеками", которые все боятся удалять потому что "как бы чего не упало", и из-за которых часто поддержка этого легаси превращается в очень муторный, как горячечный бред, ад разработчика. Эти библиотеки не просто висят мертвым грузом - их приходится поддерживать в актуальном состоянии при любых изменениях, которые их могут затронуть, а такое бывает слишком часто, надо поддерживать тесты этих библиотек, если они работают с БД или внешним API, то это тоже надо поддерживать.

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

После введения VCS это его не должно беспокоить — как только они понадобятся, он всегда может их взять.

Примерно это я ему и говорил, он согласился, но явно остался недоволен в итоге, хоть лишний код мы в итоге и почистили :)

Один именитый фотограф новичкам советовал свой путь погружения в фотографию:
Провели съёмку, пришли домой, разобрали фото-материал и поняли - часть это говно - удалите эту часть или лучше всю съёмку и идите снимать по-новому. :)

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

Более сложная задача это объяснить бизнесу что этот код (фича) не нужен, т.к. он слишком переусложняет систему без значительных плюсов для бизнеса.

Но как же трудно бывает объяснить это новичкам.

Есть такое понятие ИКР. :-)

Как кто-то сказал - "лучший код это ненаписанный код".

UFO just landed and posted this here

Это специально сделано, чтобы показать, что что-то может пойти не так.

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

Программирую с 1991-го. Подписываюсь под каждым утверждением автора!

Как это - приятно, когда умные люди так хорошо формулируют твои мысли!

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

если принять модель, что Total - это PretaxTotal + Tax, это приведет к постоянным вычислениям в запросах, где требуется Total

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

мало того - это вообще хороший паттерн - иметь все три суммы в базе в одном месте даже если A+B=C. Альтернатива - 100500 мест где вы делаете этот расчет где-нить на фронтенде. И вот в один прекрасный момент..... появляется tax2 и теперь Total  = PretaxTotal + Tax + Tax2. И вам предстоит спрва найти а потом поменять и проверить все ваши 100500 мест, вместо одного апдейта в базе

Боюсь представить, что будет с автором, когда он узнает про BDD

Утверждения автора, конечно, для ряда случаев справедливы, но как и практически всякие утверждения, имеют ограниченную область применимости.

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

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

  3. Проверять - это правильно, но не всегда на это можно найти время и силы. А потому нередко приходится доверять. Но, таки да, обоснованность доверия следует контролировать.

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

  5. Аналогично п.3, не всегда есть время и ресурсы для сбора доказательств. Так что это правило всегда следует иметь в виду, но необходимо оценивать, принесет ли оно пользу в данном случае.

TDD в силу своей конструкции навязывает восходящий метод проектирования ...

Периодически слышу такое-же мнение, но не могу понять как люди пришли к этому заключению?

Вот я считаю, что 100% использую TDD в варианте "Outside In".

Это когда пишутся только высокоуровние тесты, которые тестируют пользовательские сценарии.

Вот пример стандартного теста (пример на python с pytest, т.к. на java или чем-то подобном будет лишний бойлерплэйт):

def test_answer_to_question(client, pub_sub_mock):
    # given
    question = Question.objects.create(...)
    
    # when
    url = f"/api/v1/questions/{question.id}"
    response = client.put(url, { "answer": "some answer" })

    # then
    assert response.status_code == 200
    question.refresh_from_db() # тут перечитываем вопрос из БД
    assert question.answer == "some answer"
    pub_sub_mock.assert_called_once_with({
        "event": "question.answered",
        "id": question.id
    })

Тест делает следующее:

  • приводим систему в некое начальное состояние

    • сохраняем в БД объект Question

  • выполняем над ней определенное действие

    • отвечаем на вопрос делая PUT запрос в API

  • проверяем только сайд-эффекты

    • API вернул код 200

    • свойство объекта Question изменено в БД

    • Event был отправлен во очередь сообщений

На последнем проеке таких тестов было процентов 95, и покрывали 100% бизнес сценариев. Остальные 5% тесвов покрывали разные технические штуки.

Преимущество такого подхода в том, что вас вообще не интересует имплементация.

А главное - простота рефакторинга, можно просто выбросить текущую имплементацию и переписать заново и тесты не нужно переписывать.

А разве это - тот самый модульный тест, который в TDD должен использоваться по фэн-шую?

Почему вы считаете, что по фэн-шую в TDD должны быть именно модульные тесты?
Я понимаю, что Дядя Боб рассказывает, что TDD это когда вы пишете сначала очень простой тест (за минутку), потом супер простоую имплементацию (опять за минутку) чтобы тест стал зеленым. И повторяете такие же маленькие итерации многократно, потихоньку усложняя тесты и соответственно имплементацию.
Для обучения TDD в домашних условиях — это норм подход, но практиковать его на реальном проекте — боль, т.к. при изменениях слишком много тестов нужно будет постоянно переписывать.

Outside in подход такой — пишете один высокоуровневый тест, который конечно-же не проходит, т.к. нет имплементации, а потом пишете-пишете-пишете имплементацию (хоть час, хоть день, сколько нужно) пока этот тест не станет зеленым.
Получается такое — разработка через тестирование пользовательских историй.

По моему мнению TDD — это когда сначала тест, потом код. И все, больше нет никаких дополнительный условий.

И с другой стороны, почему мой тест не модульный? Я просто тестирую сервис (вместе с БД, очередью, кэшем и т.д) как один независимый модуль.
Ведь что угодно может быть модулем — функция, класс, библиотека, сервис, множество сервисов как единая система.

Хорошо бы было привести в статье описание самого термина TDD. Не все знают, что это такое.

Тест написан до кода? - Написан. Так что под Test-driven подходит. А остальное ваши домыслы

Домыслы это сентенции "пишете один высокоуровневый тест".

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

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

Именно поэтому, то что написано выше, не только не TDD, а просто наивные фантазии.

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

Это как раз легко. Предрассчитанная таблица со всеми краевыми случаями и верными ответами. Идеальный тест, не зависящий от реализации вообще.

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

Это не так, хоть я и не адепт прямо-таки "ортодоксального" TDD "сначала тесты". TDD навязывает писать слабо связанный (coupled) код, который как раз и позволяет разрабатывать и тестировать модули в любой последовательности и независимо друг от друга.

И именно эта слабосвязанность порождает в Java например такую монструозную конструкцию как Spring и всё вокруг неё, превращая приложения из стройного монолитного строения в подобие бесформенной кучи слизи, с залипшими в ней кубиками лего, каждый из которых зато по отдельности может быть взят для тестирования.

Про дублирование в бухучёте (очевидно, двойная запись имелась ввиду) повесилили изрядно.

Скажите, а у человека две ноги тоже для дублирования?

Про дублирование в бухучёте (очевидно, двойная запись имелась ввиду)

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

повесилили изрядно.

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

Согласен, хотел бы первый ваш пункт ещё дополнить:

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

А второй вариант дублирования называется "кэширование", оно бывает очень полезно, но всегда имеет свою цену. Но нужно бы уметь кэшировать (организовывать актуальность и полезность кэша), и нужно чётко понимать, где кэш, а где его первоисточник. Различать их.

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

Работаю в SAP R/3

Система позволяет писать SQL команды в ABAP. Есть два подхода выбирать записи из нескольких таблиц

  1. Выбирать последовательно. т.е. выбрали из A, потом по списку записей из A выбрали данные из Б, потом из В и т.д.

  2. Сделать один мега-запрос через join

Вот только когда поступает от заказчика вопрос вида: а почему не выбирается такая та строка из такой-то таблицы по таким условиям, то разработчиков, которые выбрали способ 2 хочет просто убить. Да, вы умный разработчик, вы можете написать JOIN с 2+ таблицами в одном запросе. Но вот чтобы потом определить это "почему" приходится раскручивать этот запрос вручную. А если там ещё и данных много, то этот процесс затягивается.

А при пункте 1 это делается достаточно быстро.

Поэтому меньше кода - не всегда лучше.

Может я что-то не понял, с SAP не знаком, но вы серьезно считаете, что выполнить 1 + А запросов лучше чем 1? А если есть В, то вообще жесть. Про Г я молчу. Или в САПе какой то другой SQL?

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

P.S. Еще есть нюанс, в SAP все "старые"(т.е. 90%) названий таблиц и полей "обфусцированы", т.е. имеют длину в 4-5 символов и это не сокращение на английском, а больше похоже на рандомный набор букв (что например по вашему может значить поле loekz?). Это несколько затрудняет чтение запросов, если с конкретным модулем не сильно часто работаешь.

выполнить 1 + А запросов лучше чем 1?

Да. Особенности работы в SAP.

Если запрос выполняется меньше 30 секунд, то особой разницы в выполнении 1-го или 1+A запросов нет.

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

При этом удобно потом в отладчике искать "почему не выбралось нужное". А вот если у вас 1 запрос, который больше 10 минут работает (а это распространенная ситуация) и тащит сразу из 1+A таблиц, то поиск ответа становится реально болью. Так что более длинный код помогает потом в сопровождении.

Или в САПе какой то другой SQL?

Просто там объемы данных большие. К примеру была ситуация когда запрос падал в дамп потому что что был исчерпан лимит времени ответа от сервера БД. Вызывалась команда вида "DELETE FROМ table WHERE ..." и эта команда через 20 минут роняла сервер приложений в дамп потому что это был предельный лимит выполнения команды на сервере БД. А сервер БД просто не успевал за 20 минут выполнить команду. Пришлось разбивать её по полю на несколько отдельных команд.

Много данных, плохой код или плохая БД?

в общем случае в MS SQL, delete при удалении существенного количества записей падает в табличную блокировку, что на активной рабочей системе приводит к мертвому подвисанию .

Секреты как этого избежать тоже известны, но тут нет иного SQL, просто здравый смысл

В данном случае много данных. И в большинстве случаев это основная причина тормозов в SAP.

Это совсем немного. А еще с учетом, что SAP это ERP и там наверняка партишионинг должен работать с данными 3+ лет давности, это вообще копейки.

В рейтерсе в оракле где хранились данные по товарным рынкам было 4 триллиона записей (в 2016).

Не нужно убивать разработчиков. Нужно уметь работать с SQL :)
Если серьезно, то однозначно JOIN c N таблицами единственно правильное решение.

JOIN плох не потому что непонятен (как раз наоборот с ним более приятно чтение кода), но он создаёт головную боль при поиске ошибки в данных.

В ABAP разница между 1 и 1+N запросом несущественна. Само собой если это не делается в цикле.

А в некоторых случаях и вообще быстрее получается вместо JOIN делать два запроса. Потом сортировать одни данные и пробегать в цикле по вторым данным, читая из отсортированной внутренней таблице по отсортированным полям.

Это вот еще одна догма со времен двузвенных систем, серверов на 486 процессоре и коаксиальных сетей: "не дай бог сделать лишний запрос или вытянуть из БД хоть одну ненужную запись или колонку". Миллион лишних записей зря тянуть в приложение, конечно, не стоит, но сейчас часто практичней забрать из БД в разумных объемах что-то ненужное и обработать уже в приложении, чем лезть из кожи вон обязательно засовывая всю логику запроса в одно SQL выражение.

Чем практичнее? Чем дольше читаю, тем больше кажется что разрабы сап живут в том же мире, что и 1Cники

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

Вот тут я бы поспорил, смотреть результаты запроса в отладчике - то ещё удовольствие. SQL вы всегда можете запустить в какой-нибудь консоли и играться с ним сколько угодно. Кроме того, обратывать ручками вы себе можете позволить только если точно знаете сколько у вас данных и их совсем немного.

Не знаю как в 1C, но в SAP нет никакой консоли. Вы пишите в системе разработки (где данных может и не быть вовсе), потом несете в тестовую систему (где данные тоже могут только примерно соответствовать реальным), а потом в продуктив. И частенько бывает, что то, что у вас в разработке летало, в тесте немного притормаживает, а в продуктиве просто становится колом. Потому что в продуктиве данные вот такие, на которые запрос не был готов.

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

Как страшно жить, сочувствую

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

Залез в код и охренел - там для каждого документа, подгружались из базы все связанные с ним данные, причем подгружались они в виде целых таблиц. Т.е. если в документе есть адрес, к примеру, то грузились все адреса, и искался нужный по id тупым перебором. В итоге уже при пяти доках и более-менее заполненной БД, форма могла минут пять открываться, конечно сети и железо тогда были медленнее чем сейчас, но думаю и на современном железе такой замечательный код тупил бы жестко. Разумеется переписал все это барахло на простой запрос, с несколькими джоинами, и форма стала летать даже на огромном количестве документов (там и отображалось-то пяток полей этого документа).

Часто все эти переборы пишут не от большого ума/опыта, просто по другому не умеют.

Дело не в том, что сейчас производительность сетей и серверов позволяет делать 100500 запросов со скоростью одного 30 лет назад, а в том, что СУБД имеют более эффективные механизмы работы с данными, и использовать Oracle как Excel-таблицу несколько глупо.

В случае обработки данных преимущественно на стороне клиента вы упираетесь в общую производительность системы быстрее, чем при обработке на стороне СУБД.
Особенно это относится к OLAP-задачам.

Я бы сказал - код надо писать настолько простым, насколько это возможно. И чем больше опыта, тем проще и понятнее он получается.

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

Вот да, всё так и есть. Я поэтому стараюсь новичкам привить мысль, что в процессе разработки всегда нужно задумываться — а зачем именно мы делаем то, что делаем? Какова цель написания данного кода? Но всё равно, не всегда получается достигнуть понимания. Даёшь, допустим, несложную задачку на пару часов (ну там, скриптик написать, перекладывающий поток данных из COM-порта в сокет). Через неделю интересуешься, как там дела; отвечают, что нужно ещё полгода разработки ) Ээ, окей, а зачем? А вот, чтобы построить универсальную расширяемую архитектуру, красиво всё на паттернах запилить, и всё в таком духе... В очередной раз пытаешься объяснить, что здесь всё это совершенно не нужно, потому что задача вообще не в этом, а нужен простой скриптик на пол-страницы. На этом моменте, бывает, даже обижаются, что я ничего не понимаю и рушу их идеалы )

И ещё вот что интересное замечаю. Многие, видя большой, сложный, запутанный код, проникаются уважением к автору — мозг! Во какое написал! (разбираться в этом мы, конечно, не будем)). А когда встречают простой, короткий и понятный код от более опытного коллеги, думают — банальщина, неинтересно. И, наверное, так получается, что подсознательно хочется быть похожим на "мозга", чтобы и от твоего кода все "ахали".

Разработка простого кода зачастую требует больше мозговых усилий, чем сложного :)

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

Это еще часто от того, что запилить какой-то простой и компактный монолитик, на типовом фреймворке - это банально, скучно, да и в резюме после него особо писать нечего. А вот если ты для той-же задачи зафигачишь 30 микросервисов с 60-ю БД, обменом по grpc, Кафкой, Кассандрой, Эластиком, Кейклоком и кучей других страшных слов, то это и развлечение поинтереснее и в резюме все эти страшные слова можно писать смело - ты с ними уже поигрался.

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

Всё бы хорошо с минимальными формулами, но при малейшем изменении бизнес-требований такие формулы потребуют переделки. Тут нужен опыт, чтобы заранее подумать, какие требования могут измениться и не оптимизировать формулу в этом месте. Можно было бы это назвать "достаточным уровнем оптимизации".

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

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

Одна вещь, которую я понял за много лет программирования - все всегда не так как нам кажется.

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

Видимо мне очень далеко до гуру с 20 летним стажем, поэтому очень тяжело понять что означает эта фраза.

Думал, что если посмотрю оригинал, то станет понятнее. Не стало.

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

Именно про этот кусок могу сказать, что очень часто копи-паст более читаем и часто при изменении одного куска другой кусок с него скопипасчен НЕ ПОДЛЕЖИТ изменению. А елси подлежит — да надо создать метод.

Глобальной переменной присваивается значение в теле программы, а затем (возможно) повторно присваивается значение из файла конфигурации.

Что ему тут не нравится? А если нет файла конфигурации, или в нём нет искомого значения?

По поводу объёма кода давно решил для себя так: «Мой код — мой враг». Или, другими словами, код — это необходимое зло для решения задачи. А количество зла надо минимизировать.

На работе я заметил, что некоторые задачи хоть и сложные, но не приводят к усталости. А другие задачи простые, «обезьяньи», но после рабочего дня вымотан так, что на следующий день хоть не выходи на работу. Долго пытался найти закономерность, и нашёл — дело в нагрузке на память (для меня нагрузка на память чрезвычайно стрессовая и изматывающая), а нагрузка на память напрямую связана с объёмом кода, с которым я работаю. Если весь день ковырял 100 строк кода, то всё норм. Ковырял 10 тысяч строк — устал, что ноги подкашиваются.

Помимо нагрузки на память большой объём кода снижает скорость дальнейшей разработки. Реализацию многих функций можно условно разделить на 2 этапа: 1) рефакторинг имеющегося кода для того, чтобы новая функция реализовалась максимально просто и естественно 2) собственно реализация новой функции. Т.е., грубо говоря, новую функцию можно реализовать за день, но на подходящей для этого кодовой базе. А сделать кодовую базу подходящей — это рефакторинг, который может занимать несколько дней. И вот время время этого рефакторинга как раз связано с объёмом кодовой базы (больше кода — медленнее рефакторинг).

Размер кода не важен. Важна его читаемость. Если у вас есть код, который занимает три строчки, и вы на нем зависаете на минуту и другой в 10 строчек — который вы понимаете за 10 секунд, то какой вариант вы выберете для дальнейшего изменения через 5 лет?

Речь про объём кода шла «при прочих равных условиях».

Любопытства ради - с каким размером кодовой базы вы работаете. Можете померять через tokei

Ну да, зачем нам быстрая работа приложения, зато денормализацию БД не провели и потешили своё эго.)

В остальном советы уровня "прочти любую книгу о программировании".

Немного надоедает слышать про копипасту и изменения.

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

В целом, сам подход, при котором мы в одном месте меняем и все где-то как-то само происходит - таит в себе кучу проблем. Нет уж - дублируйте код, делайте копипасту, пока вы точно не уверены, что понимаете, как работать с "общими" компонентами.

Соглашаюсь и плюсую. Я не был бы так однозначен по поводу копипаста. Биллиард раз видел и продолжаю видеть, как ради того чтобы не копипастить всего лишь в одном месте пару строчек кода наворачивают полдюжины сущностей и зависимостей в которых через неделю уже черт ногу сломит. Потому что кто-то когда-то услышал, что копипаста зло и вообще DRY. Любому принципу не стоит следовать как тот не слишком умный человек из поговорки, которого молиться научили.

printf("Hello World!"); 

Как вы думаете, сколько всего может пойти не так?

  • Не станет ли эта волшебная строка впоследствии проблемой?

Респектую статье и привожу пример 2019 года, когда более года тушили corrupt memory, который проявил себя из-за sprintf(). Я кстати так и не понял, точно ли этот баг окончательно поправили?..

ТДД - это тесты. Тесты - это код. Кода должно быть меньше. Напрашивается очевидное противоречие.

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

Мне кажется что первое правило копипасты надо разделить на 2 правила. Первое правило - это понимать как работает то, что ты копируешь. Второе - дублирование кода

И вот к дублированию как невероятному злу у меня большая претензия:

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

  • Во вторых бывает "правильное дублирование". Дядя боб, на которого есть ссылка в начале статьи часто об этом пишет. То есть код совпал случайно - и не нужно делать наследование или композицию в таком случае

  • В третьих если код написан через TDD и хорошечно покрыт интеграционными тестами - то ошибка если забыл исправить во втором месте не сильно вероятна

В третьих если код написан через TDD и хорошечно покрыт интеграционными тестами - то ошибка если забыл исправить во втором месте не сильно вероятна

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

По тексту вместо "журнала" имеется в виду "логи", либо "журнал логирования"

Нет, автор статьи всего лишь перевел слово log на русский язык, вместо того, чтобы использовать профессиональный жаргон. Имеет право.

Если смысл утерян - это плохой перевод. Односложно "журнал" смысл не передает.

Передача смысла - это субъективный процесс. И лично для меня смысл вполне сохранился.

Sign up to leave a comment.