«Занятное решение. Но очень медленное, к сожалению.»
Да, есть такое. Но это было первое решение. Я немного переделал движок. Дело в том, что если один и тот же объект нужен не одному тесту, то не обязательно его убивать и снова вытягивать. Таблицы только чистить.
Да самому стыдно за движок — велосипеды сочиняю :)) Но какого-то готового решения не нашел. Может плохо искал. Майкрософтовский вместе со студией стоит дорого. Не захотели на каждого разработчика покупать такую версию студии.
Здесь описал движок, как пример, что при отсутствии инструмента — не так сложно его сделать.
«Вопрос в философии. Когда мы меняем код, мы меняем алгоритм. Когда мы меняем базу, мы меняем уже существующие данные.»
Конечно. Если бы у вас не было БД, а вы хранили данные в файлах, например. И писали только на шарпе (или джаве и т.д.). Такой идеал, как автор предлагает. И запустили систему в продакт. А потом нужен рефакторинг кода. Упс — та же проблема. Если информация утеряна, ее из космоса получить невозможно.
Даже если у вас проект с БД и вы выкатили в продакт, а потом рефакторите код, и он затрагивает объекты, которые уже хранятся в БД — та же проблема. Потеря данных.
Если же вы не выкатывали ничего в продакт, а находитесь как положено на этапе разработки системы, то рефакторить можно и то и другое.
Алгоритмы работают с данными. Если у вас такой случай, что не надо трогать данные — то отлично, рефакторите без проблем. А если надо, то СУБД в этом не виновна. Эти проблемы будут всё равно.
«Вы с ним работали? Я работал. Никакого «билда» в смысле компиляции там нет, тамошний «билд» — это расчет скриптов изменений.»
немного. Я «олдскульный». Всё скриптами пользуюсь ))
Но «билд» — замечательная вещь. Конечно, там нет компиляции, потому что и не может для SQL-SERVER компиляции. Для скриптового языка. Но расчет скриптов изменений — это то, что нужно. Он позволяет указать любые ошибки. Вот как раз проверить все запросы на предмет соотвествия именам таблиц и колонкам.
«Интересно, как ваш «движок» изолирует тестируемый код от его зависимостей»
Тестовый движок, это отдельная база данных. Состоит из нескольких несложных хранимок. Сам тест — хранимая процедура. Этому движку указывается, к какой базе данных нужно приконектиться. И она вытягивает в свою базу требуемые для теста таблицы (пустые, только структура), хранимки, функции и т.д.
Тест начинается с указания какие таблицы нужны. Далее заполнение тестовыми данными. И прогон тестов.
Всё просто. На самом деле кода не много. А развязка от тестируемой базы неплохая. Тесты это хорошо выручают.
Но тесты эти тестируют только код. Например, зависимости между таблицами не вытягиваются. Декларативную часть бессмысленно тестировать.
«Не стоит? Ну если для вас двух-трехкратная разница в производительности работы сотрудника не стоит выбора технологии, то я даже не знаю, что его стоит»
Конечно, важна производительность сотрудника. Я только говорю о том, что часто переоценивают проблемы. Эти проблемы решаются разово и тратится на них время не такое большое. Программисты мы или кто? У нас в конторе например, выбирали ORM и боялись, что в некоторых вариантах не генерировались классы по БД. Объяснил как, и дело пошло. Людям казалось, что это сложно. А оказывается, совсем нет.
«Угу. Спасибо за предложение заниматься интеграционными тестами. Проблема в том, что это — дороже, чем изолированные.»
Дело не в интеграционных тестах. И не в дороговизне их. Тесты и интеграционные и модульные нужны. Но вы этот сервер можете хоть вручную создать и разворачивать, а также прогонять все тесты (не только интеграционные). Тесты на БД тоже можно без проблем прогонять отдельно.
«А вообще, ваш пост — хорошая демонстрация того, почему базу рефакторить не так же просто, как и код. „
Тут другая тема есть. Кода на СУБД меньше. Следовательно и рефакторинг в этом плане будет проще. Если данные потеряны, то сложно будет и в слое над БД. Если нет, то рефакторинг в БД не сложный. Даже не знаю, как кратко описать. О рефакторинге кода писал Фаулер. Там всё строится почти на выносе метода или выносе класса. В БД — вынос сущности (не только это, конечно, но чаще всего). Была колонка, стала отдельной сущностью. Всё это — декларативное описание. Как по мне, и работы меньше.
“Я вижу это каждый день, имея возможность сравнить стоимость типичных рефакторингов в .net-команде и sql-команде. Во второй их практически нет.»
я и sql-команд не видел. Мало из базовиков вообще с программированием нормально знакомы. А уж с понятием рефакторинга или юнит-тестов, тем более. Это проблемы профессионализма, которые остро стоят в нашей отрасли. Берем тех, кто вообще что-то может.
Сам подход — моделирование в БД ничем не хуже моделирования в шарпе. Даже лучше иногда. Чем код создания таблиц и связей так плох по сравнению с другим языком? Если вопрос только в инструменте — то это мелко. Я не базовик, там есть разные не известные мне инструменты.
ERwin например. Базовики пользуются и хвалят. Не знаю его возможностей.
VS-2010 имеет тип проекта для работы с MS-SQLSERVER. Прямо переименовываниями не вижу, чтобы занималась, но она имеет возможность делать «билд», т.е. проверить всё. А значит покажет, что что-то потерялось в случае переименования.
Потом, дело не в инструменте. Инструмент написать можно быстро. Имена таблиц, как уже сказано, не имеет смысла покрывать тестами.
У меня не было свободных бесплатных инструментов, я сделал анализатор упоминаний по названию на шарпе с графическим интерфейсом и даже подсветкой ключевых слов за пару часов.
Потом, не было в наличии средства для тестирования кода БД. Существующий проект, когда пришел, многое реализовывал в хранимках. Вижуал студия позволяет писать тесты, но не на той версии, которую в конторе позволили купить. Так и написать движок для тестов заняло тоже в районе нескольких часов.
Инструменты — не такая уж проблема. Завязываться на них, выбирая технологию, не стоит. На прошлой работе, например, базовику не нравился синтаксис TRANSACT-SQL. Он прикрутил препроцессор С. Свободный. И там понаписывал макросы подмены, в результате писал на более удобном для себя синтаксисе. Циклы и ветвления похожи на С были.
Потом, при отсутствии инструмента можно вполне без проблем построить даже самому разворачивание базы из скриптов. Билд-сервер настроить. И там уже как хотите — переименовывайте. Сделаете анализатор.
Конечно, сделать полный парсер SQL сложно. Но анализ связей вполне можно. И поиск по вхождению слова без проблем. А на запросы написать тесты тоже можно.
БД для этого хорошо подходит. А чем лучше смоделировать ее в коде, а из БД делать хранилище?
В БД моделировать — дело минут. Создать таблицы, связи и ограничения. Декларативный способ.
Реляционная БД позволяет как бы создать зависимости между данными другим (не ООП) способом. Но этот способ жестче — дает возможность применять реляционную алгебру. При этом, данные можно представить, как некий н-мерный куб. Это дает возможность обобщать. Видеть упущенные требования.
Ограниченния реляционной БД — это как статическая типизация. Если в коде вы для переменной ставите целочисленный тип, зная, что это будет счетчик, то не жалуетесь, что тип ограничен и не позволит вам в будущем там хранить число с плавающей точкой. Схема БД — это такая типизация, где на уровне схемы будет проверяться взаимосвязь с данными. И на уровне схемы выражается практически вся предметная область, еще без строчки кода.
Хотя, бывает, конечно, не всё можно выразить, но многое. Зачем же упускать такую возможность. А гибкость кода — это возможность багов.
Рефакторить базу данных так же легко, как и код, если не легче. Но вы правы, если БД работает, то рефакторить сложнее — не откуда брать данные, в случае, если они не фиксировались. Но а откуда брать данные, если рефакторить код с т.з. таких же измененний? Допустим, не держим реляционную БД. То же самое. Чудес не происходит, информацию родить нельзя.
Реляционная БД тем и хороша, что в ней более наглядно видны такие проблемы. И легко моделируется предметная область. В определенных пределах.
Проблема автора, по моему мнению, что он БД рассматривает, как нечто связанное с реализацией, нечто второстепенное. Так никто ж и не заставляет писать кривую схему непонятно чего. Если в БД моделировать предметную область (т.е. всем понятно — таблицы — сущности или связи и т.д.), то никаких проблем с юз-кейсами, от которых отталкиваться проектирование и разработка должны — нет.
Сложность — не совсем абстрактное понятие. В него уже вкладывают смысл. Повторю: у майкрософта в студии встроен анализатор кода, который показывает индекс, насколько код хорош. Конечно, эта вещь не абсолютна, т.к. мерки снимает автоматические. Но стоит посмотреть на эти коэфициенты. Проводят исследования и как-то сложность превращают в вычислимую величину.
Там много критериев. Самый известный — цикломатическая сложность. Грубо говоря, это количество независимых маршрутов в графе кода. Т.е. в таком простом примере как привели вы — сложность одинакова — там только один путь. Но строк кода в три раза больше. Это другой коефициент. Да и очевидно, что первый фрагмент сложнее второго.
Но. Если бы там были ветвления, то в три раза и увеличилась бы сложность от трех повторений. Далее, эти маршруты меряются по теории в одном куске кода. Если будет два метода с одинаковым выполнением — цикломатическая сложность увеличится на два. Насколько я понял (не написано про отдельные методы), но проведя эксперимент, видно, что просто цикломатические сложности методов суммируются.
Глубина наследования — другой коэфициент. Тоже вынос в методы, вынос в базовые классы, хоть и уменьшает по DRY размер кода и повторения, но увеличивает этот коэфициент. Он негативно влияет на простоту кода.
«Вы как-то странно со мной не соглашаетесь, полностью повторяя мои выкладки о цене применяемых методов O_O.»
А я с вами и не несоглашаюсь. Только не соглашаюсь с отдельным моментом — что сведение сокращение повторений сложных кусков не уменьшает сложность.
да нет же! ))
Если сложный кусок повторяется несколько раз, то во столько же раз и сложность растет. Если условно сложность куска кода равна единице, то пять повторений — сумма — сложность 5. Сведение в одно место уменьшает сложность.
Другое дело, что это не единственный критерий оценки сложности. Вот, можно посмотреть, как майкрософт автоматически в вижуал студии оценивает сложность кода.
Сведение в одно место кода уменьшает сложность — цикломатическую сложность. Но повышает другие сложности — туда входит и количество строк. И глубина вложенности классов. Всё это вопрос балланса.
Например, возможно круто использовать виртуальные методы вместо ветвлений. Но очевидно, что для несложных операций если будет стоять if и метод от этого не слишком велик, то иф лучше — потому что код лежит в одном месте, доступен обзору глазами. То же наверное и с копипастом. Если то, что куски кода делают — ясное и в обзоре глазами воспринимается хорошо, то пусть будет. Но если этому куску можно дать ясное осмысленное имя и этот кусок встречается не два раза, а много раз — значит это некоторая операция, характеризующая программу, выносить надо однозначно.
Где-то так. Обилие методов (функций), которые содержат по три строчки, возможно кому-то нравится. Но разброс логики по разным файлам в разных функциях не делает код читаемее. Потом, за каждый вынос куска кода нужно платить — придумывать имя. А имя — это новая сущность, смысл которой нужно помнить. А новому программисту с ней разобраться и тоже запомнить.
Всё несет нагрузку. Просто надо в комплексе рассматривать. Для меня основной критерий — это отражение требований. Т.е. ясность кода с т.з. предметной области. Кое чем можно жертвовать, оценивать по разным параметром код, но этот критерий — главный.
Логика и наоборот действует — можно сложное упрощать. А если вопрос стоит в том, что уже достаточно упрощено, то я следую принципу максимального соотвествия кода задаче (не реализации). Прятать куда-то что-то за имена и вращать разными паттернами не нужно, потому что чаще всего есть одно-два понимания, как в коде правильно закодить требования, нечего выдумывать.
Сегодня была ситуация. Дали задание проанализировать как создать архитектуру для одной задачи. Вклиниться в уже созданную БД, это просто очередная задача.
Через полчаса получили развернутый ответ, всё время просто письмо заняло. И начали удивляться, что я мало времени потратил на «создание архитектуры».
Какая еще архитектура. Есть требования, они однозначно переводятся в сущности БД. Нечего выдумывать, анализ занимает время всего лишь, чтобы понять смысл задачи. Остальное автоматом.
Так и с любым кодом. Если люди начинают что-то прятать, высовывать — то это или рефакторинг, чтобы лучше код соответствовал задаче или не совсем понимают, зачем это нужно.
По моему вы путаете сложность с количеством информации. Которая только от задачи должна зависеть в идеале.
А вот сложность реализации может быть любая. Вот, пример. Когда-то меня «заставили» писать научную статью. Для галочки. У меня не было никаких идей и муза была в отпуске, а настороение не очень, поэтому я написал на первое выражение 0 = 0. Далее начал наращивать. В конце у меня получилась большая система сложных уравнений. И была она математически верной. Но суть осталась — ноль равен нулю. Я такое часто вижу в разработке ПО.
У нас один чудак написал проект для переливки таблицы из эксель в БД. Там тысячи классов. Понесло, что называется. Искусство ради искусства. Куча паттернов, куча совершенно не обоснованных решений. Погоня за гибкостью на самокате. )))
Один из примеров. В базе есть справочник, который он создал. У элементов справочника есть целочисленные идентификаторы. Но он решил, что в коде пользоваться идентификаторами — хардкод и опасно (он вообще странный). И поэтому дал имена элементам справочника вроде «Item_1», «Item_2» и т.д. Потом в коде парсит и находит цифру. Она уже как бы от идентификатора не зависит.
Почти все проблемы, которые я видел в «архитектуре», работая в разных компаниях — это излишняя сложность. Когда люди пытаются не отталкиваться от требований, а предвидеть и сделать более гибкое решение или более оптимальное (преждевременная оптимизация). В результате появляется код, оснований писать который не было никакого. Этот код требует поддержки и создается костыль за костылем, «чтобы уравнять уравнения».
Даже не совсем так. YAGNI не только на функционал, но и на хитрые реализации, паттерны распространяется. Но не на рефакторинг. Рефакторинг — механическая вещь, ведущая к KISS. И он делается просто на каждом шаге по ТДД. Это неотъемлемая часть процесса разработки.
Рефакторинг — это не добавление и не изменение функционала. YAGNI распространяется на функционал, а не на рефакторинг. Если что-то повторяется — рефакторинг обязателен. Говорить про рефакторинг «нам это не понадобится» — нельзя. Поэтому не противоречит.
Вообще со статьей местами очень не согласен. YAGNI — это основополагающий принцип и не вижу проблем в сложных проектах его использовать
Мы сейчас начнем спорить исходя из собственных вкусов и примеров, которые каждый себе представляет :) Скажу только, что я был такого же мнения как и Вы. Но со временем мнение изменилось. Просто по опыту, что случалось, а что нет. У вас может быть другая статистика.
Все эти методики работают только при определенных навыках работы с кодом. Я и в сложных проектах вижу только такой путь — чем проще тем лучше. Не могу себе представить, где это не работает.
Можно в личной переписке погонять какие-то примеры. А то не сможем обоснований найти.
С примером с БД не вижу проблем. Тут надо смотреть. Если логику всю выполняет база с помощью запросов, то видимо слой будет, который эти запросы хранит. Не будут же они размазаны по всей иерархии? Ну и это не является какой-то хитрой архитектурой. После двух запросов и рефакторинга может выделиться объект, который оборачивает запросы в методы.
Далее, если база не справляется, это задача оптимизации. Стандартно и смотреть, как можно это оптимизировать. Кстати, правильный путь — базу грузить работой. Как раз предугадывание того, что база не справится — и попытка на этапе проектирования разгрузить — потенциальные проблемы. СУБД создают для работы. Для запросов. Для масштабируемости. Пусть и делают то, за что ей деньги платят ))
Когда тормозит, то:
1. Пытаемся решить железом. Дешевле.
2. Если не выходит первое, то пытаемся кешировать. Замеры, попытки кешировать, вплоть до того, что что возможно выделится слой кеша на апп-сервере, который будет фреймоврком Unit Of Work.
Можно и второй этап сразу с конца сделать — выбрать подходящий фреймворк (если замеры говорят, что база много где тормозит). Ну и переписывать не должно быть так уж много. Методы уже возвращают списки сущностей или принимают на вход. Переписывается ровно то, что тормозит — логика работы.
Не вижу причин это предугадывать. Да, это может занять время. Но с другой стороны, умные люди СУБД придумывают, как систему обработки больших объемов информации, кеширование запросов и всяческие оптимизации. А я вижу много программистов, которые создают свои велосипеды, которые повторяют чуть ли не всю СУБД. А СУБД отводят роль только файловой системы для хранения состояний между выключениями машины.
И на счет ORM. Какие-то объекты-сущности нужны. По условию, ORM не выбрали, потому как руками писали запросы. Но надеюсь, что какие-то генераторы классов были. Нет, то следующий шаг — сделать. Или может уже выбрали ORM, но тогда запросы в хранимках хранились, а не в коде.
Сразу же выбрать такую ORM, которая будет брать на себя функции СУБД, я бы не стал. Всё нормально с примером.
Как-то нелогично, когда много работы связывается не с задачами, а с инфраструктурными средствами. Значит средство плохое, если не дает достаточной развязки. Почему код связи с БД должен влиять сильно на остальной код, который на него завязывается? Хотя, бывает, но всё таки не вижу выгоды и здесь в предугадывании. Придет новое требование — перепишем. Гораздо чаще на моей памяти проблемы возникали, когда наоборот — угадывали. Исходя из предположений: а вдруг база поменяется и т.д. Ни разу не менялась база, а вот код доходил до точки, когда у людей, его писавших возникало желание его с нуля переписать. Программисты же всегда оптимисты, если не пишут тесты и рефакторинг. Они всегда переоценивают свои силы. И происходит потому, что не верят, что напишут баги. И вот в «разработке архитектуры» по умолчанию думают, что код будет сразу верный и лучше предугадать и создать нужную гибкость. Я думаю, что даже в случае, описанном вами, когда вначале код завязался на что-то, а потом надо переписать, в большинстве случаев такой вариант окажется менее затратным. Чем поддерживать излишне гибкий код, когда даже угадали и пришло изменение требований и переписывать не надо.
Кто бы такие такие замеры времени делал и делал сравнения? Сплошной оптимизм в планировании. А выводы в случае неудачи планирования всегда обратные: не в том, что планировали преждевременно, а в том, что неправильно угадали.
Не предусматривать ничего. Сложности возникают при несоблюдении процесса — отсутствии рефакторинга. Если же с ним всё хорошо, то:
1. Потраченное время на предугаданную вещь в случае угадывания равно приблизительно времени, которое потратится позже. Когда возникнет потребность.
2. Потраченное время на предугаданную вещь, которая не потребуется (не угадали) остается зря потраченным временем. Плюс к этому добавляется время на поддержку ненужного кода «на всякий случай». Или выпиливания.
Когда процесс налажен, то рефакторинг и тесты писать не сложно и менять код в случае небольших изменений тоже. Ведь если не предугадывать, то код всегда самый простой. А когда люди предугадывают, то у них и мышление построено так: не буду убирать код, который уже написан и еще не понадобился — вдруг понадобится. Тут не до рефакторинга. Получается дедушкин чердак со всяким хламом, а не код.
Думаю, дело не совсем в самой парадигме ООП. Можно их совмещать. Но осторожно. Со статьей согласен во многом. Объекты больше должны просто быть структурами данных, а не инкапсулировать состояние.
Скрытое состояние объекта — это сайд-эффекты (хотя и любое состояние — сайд-эффект). Их надо сводить к минимуму. А лучше вообще перевычислять. Любое состояние — это то, что раньше называли глобальными переменными. Не особо большая разница — глобальные переменные для всей программы или это поле класса. Любое такое состояние, живущее между вызовами, а не в параметрах методов и в локальных переменных — сайд-эффект. Который порождает неповторяемое поведение.
Да, отказ от паттернов — я имею ввиду, от желания их применять. Они, конечно, используются. Куда ж без стратегии, фабрик, и синглтона. Но паттерны часто сами рождаются при рефакторинге и оценке кода. Естественным образом. И вот, эти три паттерна, наверное, часто используются, остальные гораздо реже или вообще не используются. Код должен отвечать критерию простоты, а не гибкости.
Так 5 лет учебы мало дают. Только после 5 лет работы полноценный рабочий день, при этом в хорошем колективе, люди понемногу приходят в себя.
А образование программистов — портит людей. Мне повезло, я по образованию не программист. Но потом даже преподавал программирование в ВУЗе.
Что я увидел.
1. Низкий уровень преподавания колег. Очевидно, что люди, умеющие хоть как-то программировать, не будут за такие копейки работать в универе. Остаются те, кто не может. Поэтому студенты брошены сами на себя. После окончания учебы программистами (нормальными) становятся только те, кто занимался самообучением.
2. Плохо устроена программа обучения. За пять лет нужно изучить много направлений. По сути, не получается ни одного. Я учился не на программиста, но предмет был несколько лет. Тупо один паскаль. Но только благодаря такому количеству часов мы почти Кнута прошли. Стуктуры данных и алгоритмы. У программистов на предмет ООП столько, что не успевают толком до виртуальных методов дойти.
Плюс слабая математическая подготовка. Это всё не только от уровня преподавания, а от распределения часов.
Т.е. выходят люди из вузов — база плохая. Но далее, работа и желание развиваться — всё поправит. Но и тут есть несколько этапов (сужу по себе):
1. Человек должен научиться писать программы хоть как-то. Циклы, ветвления, алгоритмы. Хорошо, если эта база хорошо проработана, но не важно. После этого человек ощущает, что может написать всё и часто такие устраиваются уже на работу. Что-то пишут, но говнокод пока.
2. Изучают паттерны, примеры реализованных проектов и т.д. Важный этап в обучении. Но сам способ обучения — человеку предлагают решения на примерах. И у него складывается ощущение, что надо в любой задаче сразу увидеть архитектуру, добиваться гибкости, использовать побольше паттерны. Часто люди изучают UML и упираются в то, что надо разрабатывать архитектуру ДО. И всё правильно прорисовать. Ну и куча ереси рождается — жалуются, что требования меняются, а они не должны, что в правильных компаниях такого не допустят, что начальник-архитектор должен всё сам разрисовывать, а кодерам давать схемы, тогда будет рай.
3. Возврат назад в природу. Отказ от паттернов и архитектуры. Это не шутка. Этот этап — доработка навыков кодирования и получение знаний о кодировании. Т.е. книги о рефакторинге, тест-драйвинг девелопменте, домейн-драйвинг девелопменте (все книги прорабатываются критично), теории функционального программирования. У человека появляются навыки, как правильно выходить из той или иной ситуации. Как правильно решать возникшие конкретные задачи. Как не дублировать данные или код. Какой код плохой. И расширяется список способов оценки кода. Когда у человека появятся навыки кодирования и оценки, он перестает бояться кодировать без тщательного планирования.
Где-то так. Главное на втором этапе не застрять. Грубо проектировать масштабируемое ядро на бумаге — конечно можно. Тут вопрос баланса, что такое грубо, а что нет. В идеале, возможно, есть такие джедаи, которые вообще не рисуют и даже не воображают. А умеют доращивать код, доращивая юз-кейсы. Возможно, к этому надо стремиться, но ни я таких людей не встречал, ни сам так в чистом виде не пробовал. Минимальное ядро всё таки проектировал.
Что интересно — фантазия у людей, которые не отталкиваются от требований — неистощима. Такое иногда пишут в погоне за гибкостью.
Вот, в моем проекте написали класс, который является «сообщением». В него вставили тип и передают список параметров object. Уверен, что гордятся этим. Недавнее изобретение. А то, что нарушилась статическая типизация и забили болт на шарп, как таковой, создавая «новое измерение» в сверхгибком параллельном и непересекающимся с шарпом пространстве — никто не думал.
Кстати, страдаю как раз по причине того, что ищу баги в этом чужом коде второй день. И это непросто. Не знаю, как людей можно отучить от целенаправленного нанесения вреда.
Архитектура — это для меня в последнее время самое злое ругательство.
Простите, не верно. То, что люди пишут совсем г… нокод, — бывает и часто. Но средства борьбы ваши тоже не верные.
По порядку:
1. Архитектура БД и классов. Худший из способов — это строить архитектуру до написания кода. Опытному программисту сам код по разным показателям подсказывает, где лажа и что можно изменить. Выбирая листочки или UML — вы выбираете другой язык для моделирования — не язык программирования. Графические языки иногда более наглядны, но всегда более ущербны. И часто не могут показать лажу. Потом, один из главных принципов ООП — скрывание внутренностей — т.е. инкапсуляция. UML нарушает этот принцип по полной, делая анатомический разрез. Код не является настолько иногда наглядным как UML. Но как раз в этом его сила — он стимулирует писать, отражая более прямым образом суть. Цель — код должен читаться легко и понятно, как книга, которая описывает, что должно выполняться.
UML — хорошее средство документирования для более быстрой передачи знаний другим программистам о продукте. Но такая документация все же делается после, а не до. Проектировать так — плохо.
Хотя, не буду однозначен, проектировать на листочках ручкой можно и полезно, но очень грубо. Не подробно, развешивая на стену все зависимости. А только на следующий этап.
2. Переменные, методы и комментарии. Переменные и методы да, обязаны называться как надо. В итеративной разработке именно поэтому они часто переименовываются. Но замечания по поводу комментариев. Комментарии — это «естественный» язык. Сам код надобно писать так, чтобы он отражал то, о чем он написан. Не какие-то реализации, а смысл. Комментарии — это параллельно ведется на другом языке описание. Если рука тянется писать комментарии — значит лажа в коде, он не выполняет своей функции — ясного изложения мыслей. Второй минус комментариев — это язык, который не имеет никакого отношения к продукту, к его компиляции (как и UML), а значит это дополнительная вещь, которая по сути дублирует логику и требует еще и поддержки. При этом поддержка эта сложнее, чем поддержка кода. Потому что у вас нет средства компиляции и проверки соотвествия требованиям.
Я говорю не обо всех комментариях, а о комментариях внутри методов. Комментрарии, описывающие классы или методы — полезны. Но опять же, они не должны брать на себя функцию объяснения, что делает класс, который называется как попало и из его названия ничего не скажешь.
Иногда комментарии в коде нужны — бывает, что код подвергался оптимизации и уже не прямо выражает смысл. Или просто выбран сложный алгоритм. Но всё же вот так — с комментариями нужно быть осторожным, они ведутся по остаточному принципу, когда другие средства повышения читаемости кода не дали результата.
3. Копипаст. Проблема копипаста полезна для понимания рефакторинга. Представьте, что у вас уже есть код и в нем есть похожие куски. Вот, самое время подумать, возможно есть лажа. И вынести в отдельный метод. Классика и наверное многие возмутятся, к чему я это пишу. А пишу я к тому, что я не сторонник проектирования наперед. Проблемы решаются по мере поступления. А у вас подход — решить всё в воображении и предвидеть, что понадобится. И вы проблему копипаста рассматриваете — как проблему недопущения копипаста. Я рассматриваю, как проблему исправления копипаста. Если код повторяется два раза — сингнал рефакторить. При этом он должен повториться два раза. А не «не должен вообще».
Решил написать комментарий, потому что сейчас страдаю от чрезмерной тяги людей архитектурить. При этом наперед. Когда простая задача по смыслу породила больше 20 классов, каждый минимум на две страницы. Хотя как по мне, кода должно быть максимум на две страницы. Аргументируют это так: чтобы потом можно было просто писать, не задумываясь об архитектуре.
Вот как раз, теперь и потом придется задумываться постоянно. Не было во Вселенной ни единой уже создавшейся причины писать столько кода. И глючного, как и всегда бывает.
Код не аналогичен. Потом, я указывал, что SQL короче и яснее по сравнению с императивным кодом шарпа. Если пользоваться линкью (или лямдами, понятно, хотя я это не сказал), то код будет более менее одинаковый для восприятия и по объему.
Да, есть такое. Но это было первое решение. Я немного переделал движок. Дело в том, что если один и тот же объект нужен не одному тесту, то не обязательно его убивать и снова вытягивать. Таблицы только чистить.
Да самому стыдно за движок — велосипеды сочиняю :)) Но какого-то готового решения не нашел. Может плохо искал. Майкрософтовский вместе со студией стоит дорого. Не захотели на каждого разработчика покупать такую версию студии.
Здесь описал движок, как пример, что при отсутствии инструмента — не так сложно его сделать.
Конечно. Если бы у вас не было БД, а вы хранили данные в файлах, например. И писали только на шарпе (или джаве и т.д.). Такой идеал, как автор предлагает. И запустили систему в продакт. А потом нужен рефакторинг кода. Упс — та же проблема. Если информация утеряна, ее из космоса получить невозможно.
Даже если у вас проект с БД и вы выкатили в продакт, а потом рефакторите код, и он затрагивает объекты, которые уже хранятся в БД — та же проблема. Потеря данных.
Если же вы не выкатывали ничего в продакт, а находитесь как положено на этапе разработки системы, то рефакторить можно и то и другое.
Алгоритмы работают с данными. Если у вас такой случай, что не надо трогать данные — то отлично, рефакторите без проблем. А если надо, то СУБД в этом не виновна. Эти проблемы будут всё равно.
«Вы с ним работали? Я работал. Никакого «билда» в смысле компиляции там нет, тамошний «билд» — это расчет скриптов изменений.»
немного. Я «олдскульный». Всё скриптами пользуюсь ))
Но «билд» — замечательная вещь. Конечно, там нет компиляции, потому что и не может для SQL-SERVER компиляции. Для скриптового языка. Но расчет скриптов изменений — это то, что нужно. Он позволяет указать любые ошибки. Вот как раз проверить все запросы на предмет соотвествия именам таблиц и колонкам.
«Интересно, как ваш «движок» изолирует тестируемый код от его зависимостей»
Тестовый движок, это отдельная база данных. Состоит из нескольких несложных хранимок. Сам тест — хранимая процедура. Этому движку указывается, к какой базе данных нужно приконектиться. И она вытягивает в свою базу требуемые для теста таблицы (пустые, только структура), хранимки, функции и т.д.
Тест начинается с указания какие таблицы нужны. Далее заполнение тестовыми данными. И прогон тестов.
Всё просто. На самом деле кода не много. А развязка от тестируемой базы неплохая. Тесты это хорошо выручают.
Но тесты эти тестируют только код. Например, зависимости между таблицами не вытягиваются. Декларативную часть бессмысленно тестировать.
«Не стоит? Ну если для вас двух-трехкратная разница в производительности работы сотрудника не стоит выбора технологии, то я даже не знаю, что его стоит»
Конечно, важна производительность сотрудника. Я только говорю о том, что часто переоценивают проблемы. Эти проблемы решаются разово и тратится на них время не такое большое. Программисты мы или кто? У нас в конторе например, выбирали ORM и боялись, что в некоторых вариантах не генерировались классы по БД. Объяснил как, и дело пошло. Людям казалось, что это сложно. А оказывается, совсем нет.
«Угу. Спасибо за предложение заниматься интеграционными тестами. Проблема в том, что это — дороже, чем изолированные.»
Дело не в интеграционных тестах. И не в дороговизне их. Тесты и интеграционные и модульные нужны. Но вы этот сервер можете хоть вручную создать и разворачивать, а также прогонять все тесты (не только интеграционные). Тесты на БД тоже можно без проблем прогонять отдельно.
«А вообще, ваш пост — хорошая демонстрация того, почему базу рефакторить не так же просто, как и код. „
Тут другая тема есть. Кода на СУБД меньше. Следовательно и рефакторинг в этом плане будет проще. Если данные потеряны, то сложно будет и в слое над БД. Если нет, то рефакторинг в БД не сложный. Даже не знаю, как кратко описать. О рефакторинге кода писал Фаулер. Там всё строится почти на выносе метода или выносе класса. В БД — вынос сущности (не только это, конечно, но чаще всего). Была колонка, стала отдельной сущностью. Всё это — декларативное описание. Как по мне, и работы меньше.
“Я вижу это каждый день, имея возможность сравнить стоимость типичных рефакторингов в .net-команде и sql-команде. Во второй их практически нет.»
я и sql-команд не видел. Мало из базовиков вообще с программированием нормально знакомы. А уж с понятием рефакторинга или юнит-тестов, тем более. Это проблемы профессионализма, которые остро стоят в нашей отрасли. Берем тех, кто вообще что-то может.
Сам подход — моделирование в БД ничем не хуже моделирования в шарпе. Даже лучше иногда. Чем код создания таблиц и связей так плох по сравнению с другим языком? Если вопрос только в инструменте — то это мелко. Я не базовик, там есть разные не известные мне инструменты.
ERwin например. Базовики пользуются и хвалят. Не знаю его возможностей.
VS-2010 имеет тип проекта для работы с MS-SQLSERVER. Прямо переименовываниями не вижу, чтобы занималась, но она имеет возможность делать «билд», т.е. проверить всё. А значит покажет, что что-то потерялось в случае переименования.
Потом, дело не в инструменте. Инструмент написать можно быстро. Имена таблиц, как уже сказано, не имеет смысла покрывать тестами.
У меня не было свободных бесплатных инструментов, я сделал анализатор упоминаний по названию на шарпе с графическим интерфейсом и даже подсветкой ключевых слов за пару часов.
Потом, не было в наличии средства для тестирования кода БД. Существующий проект, когда пришел, многое реализовывал в хранимках. Вижуал студия позволяет писать тесты, но не на той версии, которую в конторе позволили купить. Так и написать движок для тестов заняло тоже в районе нескольких часов.
Инструменты — не такая уж проблема. Завязываться на них, выбирая технологию, не стоит. На прошлой работе, например, базовику не нравился синтаксис TRANSACT-SQL. Он прикрутил препроцессор С. Свободный. И там понаписывал макросы подмены, в результате писал на более удобном для себя синтаксисе. Циклы и ветвления похожи на С были.
Потом, при отсутствии инструмента можно вполне без проблем построить даже самому разворачивание базы из скриптов. Билд-сервер настроить. И там уже как хотите — переименовывайте. Сделаете анализатор.
Конечно, сделать полный парсер SQL сложно. Но анализ связей вполне можно. И поиск по вхождению слова без проблем. А на запросы написать тесты тоже можно.
В БД моделировать — дело минут. Создать таблицы, связи и ограничения. Декларативный способ.
Реляционная БД позволяет как бы создать зависимости между данными другим (не ООП) способом. Но этот способ жестче — дает возможность применять реляционную алгебру. При этом, данные можно представить, как некий н-мерный куб. Это дает возможность обобщать. Видеть упущенные требования.
Ограниченния реляционной БД — это как статическая типизация. Если в коде вы для переменной ставите целочисленный тип, зная, что это будет счетчик, то не жалуетесь, что тип ограничен и не позволит вам в будущем там хранить число с плавающей точкой. Схема БД — это такая типизация, где на уровне схемы будет проверяться взаимосвязь с данными. И на уровне схемы выражается практически вся предметная область, еще без строчки кода.
Хотя, бывает, конечно, не всё можно выразить, но многое. Зачем же упускать такую возможность. А гибкость кода — это возможность багов.
Реляционная БД тем и хороша, что в ней более наглядно видны такие проблемы. И легко моделируется предметная область. В определенных пределах.
Проблема автора, по моему мнению, что он БД рассматривает, как нечто связанное с реализацией, нечто второстепенное. Так никто ж и не заставляет писать кривую схему непонятно чего. Если в БД моделировать предметную область (т.е. всем понятно — таблицы — сущности или связи и т.д.), то никаких проблем с юз-кейсами, от которых отталкиваться проектирование и разработка должны — нет.
Там много критериев. Самый известный — цикломатическая сложность. Грубо говоря, это количество независимых маршрутов в графе кода. Т.е. в таком простом примере как привели вы — сложность одинакова — там только один путь. Но строк кода в три раза больше. Это другой коефициент. Да и очевидно, что первый фрагмент сложнее второго.
Но. Если бы там были ветвления, то в три раза и увеличилась бы сложность от трех повторений. Далее, эти маршруты меряются по теории в одном куске кода. Если будет два метода с одинаковым выполнением — цикломатическая сложность увеличится на два. Насколько я понял (не написано про отдельные методы), но проведя эксперимент, видно, что просто цикломатические сложности методов суммируются.
Глубина наследования — другой коэфициент. Тоже вынос в методы, вынос в базовые классы, хоть и уменьшает по DRY размер кода и повторения, но увеличивает этот коэфициент. Он негативно влияет на простоту кода.
«Вы как-то странно со мной не соглашаетесь, полностью повторяя мои выкладки о цене применяемых методов O_O.»
А я с вами и не несоглашаюсь. Только не соглашаюсь с отдельным моментом — что сведение сокращение повторений сложных кусков не уменьшает сложность.
Если сложный кусок повторяется несколько раз, то во столько же раз и сложность растет. Если условно сложность куска кода равна единице, то пять повторений — сумма — сложность 5. Сведение в одно место уменьшает сложность.
Другое дело, что это не единственный критерий оценки сложности. Вот, можно посмотреть, как майкрософт автоматически в вижуал студии оценивает сложность кода.
Сведение в одно место кода уменьшает сложность — цикломатическую сложность. Но повышает другие сложности — туда входит и количество строк. И глубина вложенности классов. Всё это вопрос балланса.
Например, возможно круто использовать виртуальные методы вместо ветвлений. Но очевидно, что для несложных операций если будет стоять if и метод от этого не слишком велик, то иф лучше — потому что код лежит в одном месте, доступен обзору глазами. То же наверное и с копипастом. Если то, что куски кода делают — ясное и в обзоре глазами воспринимается хорошо, то пусть будет. Но если этому куску можно дать ясное осмысленное имя и этот кусок встречается не два раза, а много раз — значит это некоторая операция, характеризующая программу, выносить надо однозначно.
Где-то так. Обилие методов (функций), которые содержат по три строчки, возможно кому-то нравится. Но разброс логики по разным файлам в разных функциях не делает код читаемее. Потом, за каждый вынос куска кода нужно платить — придумывать имя. А имя — это новая сущность, смысл которой нужно помнить. А новому программисту с ней разобраться и тоже запомнить.
Всё несет нагрузку. Просто надо в комплексе рассматривать. Для меня основной критерий — это отражение требований. Т.е. ясность кода с т.з. предметной области. Кое чем можно жертвовать, оценивать по разным параметром код, но этот критерий — главный.
Логика и наоборот действует — можно сложное упрощать. А если вопрос стоит в том, что уже достаточно упрощено, то я следую принципу максимального соотвествия кода задаче (не реализации). Прятать куда-то что-то за имена и вращать разными паттернами не нужно, потому что чаще всего есть одно-два понимания, как в коде правильно закодить требования, нечего выдумывать.
Сегодня была ситуация. Дали задание проанализировать как создать архитектуру для одной задачи. Вклиниться в уже созданную БД, это просто очередная задача.
Через полчаса получили развернутый ответ, всё время просто письмо заняло. И начали удивляться, что я мало времени потратил на «создание архитектуры».
Какая еще архитектура. Есть требования, они однозначно переводятся в сущности БД. Нечего выдумывать, анализ занимает время всего лишь, чтобы понять смысл задачи. Остальное автоматом.
Так и с любым кодом. Если люди начинают что-то прятать, высовывать — то это или рефакторинг, чтобы лучше код соответствовал задаче или не совсем понимают, зачем это нужно.
А вот сложность реализации может быть любая. Вот, пример. Когда-то меня «заставили» писать научную статью. Для галочки. У меня не было никаких идей и муза была в отпуске, а настороение не очень, поэтому я написал на первое выражение 0 = 0. Далее начал наращивать. В конце у меня получилась большая система сложных уравнений. И была она математически верной. Но суть осталась — ноль равен нулю. Я такое часто вижу в разработке ПО.
У нас один чудак написал проект для переливки таблицы из эксель в БД. Там тысячи классов. Понесло, что называется. Искусство ради искусства. Куча паттернов, куча совершенно не обоснованных решений. Погоня за гибкостью на самокате. )))
Один из примеров. В базе есть справочник, который он создал. У элементов справочника есть целочисленные идентификаторы. Но он решил, что в коде пользоваться идентификаторами — хардкод и опасно (он вообще странный). И поэтому дал имена элементам справочника вроде «Item_1», «Item_2» и т.д. Потом в коде парсит и находит цифру. Она уже как бы от идентификатора не зависит.
Почти все проблемы, которые я видел в «архитектуре», работая в разных компаниях — это излишняя сложность. Когда люди пытаются не отталкиваться от требований, а предвидеть и сделать более гибкое решение или более оптимальное (преждевременная оптимизация). В результате появляется код, оснований писать который не было никакого. Этот код требует поддержки и создается костыль за костылем, «чтобы уравнять уравнения».
Вообще со статьей местами очень не согласен. YAGNI — это основополагающий принцип и не вижу проблем в сложных проектах его использовать
Все эти методики работают только при определенных навыках работы с кодом. Я и в сложных проектах вижу только такой путь — чем проще тем лучше. Не могу себе представить, где это не работает.
Можно в личной переписке погонять какие-то примеры. А то не сможем обоснований найти.
С примером с БД не вижу проблем. Тут надо смотреть. Если логику всю выполняет база с помощью запросов, то видимо слой будет, который эти запросы хранит. Не будут же они размазаны по всей иерархии? Ну и это не является какой-то хитрой архитектурой. После двух запросов и рефакторинга может выделиться объект, который оборачивает запросы в методы.
Далее, если база не справляется, это задача оптимизации. Стандартно и смотреть, как можно это оптимизировать. Кстати, правильный путь — базу грузить работой. Как раз предугадывание того, что база не справится — и попытка на этапе проектирования разгрузить — потенциальные проблемы. СУБД создают для работы. Для запросов. Для масштабируемости. Пусть и делают то, за что ей деньги платят ))
Когда тормозит, то:
1. Пытаемся решить железом. Дешевле.
2. Если не выходит первое, то пытаемся кешировать. Замеры, попытки кешировать, вплоть до того, что что возможно выделится слой кеша на апп-сервере, который будет фреймоврком Unit Of Work.
Можно и второй этап сразу с конца сделать — выбрать подходящий фреймворк (если замеры говорят, что база много где тормозит). Ну и переписывать не должно быть так уж много. Методы уже возвращают списки сущностей или принимают на вход. Переписывается ровно то, что тормозит — логика работы.
Не вижу причин это предугадывать. Да, это может занять время. Но с другой стороны, умные люди СУБД придумывают, как систему обработки больших объемов информации, кеширование запросов и всяческие оптимизации. А я вижу много программистов, которые создают свои велосипеды, которые повторяют чуть ли не всю СУБД. А СУБД отводят роль только файловой системы для хранения состояний между выключениями машины.
И на счет ORM. Какие-то объекты-сущности нужны. По условию, ORM не выбрали, потому как руками писали запросы. Но надеюсь, что какие-то генераторы классов были. Нет, то следующий шаг — сделать. Или может уже выбрали ORM, но тогда запросы в хранимках хранились, а не в коде.
Сразу же выбрать такую ORM, которая будет брать на себя функции СУБД, я бы не стал. Всё нормально с примером.
Кто бы такие такие замеры времени делал и делал сравнения? Сплошной оптимизм в планировании. А выводы в случае неудачи планирования всегда обратные: не в том, что планировали преждевременно, а в том, что неправильно угадали.
1. Потраченное время на предугаданную вещь в случае угадывания равно приблизительно времени, которое потратится позже. Когда возникнет потребность.
2. Потраченное время на предугаданную вещь, которая не потребуется (не угадали) остается зря потраченным временем. Плюс к этому добавляется время на поддержку ненужного кода «на всякий случай». Или выпиливания.
Когда процесс налажен, то рефакторинг и тесты писать не сложно и менять код в случае небольших изменений тоже. Ведь если не предугадывать, то код всегда самый простой. А когда люди предугадывают, то у них и мышление построено так: не буду убирать код, который уже написан и еще не понадобился — вдруг понадобится. Тут не до рефакторинга. Получается дедушкин чердак со всяким хламом, а не код.
Скрытое состояние объекта — это сайд-эффекты (хотя и любое состояние — сайд-эффект). Их надо сводить к минимуму. А лучше вообще перевычислять. Любое состояние — это то, что раньше называли глобальными переменными. Не особо большая разница — глобальные переменные для всей программы или это поле класса. Любое такое состояние, живущее между вызовами, а не в параметрах методов и в локальных переменных — сайд-эффект. Который порождает неповторяемое поведение.
А образование программистов — портит людей. Мне повезло, я по образованию не программист. Но потом даже преподавал программирование в ВУЗе.
Что я увидел.
1. Низкий уровень преподавания колег. Очевидно, что люди, умеющие хоть как-то программировать, не будут за такие копейки работать в универе. Остаются те, кто не может. Поэтому студенты брошены сами на себя. После окончания учебы программистами (нормальными) становятся только те, кто занимался самообучением.
2. Плохо устроена программа обучения. За пять лет нужно изучить много направлений. По сути, не получается ни одного. Я учился не на программиста, но предмет был несколько лет. Тупо один паскаль. Но только благодаря такому количеству часов мы почти Кнута прошли. Стуктуры данных и алгоритмы. У программистов на предмет ООП столько, что не успевают толком до виртуальных методов дойти.
Плюс слабая математическая подготовка. Это всё не только от уровня преподавания, а от распределения часов.
Т.е. выходят люди из вузов — база плохая. Но далее, работа и желание развиваться — всё поправит. Но и тут есть несколько этапов (сужу по себе):
1. Человек должен научиться писать программы хоть как-то. Циклы, ветвления, алгоритмы. Хорошо, если эта база хорошо проработана, но не важно. После этого человек ощущает, что может написать всё и часто такие устраиваются уже на работу. Что-то пишут, но говнокод пока.
2. Изучают паттерны, примеры реализованных проектов и т.д. Важный этап в обучении. Но сам способ обучения — человеку предлагают решения на примерах. И у него складывается ощущение, что надо в любой задаче сразу увидеть архитектуру, добиваться гибкости, использовать побольше паттерны. Часто люди изучают UML и упираются в то, что надо разрабатывать архитектуру ДО. И всё правильно прорисовать. Ну и куча ереси рождается — жалуются, что требования меняются, а они не должны, что в правильных компаниях такого не допустят, что начальник-архитектор должен всё сам разрисовывать, а кодерам давать схемы, тогда будет рай.
3. Возврат назад в природу. Отказ от паттернов и архитектуры. Это не шутка. Этот этап — доработка навыков кодирования и получение знаний о кодировании. Т.е. книги о рефакторинге, тест-драйвинг девелопменте, домейн-драйвинг девелопменте (все книги прорабатываются критично), теории функционального программирования. У человека появляются навыки, как правильно выходить из той или иной ситуации. Как правильно решать возникшие конкретные задачи. Как не дублировать данные или код. Какой код плохой. И расширяется список способов оценки кода. Когда у человека появятся навыки кодирования и оценки, он перестает бояться кодировать без тщательного планирования.
Где-то так. Главное на втором этапе не застрять. Грубо проектировать масштабируемое ядро на бумаге — конечно можно. Тут вопрос баланса, что такое грубо, а что нет. В идеале, возможно, есть такие джедаи, которые вообще не рисуют и даже не воображают. А умеют доращивать код, доращивая юз-кейсы. Возможно, к этому надо стремиться, но ни я таких людей не встречал, ни сам так в чистом виде не пробовал. Минимальное ядро всё таки проектировал.
Вот, в моем проекте написали класс, который является «сообщением». В него вставили тип и передают список параметров object. Уверен, что гордятся этим. Недавнее изобретение. А то, что нарушилась статическая типизация и забили болт на шарп, как таковой, создавая «новое измерение» в сверхгибком параллельном и непересекающимся с шарпом пространстве — никто не думал.
Кстати, страдаю как раз по причине того, что ищу баги в этом чужом коде второй день. И это непросто. Не знаю, как людей можно отучить от целенаправленного нанесения вреда.
Архитектура — это для меня в последнее время самое злое ругательство.
По порядку:
1. Архитектура БД и классов. Худший из способов — это строить архитектуру до написания кода. Опытному программисту сам код по разным показателям подсказывает, где лажа и что можно изменить. Выбирая листочки или UML — вы выбираете другой язык для моделирования — не язык программирования. Графические языки иногда более наглядны, но всегда более ущербны. И часто не могут показать лажу. Потом, один из главных принципов ООП — скрывание внутренностей — т.е. инкапсуляция. UML нарушает этот принцип по полной, делая анатомический разрез. Код не является настолько иногда наглядным как UML. Но как раз в этом его сила — он стимулирует писать, отражая более прямым образом суть. Цель — код должен читаться легко и понятно, как книга, которая описывает, что должно выполняться.
UML — хорошее средство документирования для более быстрой передачи знаний другим программистам о продукте. Но такая документация все же делается после, а не до. Проектировать так — плохо.
Хотя, не буду однозначен, проектировать на листочках ручкой можно и полезно, но очень грубо. Не подробно, развешивая на стену все зависимости. А только на следующий этап.
2. Переменные, методы и комментарии. Переменные и методы да, обязаны называться как надо. В итеративной разработке именно поэтому они часто переименовываются. Но замечания по поводу комментариев. Комментарии — это «естественный» язык. Сам код надобно писать так, чтобы он отражал то, о чем он написан. Не какие-то реализации, а смысл. Комментарии — это параллельно ведется на другом языке описание. Если рука тянется писать комментарии — значит лажа в коде, он не выполняет своей функции — ясного изложения мыслей. Второй минус комментариев — это язык, который не имеет никакого отношения к продукту, к его компиляции (как и UML), а значит это дополнительная вещь, которая по сути дублирует логику и требует еще и поддержки. При этом поддержка эта сложнее, чем поддержка кода. Потому что у вас нет средства компиляции и проверки соотвествия требованиям.
Я говорю не обо всех комментариях, а о комментариях внутри методов. Комментрарии, описывающие классы или методы — полезны. Но опять же, они не должны брать на себя функцию объяснения, что делает класс, который называется как попало и из его названия ничего не скажешь.
Иногда комментарии в коде нужны — бывает, что код подвергался оптимизации и уже не прямо выражает смысл. Или просто выбран сложный алгоритм. Но всё же вот так — с комментариями нужно быть осторожным, они ведутся по остаточному принципу, когда другие средства повышения читаемости кода не дали результата.
3. Копипаст. Проблема копипаста полезна для понимания рефакторинга. Представьте, что у вас уже есть код и в нем есть похожие куски. Вот, самое время подумать, возможно есть лажа. И вынести в отдельный метод. Классика и наверное многие возмутятся, к чему я это пишу. А пишу я к тому, что я не сторонник проектирования наперед. Проблемы решаются по мере поступления. А у вас подход — решить всё в воображении и предвидеть, что понадобится. И вы проблему копипаста рассматриваете — как проблему недопущения копипаста. Я рассматриваю, как проблему исправления копипаста. Если код повторяется два раза — сингнал рефакторить. При этом он должен повториться два раза. А не «не должен вообще».
Решил написать комментарий, потому что сейчас страдаю от чрезмерной тяги людей архитектурить. При этом наперед. Когда простая задача по смыслу породила больше 20 классов, каждый минимум на две страницы. Хотя как по мне, кода должно быть максимум на две страницы. Аргументируют это так: чтобы потом можно было просто писать, не задумываясь об архитектуре.
Вот как раз, теперь и потом придется задумываться постоянно. Не было во Вселенной ни единой уже создавшейся причины писать столько кода. И глючного, как и всегда бывает.
Код не аналогичен. Потом, я указывал, что SQL короче и яснее по сравнению с императивным кодом шарпа. Если пользоваться линкью (или лямдами, понятно, хотя я это не сказал), то код будет более менее одинаковый для восприятия и по объему.
Плюс бывают и более выразительные языки.
Решать тут нечего.