Примечание переводчика: лично я согласен с автором.
Avdi, должны ли мы использовать стандарт разработки?
Да.
Какой стандарт нам использовать?
Не думаю, что есть какой-то один «правильный» стандарт. Каждый проект должен выработать свой стандарт на основе своих нужд и предпочтений разработчиков.
Думаю, что разнообразие стилей в сообществе хорошо. Это хорошо даже для большой организации.
Когда я работал в такой организации, у нас была пара рекомендованных стандартов для любого используемого нами языка. Но окончательный выбор стандарта для каждого нового проекта оставался на совести тимлида.
Я думаю, что выбор стандарта для каждого конкретного проекта — хорошая детализация. Таким образом, у каждого отдельно взятого репозитория и каждой команды может быть отдельный стандарт.
Как нам выбрать стандарт разработки?
Многие сообщества языков программирования уже выработали общие стандарты. К примеру, если вы пишете код на C, вы можете выбрать стандарт разработки GNU. Или если вы разрабатываете на Ruby, вы можете использовать стандарт сообщества, поддерживаемый Божидаром Бацовым: Bozhidar Batsov’s community-influenced standard.
Эти стандарты — плод мыслей и дебатов сообщества. Поскольку соглашения, входящие в эти стандарты, основаны на решении большинства, это хорошая точка отсчета.
Но некоторые решения в этих стандартах — глупые и неправильные!
Истина. Поэтому я и говорю, что стандарты сообщества — хорошая отправная точка. Когда у вашей команды есть четкие возражения по определенным соглашениям в стандартах — наступает время для модификации стандарта под свои нужды.
Наша команда разделилась пополам в выборе определенного пути! Как я могу убедить другую половину?
Будем честны: очень маловероятно, что команда действительно разделилась пополам. Обычно есть один-два человека с одной стороны и примерно столько же с другой, остальная же команда не выбирает ни одну сторону.
Хорошо, так как мне убедить этих людей по другую сторону баррикад?
Уверенность другой стороны могут поколебать хорошо обоснованные, реальные примеры того, что ваш вариант изящнее раскрывает суть кода, лаконичнее пишется или лучше показывает опасные места.
Это не так, просто мой стиль красивее. В любом случае, это более идиоматичный подход.
После многих лет знакомства с разными стандартами разработки я могу сказать, что сильнее всего на отношение к стандарту влияет знакомство с этим самым стандартом. Даже не помню, сколько раз я приходил в проект со стандартом, который я просто ненавидел, но после шести недель работы переставал об этом беспокоиться. Иногда я даже использовал его как новый стандарт для собственных проектов.
Например, когда я начинал разрабатывать на C++, я выравнивал фигурные скобки так, чтобы можно было посмотреть выше и сразу увидеть открывающую скобку в той же колонке:
int main(int argc, char** argv)
{
std::cout << "Hello, world!";
}
Позже я встретился с кодом в стиле GNU/GNOME, где открывающая скобка стоит на строке с объявлением метода. Аргх!
int main(int argc, char** argv) {
std::cout << "Hello, world!";
}
Я пожал плечами и продолжил. Вариант в GNU-стиле даже показался мне предпочтительнее.
Мне встречалось множество примеров кода вроде «А против Б», где вариант Б представляли объективно красивее варианта А. Большую часть времени мне было сложно увидеть разницу, которую автор полагал очевидной.
В Ruby популяризованное Rails соглашение о выборе между фигурными скобками и do...end, основанное на количестве строк кода внутри блока, немного глупо. Мне говорили, что одна из причин такого соглашения в том, что do...end на одной строке объективно выглядит ужасно:
h.fetch(:some_key) do raise ":some_key is required" end
Лично я не вижу ничего плохого в этой строке. Объективные эстетические суждения плохи в качестве правил.
Стоит помнить, что «стандартный» синтаксис языка обычно немного странный в начале. Например, в каждом основанном на C языке мы обозначаем «сообщение» или «аттрибут» примерно так:
farm_boy.fetch_me_that_pitcher()
Так как это слова на английском, давайте рассмотрим это с точки зрения английского языка.
Точка обычно указывает конец предложения, или разделитель между целой и дробной частью числа. Здесь она стоит на том месте, где обычно мы ставим запятую:
"Farm boy, fetch me that pitcher"
Или двоеточие:
"Farm boy: Fetch me that pitcher!"
Тем временем слова отделяются друг от друга символом, которого новички вообще не знают. Я уже даже не хочу думать о кавычках.
Окей, хорошо, стиль субъективен. Но некоторые вещи действительно делают код проще, или ошибки заметнее!
Да, думаю это так.
И мой тимлид делает неверный выбор!!
Возможно, что у вас действительно есть хорошие, целостные, ориентированные на качество кода аргументы в пользу определенного стиля. И вопрос выбора стандарта действительно может развести вас и вашу команду по разные стороны баррикад.
Есть один важный вопрос, который необходимо задавать себе, ратуя за выбор определенного стиля кода. Это настолько важно, что я напишу его отдельной строкой.
Как дорого проекту обойдется ваша правота?
Давайте посмотрим: вы полностью правы насчет выбора стиля или практики разработки. Ваши оппоненты неправы и глупы.
Но спросите себя, сэкономит ли ваша правота достаточно, чтобы компенсировать:
- Потраченные часы всей команды на совещания касательно стиля, которые не заканчиваются ничем толковым?
- Потерю хороших отношений как результат столкновений лбами на этих совещаниях?
- Цену огромных цепочек в почте с обсуждениями плюсов и минусов разных стандартов?
- Злость и гнев вашей команды, когда они будут вынуждены следовать стилю, который им не нравится, но который им в итоге навязали?
Некоторые стандарты разработки стоят всего этого. Но я рекомендую хорошо подумать, прежде чем бросаться в бой.
Стандарты разработки предохраняют от дурных привычек, но в чем их главный плюс?
Стандарты разработки дают программистам, особенно неопытным, определенную защиту от дурных привычек, но я не считаю это главным преимуществом. Для меня главный выигрыш от стандартов — это последовательность.
Сравним два блока кода на Ruby:
# Block #1
output = []
while word = input.shift
unless STOP_WORDS.include?(word.downcase)
output << word
end
end
# Block #2
output = input.map(&:downcase) - STOP_WORDS
Очевидно ли на первый взгляд, что оба блока делают одно и то же? Более важно: очевидно ли, что несмотря на аналогичную функциональность, блоки имеют мелкое, но важное различие?
Реальная ценность стандарта разработки в том, что он делает похожие вещи похожими, а разные — разными. Такая последовательность помогает команде быстрее понимать суть написанного кода. В свою очередь это означает, что они могут работать на более высоком уровне абстракции большую часть времени.
Как это понимать для легаси-кода? Должны ли мы обновлять каждый файл в проекте под новый стандарт?
Одним словом — нет. Не вижу никакой пользы в том, чтобы потратить часы на обновление каждого файла в проекте строчка за строчкой согласно новому стандарту разработки. Это не стоит тех затрат времени и усилий, и может принести новые баги в легаси-код.
Что, если я вношу изменение в легаси-код? Должен ли я обновить весь файл, раз уж я за него взялся?
Мой ответ отличается от общепринятого мнения и порой удивляет людей.
Ответ — нет. Обновление стиля легаси-кода потребует времени, создаст большой diff и увеличит шанс случайной регрессии.
Значит ли это, что я должен использовать новый стиль только в новых или измененных участках кода?
Давайте конкретизируем вопрос. Представьте, что это кусок легаси-кода:
STOP_WORDS = [
'i',
'we',
'if',
'and',
'the'
]
Предположим, ваша задача — добавить слово «they» в список. И по новому стандарту разработки все строки должны обрамляться двойными кавычками.
Нужно ли обновить весь список в процессе добавления нового слова?
STOP_WORDS = [
"i",
"we",
"if",
"and",
"the",
"they"
]
Или применить новый стандарт только для добавленной строки?
STOP_WORDS = [
'i',
'we',
'if',
'and',
'the',
"they"
]
Как я говорил раньше, для меня ценность стандарта — сохранение последовательности. Последовательность важна на уровне проекта, но она важна и на уровне файла. Поэтому я не думаю, что стоит следовать новому стандарту, когда вы обновляете файл, написанный по старому стандарту. Напротив, если в файле уже есть последовательный стиль оформления, вам стоит постараться выдержать его.
Это значит, что если слова в списке обрамлены одиночными кавычками, вы должны добавить новое слово в одиночных кавычках.
STOP_WORDS = [
'i',
'we',
'if',
'and',
'the',
'they'
]
Если вы часто обновляете файл с легаси-кодом, тогда мне кажется хорошей идеей запланировать обновление конкретно этого файла по новым стандартам. Но не думаю, что стоит превентивно обновлять малоиспользуемые файлы. И я не поддерживаю локально несогласованные изменения только ради того, чтобы оставаться в рамках стандарта.
___
Что думаете о стандартах разработки? Какие стандарты применяете? Рассказывайте.
Комментарии (104)
maxru
06.05.2016 17:26+8Могу сказать только о том, что каждый раз, когда я делаю ревью древнего legacy-класса, в котором какой-то добрый человек автоформаттером IDE подогнал всё под текущие стандарты, у меня неистово бомбит.
Хотя бы потому, что конкретные изменения по таску найти сложновато.vsb
06.05.2016 19:06+1Надо в 2 коммита делать. 1 коммит — работа автоформаттера (которую ревьюить смысла нет), второй коммит уже, собственно, изменения.
leventov
06.05.2016 21:59Это полбеды. Когда еще заботливый форматтер переставляет декларации в файле в "правильном" порядке, какие-либо изменения вообще отследить невозможно, ломается даже "Show Git history for method"
babylon
06.05.2016 17:37-4Нужно не только использовать существующие стандарты, но и дополнять их, не нарушая при этом обратной совместимости. Классический пример JSON. Совершенно непригодный для промышленной разработки академический стандарт, но тем не менее повсеместно используемый. Все закрывают глаза на неудобства, так как он широко распространен. И как будто так и надо. Пофигизм и безволие.
Fedcomp
06.05.2016 18:35+1А можно конкретнее чем json непригоден?
bromzh
06.05.2016 18:43+5Отсутствие комментов, один вид кавычек, необходимость эти кавычки ставить в ключе, отсутствие trailing comma (это когда нельзя делать так:
[1, 2, 3,]
), скудность типов. Это из того, что сразу вспомнилось.khim
06.05.2016 19:44+3Вы перечислили несколько важных преимуществ JSON'а, которые, собственно, людей к нему и привлекают и облегчают его использование — в чём проблема-то?
P.S. Вы, возможно, хотели сказать, что JSON не слишком удобен для написания его человеком — что, отчасти, правда, но… наши недостатки — продолжение наших достоинств. Там где удобство чтения/разбора важнее удобства напиcания (то есть почти везде) всё, что вы перечислили — достоинства.
Lure_of_Chaos
06.05.2016 23:21+2Отсутствие комментов
1. Комментарии свели бы всю красоту лаконичности формата к нулю.
2. Формат является «обрезанным» способом записи объектов в JS и предназначается скорее для общения техники, нежели людей.
3. В JS комментарии были.
4. Пришлось бы изобретать еще одну форму записи.
5. Привело бы к усложнению (и замедлению) парсеров.
6. Поскольку формат свободный (в том смысле, что нет аналога схем xsd и dtd), то ничто не мешает добавить «лишние» данные в качестве комментариев.
один вид кавычек
Единообразие — прекрасно! Кстати, в статье обсуждался этот вопрос, в абзаце про legacy.
необходимость эти кавычки ставить в ключе
Скорее, для совместимости с JS, чтобы не ломать голову ни человеку, ни машине, как интерпретировать ключ.
отсутствие trailing comma
Опять же, для упрощения интерпретации, обход возможных неоднозначностей (не понимать ли запись ",]" как еще один пустой элемент?)
скудность типов
А зачем в данных разнообразие типов? Не думаю, что в JSON очень уж нужно различать знаковые и беззнаковые целые, например.
Т.е. JSON как формат, не слишком нуждающийся в упаковке (скажем, сравните с избыточностью XML) прекрасно справляется со своей функцией краткой записи данных и простоты разбора\генерации, не потеряв при этом в читабельности. Это как раз то, почему он так популярен, вовсе не «Совершенно непригодный для промышленной разработки академический стандарт».
Кроме того, он совсем не мешает жить другим не менее популярным форматам, таким, как HAML\YAML, например. Тем не менее, и они имеют свои преимущества и свои недостатки, а потому и используются совсем не там, где JSON, там бы они не подошли.
Ну и наконец, популярность != засилие, не надо, пожалуйста, вот этого " И как будто так и надо. Пофигизм и безволие."babylon
07.05.2016 18:05+41. Лично мне комментарии в конфигах бы не помешали. Я бы по крайней мере знал бы, что это комментарии и в трафик их не пускал.
2. Видимо это люди читают JSON и XML, сотни миллионов раз открывая код страницы market яндекса, чтобы наконец понять, что так быстро заполняет память их телефона.
3. Комментарии в JS оставим без комментариев.
4. Не пришлось. Всё изобретено уже в XML,JSONNET.
5. А ничего так повторно парсить большие клонированные ноды в строку по несколько раз? Комментарии в этом смысле линейны и не избыточны. Их вряд ли клонируют.
6. Мешает. Ибо не хочу свободы, хочу стандарта. Чтобы всем было понятно, что #,/**/.// это комментарии.
7. Ключ это то, что слева от :, и ломать ничего не надо.
8. К слову сказать, JSONPath ругается на "." в value. Но все всё равно старательно портируют ошибку. Возможно кроме китайцев.
9. Разнообразие наверно для того, чтобы не посылать кучу ответов с разными данными.
Как-то так…Lure_of_Chaos
07.05.2016 21:17+1Соглашусь, что так было бы удобнее… Если бы было… Ну а на «нет» и суда нет, как уж получилось.
khim
08.05.2016 14:02Не было бы так «удобнее». Вот как раз все рассуждения выше выдают их академичность. «В траффик» он их пускать не будет, ага. Свободы и стандарта он хочет. Какого стандарта? Такого?
<html> <body> <script type="text/javascript"> //<!-- document.write("Hello World!"); //--> </script> </body> </html>
А ведь недавно куча сайтов так выглядели — и в книжках именно так рекомендовали писать.
Ещё раз: либо комментариев у вас в стандарте и в соответствующих документах нет (а тогда и вырезать нечего), либо они есть — а тогда их под что-нибудь важное приспособят (и тогда они, по большому счёту, перестанут быть комментариями — но мы вроде о практике тут говорим, а не об академических изысках).
Отсутствие комментариев в JSON — это очень важное практичнеское преимущество этого стандарта. Если конкретно в вашей программе вы хотите-таки иметь комментарии — ну так разрешите их, это-то как раз несложно.
Но таки именно то, что куча парсеров не признают комментариев и гарантирует, что всевозможные шаблонизаторы (где комментарии могут быть как раз вполне уместны) вырежут комментарии до того, как файл превратится в конечный JSON, который вы будете отдавать кому-то ещё и «в траффик» они не пойдут — что может быть практичнее?
Кстати о практичеости. Заметьте, что JSON пришёл в мир, где всё уже было «предрешено»: «все идут в XML, всё идёт в XML, везде будет XML». В 2001м, когда JSON появился, уже были и XML-RPC/SOAP и XHTML и куча других вещей. И там было и «разнообразие» и «валидация» и много всего другого. И даже комментарии, кстати.
А потом появился JSON — и прочие языки обмена данными стали почему-то менее популярны. Можно долго спорить — почему это произошло, но говорить о том, что это «академический стандарт» — это уже Оруэлл: JSON был «слеплен из того, что было» и решал практические задачи — какая ж тут академичность?
Вот «хотелки» не решающие ровным счётом ни одной задачи и исходящие из того, что люди будут обладать свободой и при этом будут соблюдать стандарты (ага, щаз: это же люди, а не ангелы) — вот это как раз «академичный подход», не имеющий к практике никакого отношения.
Потому что у вас получается, что если Google переводит GData в 2005м с XML на JSON, то это происходит потому, что JSON в 2016м используются «буквально везде». У нас тут не Океания пока и логика «Океания всегда воевала с Остазией» не работает: всё-таки причина появляется перед следствием. Если в 2005м GData начинает поддерживать JSON — то это происходит потому, что он был удобнее, чем альтернатива, а не потому, что в 2016 JSON используется «буквально везде».
P.S. Используется «буквально везде» — кстати отличный пример практического довода. И хотя он не перешибает всех других доводов (иначе бы и на JSON никто переходить не стал и переход на IPv6 не происходил бы), но он — действительно весьма весом. Когда вы приходите со своими идеями «а давайте миллионы компаний потратят миллиарды долларов, чтобы я смог получить экономию в три секунды и десять центов», то ваши предложения отвергаются не из-за того, что кругом «пофигизм и безволие», а просто потому что людим кругом умеют считать деньги и время. Чего вы, похоже, babylon делать не научился пока.
P.P.S. Кстати форматы далеко не все остаются в неизменном состоянии. В качестве примера — посмотрите на Markdown и его превращение в CommonMark. Вот там совместимость — не была основным достоинством формата и потому расширения было делать проще. Однако в конце-концов это изрядно всех «достало» и стандарт всё же появился. Посмотрим — приживётся ли. А у JSON'а — его простота, лаконичность, неизменность и совместимость (сначала с JavaScript/ActionScript, сейчас — с сотнями, если не тысячами, парсеров) — это основное достоинство. Неудивительно, что идеи отказаться от него в угоду секундной экономии отвергаются! Где вы тут видите «академичность»?
sshikov
09.05.2016 17:59>А зачем в данных разнообразие типов?
Попробуйте написать проект, где вам нужны даты/timestamp, которых в json нет. Скорее всего ваше мнение изменится довольно быстро.
Целые тут ни при чем совершенно — речь про то, что те типы, которых в языке нет, очень сложно однозначно восстановить при разборе, потому что я JSON нет и метаданных для типа, в том числе. Т.е. без схемы вы не можете понять, это вам пришло число, или дата в виде количества миллисекунд от начала эпохи, пришла строка или опять же дата в формате ISO 8601.khim
10.05.2016 02:52Таки да, JSON не предназначен для разрбора программой, которая о нём ничего не знает. И? Любой другой формат может добиться такого же успеха. Ну знаете вы, что в формате есть поле useWord97LineBreakRules и у него два значения — дальше что?
dom1n1k
07.05.2016 01:30+1Нет ничего идеального. Но альтернатив с подобным соотношением простота/удобство/понятность/читаемость/однозначность как-то не видно. XML это ад в сравнении с JSON.
Suvitruf
07.05.2016 07:53-4Например yaml.
ExplosiveZ
07.05.2016 11:58+1yaml еще хуже, там нужно пробелы правильно расставлять. JSON в этом плане в миллион раз удобнее.
Sirion
06.05.2016 17:53+3There are a two kind of people
Простите, это на каком языке?Areso
06.05.2016 18:04+2Очевидно, на английском. Возможно, на английском с ошибками. Вероятно, должно было быть ...kinds…
Sirion
06.05.2016 18:12+4По идее, ещё артикль лишний. Мысль о том, что в статье, переведённой с английского, на видном месте большими буквами может красоваться надпись на том же английском сразу с двумя ошибками, показалось мне настолько невероятной, что я решил уточнить, правильно ли определил язык.
Areso
06.05.2016 21:41Возможно, у них программисты тоже имели не самую высокую оценку по английскому языку в школе)
Возможно, картинка была подготовлена рано утром или поздно вечером. Я тут в обед прочитал, что я написал рано утром на форуме — так у меня там тоже возникли вопросы к себе, насчет того, является ли русский язык мне родным или нет… Было стыдно.
vosi
06.05.2016 17:59+2three
if cond: return
unel
06.05.2016 18:05а так же
if (cond) return;
и
return if cond;
seregamorph
06.05.2016 18:17+3Мой любимый пример из личной практики на тему фигурных скобок в однострочниках:
if (logger.isLoggable(Level.DEBUG)) // level.debug("someDebugMsg"); doSomethingValuable();
Собственно, когда система вышла в production и настройки логгинга были изменены, баг внезапно и вылез.
Что касается конкретно Вашего примера — тут в целом ок, т.к. условие и действие в одну строку.Fedcomp
06.05.2016 18:37-2Такие вещи должны тестами ловиться.
MaximChistov
06.05.2016 19:01+5каждую сточку тестами не покроешь.
Fedcomp
07.05.2016 10:03это do_something_valuable вы тестами не поймаете? тогда это совсем не valuable.
MaximChistov
07.05.2016 14:35имхо, вы неправильно тестируете, если считаете что тест — это проверка каждой строки функции на то, выполнилась ли она
Regis
06.05.2016 21:30+7Это как раз типичный пример того, когда простейшее стилистическое правило (всегда использовать фигурные скобки для блоков) позволяет избежать целого класса проблем.
Fedcomp
07.05.2016 10:04-2Это как раз прекрасный пример где тест должен проверить что функция do_something_VALUABLE вызывается. Может вы еще правило йоды юзаете?
Regis
07.05.2016 12:42+1Вы реально пишете отдельный тест на каждый вызов в каждой функции? (Я подразумеваю, что типичные сценарии работы и так покрыты тестами).
И вы реально предпочитаете вместо чуть другого стиля кода писать дополнительные тесы дополнительными тесты?Dreyk
07.05.2016 15:04+1подразумевается, что если это действительно valuable, то оно должно быть покрыто хоть каким-нибудь (unit/integration/acceptance) тестом
Regis
07.05.2016 21:26Хорошо, если там действительно существенная часть бизнес-логики, то да, она будет покрыта тестами и пролема отловится. Но что, если там что-то второстепенное? Например освобождение памяти или другого ресурса? Т.е. тоже valuable, но, возможно, не покрытое непосредственно тестом?
seregamorph
06.05.2016 21:59+1Конкретно в этом куске кода тест скорее всего был бы успешен (в тестах обычно дебаг-логирование)
alexeibs
06.05.2016 22:26Хорошие инструменты для измерения покрытия показывают еще и покрытие кода по условиям.
Dreyk
07.05.2016 15:05не всегда. тесты должны быть максимально приближены к живому коду, иначе теряется их смысл
vsb
06.05.2016 21:05-1Могу поделиться своим стандартом. Фигурные скобки ставить не надо только если if без else, если условие короткое и тело короткое. При этом перенос на новую строку не делается. Если делается перенос на новую строку или присутствует else — фигурные скобки обязательны. Т.е.
if (x == 1) return; if (x == 1 || x == 2 || x > 100 && x < 1000) { performSomethingComplex(x + 100, x - 100, x * 100, x / 100); } if (x == 2) { f(); } else { g(); }
Regis
06.05.2016 21:32+2А потом кто-то применит автоформатирование с другим стилем, закоммтит и оппа — мы получили кейс как выше.
Правила без исключений лучше работают.vsb
06.05.2016 22:13Автоформатирование с тем же успехом уберёт фигурные скобки вокруг блока из одной инструкции. Стиль должен был един и его соблюдение должно автоматически проверяться при коммите или слиянии.
semmaxim
06.05.2016 22:56+1У нас в фирме, например, очень строгое правило — закомментированного кода быть не должно. За 4 года было 2 исключения и каждое подробно описывалось рядом, почему и как.
cy-ernado
06.05.2016 18:20+1Хорошо, когда можно сделать
go fmt
и никаких споров :)SerafimArts
06.05.2016 19:58+1И Ctrl + Alt + Shit + L в JetBrains IDE с попутным тыканьем новичков в доки по PSR (в стане PHP разработчиков), например.
Наверное go и php (современный) — это единственные языки, где никогда не возникает споров по поводу кодстайла.
Grox
06.05.2016 22:10+2У меня только один вопрос — зачем } else { на той же строке, что и закрывающая скобка?
SerafimArts
06.05.2016 22:53Вопрос не верный, не "зачем", а "почему". Потому что PSR основан на самых популярных практиках самых популярных продуктов. Вот и весь ответ, никакого смыслового значения "зачем" в самом стандарте нет. Взяли и основали стандарт на том, что используют большинство.
Если искать смысл в самих корнях, то подобные практики пошли, я почти уверен, из миров java, haxe, c++ и прочих. Где подобное является стандартными вещами (ну в C\C++ несколько стандартов, так что моё утверждение на счёт подобной практики в этом языке лишь частично верное).
Размышления на тему: Положа руку на сердце, может это просто привычка использовать именно этот стандарт во всех языках, которые использовал (haxe, java, js\es, sass, less, php), но мне кажется что подобное лучше выглядит, нежели размазывание одного метода на 100500 строк. "Воздух" добавляется за счёт переносов строк, а законченное по смыслу логическое выражение, включающее этот самый "else" более компактно и лучше бросается в глаза, нежели если скобки переносить на новую строчку. Фиг знает, споры о кодстайле могут продолжаться вечность и "лучшего" всё равно не найти.
Grox
07.05.2016 23:49+1Я смотрел голосование по этой теме в PSR:
А) 10 голосов за
if () { ... } else { ... }
Б) 6 голосов за
if () { ... } else { ... }
Так что да, большинство, но очень не хватает их обоснований. По каким причинам этот голос. Потому что у меня есть несколько обоснований относительно варианта Б и слабые обоснования для А.Kendrick
09.05.2016 17:59+2Мне, например. вариант Б просто режет глаза. И я объясню почему: if..else это одна единая конструкция. Поэтому "} else {" выглядит как продолжение конструкции, а «else {» с новой строчки больше похоже на новую конструкцию, никак не относящуюся к предыдущему if-у. То же самое и с фигурной скобкой на той же строчке, что и сигнатура метода: код метода непосредственно относится к его сигнатуре.
MacIn
10.05.2016 16:16Можно и в обратную сторону рассуждать: if..else — это единая конструкция, а }else{ — нет, потому что {}, т.н. блок — отдельная сущность.
Kendrick
10.05.2016 17:29Любая конструкция состоит из блоков, одного или несколько. Причем тут блоки? Я говорю именно про визуальное восприятие. Не пойму чем тогда по вашей логике отличается "}else{" от «else{»?
MacIn
11.05.2016 00:20Конструкция так называется — «блок».
Есть условный оператор, есть операторы цикла, есть выбор по ключу, есть блок.
Вы же можете написать
for (...)
;
А можете в теле использовать не один оператор action, а блок.Kendrick
11.05.2016 00:37Если вы понимаете под блоком именно фигурные скобки, то согласен. Я понимаю под блоком кода, как ни странно, блок кода, ему не обязательно быть обрамленным в фигурные скобки и он может быть пустым, как в вашем примере с «for». Так что в действительности любая конструкция(будь то if, for или еще чего) не имеет смысла без блока кода.
Т.е. получается такая иерархия: лексемы -> выражения -> блоки -> конструкции.MacIn
11.05.2016 02:15Не я понимаю; так называется. Оно же — «составной оператор».
Например, КиР:
3.1 Statements and Blocks…
Braces { and } are used to group declarations and statements together into a compound statement, or block, so...There is no semicolon after the right brace that ends a block.
3.2 If-Else
The if-else statement is used to express decisions. Formally the syntax is
if (expression)
statement1
else
statement2
where the else part is optional.
khim
10.05.2016 17:52Если у вас блок — отдельная сущность, вы придёте к GNU стандарту:
if (...) { ... } else { ... }
Мне лично не такой вариант не нравится (слишком много места пропадает зря из-за чего в GNU проектах любят не использовать фигурные скобки для однострочных выражений — и потом ясно, чем это кончается), но определённая логика тут есть.
Вариант же с «else {» — ужасен просто потому что у вас оказывается две отдельные, никак не связанные друг с другом конструкци: «if» и «else».
Вот «else { blah-blah-blah }» как прикажете понимать? Это вообще что такое? Это куда? Это зачем? Как так вообще можно писать???michael_vostrikov
10.05.2016 20:33Вас же не смущает конструкция «default: blah-blah-blah; break;». Ветки оператора switch пишутся отдельно друг от друга, и выглядят как отдельные не связанные конструкции. Почему бы и if так не писать?
Kendrick
10.05.2016 23:31весь switch — это одна конструкция, состоящая из нескольких case, которые, как вы правильно заметили, не связаны никак друг с другом. Поэтому так и пишутся :)
Я имею ввиду, что каждый следующий case не является продолжением другого, и один case может существовать без предыдущего. else в свою очередь не может существовать без предыдущего блока.michael_vostrikov
11.05.2016 07:23Тогда и try / catch надо отдельными блоками писать, потому что там catch не связаны между собой.
Цепочка if / elseif / elseif / else похожа на оператор switch, и elseif там тоже не связаны. case не может существовать без switch, catch не может существовать без try, else не может существовать без if.
PS: Писал так в одном проекте, когда ветки if идут отдельными блоками. Вполне нормально смотрится, код в блоках не сливается с предыдущими, особенно если условия длинные.
khim
12.05.2016 14:08Тогда и try / catch надо отдельными блоками писать, потому что там catch не связаны между собой.
Связаны. Если один операторcatch
сработал, то другой — уже ничего перехватить не может. Логически — это цепочка обработчиков (хотя внутри, конечно, компилятор может оптимизировать).
Цепочка if / elseif / elseif / else похожа на оператор switch, и elseif там тоже не связаны.
Связаны — тем же самым способом. Нельзя одну ветку рассматривать в отрыве от других.
case не может существовать без switch
Case
иswitch
— это просто "goto
под прикрытием". Он может скакать на любую глубину вложенности (правда не может мимо конструкторов и «наверх»), иерархии меток никакой нету.if
/elseif
/else
иtry
/catch
— устроены совсем не так.
khim
12.05.2016 13:32У оператора switch нет никаких «веток», извините. Когда вы используете низкоуровневую конструкцию с оператором goto, что часто у вас просто нет выбора.
Как вы предлагаете скобки в чём-то типа следующего расставлять:
switch (i % 8) while (i > 0) { case 0: x += a[ i-- ]; case 7: x += a[ i-- ]; case 5: x += a[ i-- ]; case 4: x += a[ i-- ]; case 3: x += a[ i-- ]; case 2: x += a[ i-- ]; case 1: x += a[ i-- ]; }
В случае же с if'ом у вас есть две ветки, которые очевидным образом связаны между собой.
MacIn
11.05.2016 00:11Если у вас блок — отдельная сущность
Так не у меня, так определяется стандартом, нет?
О, оказывается, самый логичный и верный на мой взгляд вариант имеет имя — GNU стандарт. Спасибо!
из-за чего в GNU проектах любят не использовать фигурные скобки для однострочных выражений
Ох, и тут совпадение.
Я бы сказал, что это очень старомодный стандарт, потому что легкочитаем без подсветки, в монохроме/на бумаге.khim
12.05.2016 14:16Так не у меня, так определяется стандартом, нет?
Ну так стандарт и в выраженииa = b + c;
несколько сущностей выделяет. Это не значит, что нужно в пять строк это писать.
Я бы сказал, что это очень старомодный стандарт, потому что легкочитаем без подсветки, в монохроме/на бумаге.
Угу — вот только часто получается, что читается и работает — программа по разному. Это — очень большая проблема.
P.S. Вообще, конечно, очень жаль что на проблему, которая, вообще-то, яйца выделенного не стоит, мы тратим столько времени. Причём забавно что в языках типа Make, которые, являются, в других отношениях, весьма убогимиэтой
проблемы нет. Естественная расстановка отступов в блокеifeq
/else ifeq
else/ — существует только одна, а споров о сущности «блоков» нету потому что никаких «блоков» нету. Это одна из вещей, которые в C/C++ сделаны отвратительно — но при этом, почему-то, это кривое и неудобное решение копируется везде и всюду…MacIn
12.05.2016 14:33Ну так стандарт и в выражении a = b + c; несколько сущностей выделяет. Это не значит, что нужно в пять строк это писать.
Это натяжка, ну да ладно.
Угу — вот только часто получается, что читается и работает — программа по разному. Это — очень большая проблема.
В случае с
if <>
{
}
else
{
}
Исполняется так, как читается.
Вообще, конечно, очень жаль что на проблему, которая, вообще-то, яйца выделенного не стоит, мы тратим столько времени
Я просто порадовался, что такой стандарт есть, только и всего.
Это одна из вещей, которые в C/C++ сделаны отвратительно — но при этом, почему-то, это кривое и неудобное решение копируется везде и всюду…
В Паскале аналогично, только в силу обозначения составного оператора(блока) словами, споров вполовину меньше.khim
12.05.2016 15:31+1В случае с
Как я уже сказал — этот стандарт мне не нравится, но я его понимаю. А вот
if <>
{
}
else
{
}
Исполняется так, как читается.
я просто не понимаю откуда может взяться и кому может нравится. Выглядит так, как если быif <...> { ... } else { ... }
if
был отдельно иelse
— отдельно. И вот этого я не понимаю. «Отдельно живущий блок» — это ещё туда-сюда, но «отдельно живущий»else
— это какая-то очень странная сущность.
khim
06.05.2016 20:04Угу. Для C/C++ есть clang-format, но у него есть большая беда: не все его используют.
Впрочем в проектах, его использующих, жить всё равно легче. Мне, в общем, всё равно как стоят пробелы (потому что почему-то тот вариант, который нравится мне редко нравится кому-либо ещё) и я ненавижу Style Guide'ы (по той же причине) — но спокойно отношусь к вещам подобнымgo fmt
.
Просто потому что когда мне даютgo fmt
— то мой код делают хуже понимаемым лично мной, но взамен экономят немножко моё время. Это я ещё могу терпеть. Но когда мне навязывают какой-то Style Guide и не дают инструмента, то это значит, что меня заставляют тратить время и делать работу, которая мне же и сделает хуже! Это — уже перебор.SerafimArts
06.05.2016 23:00+1Это абсолютно верная позиция, когда вы над кодом сидите один и никому не показываете. Но когда в команде больше 3х человек — не думаете ли, что подобные принципы немного эгоистичны? И что лучше использовать один стандарт, пнуть себя в мягкое место и переучиться (это тяжело, я прекрасно это понимаю, у каждого есть подобный опыт), который доступен и привычен большинству, нежели свой супер-удобный вариант, доступный только для таких, бесспорно замечательных разработчиков, как вы? =)
khim
06.05.2016 23:37-1Нет, не проще. Заметьте: я не настаиваю на том, чтобы кодить игнорируя все и всяческие стандарты. Но. Стандарт либо содержит объективно-проверяемые вещи, либо нет.
Первое — автомат типаgo fmt
способен проверить (а зачастую и исправить), второе — будет постоянным источником споров и разногласий независимо ни от чего.SerafimArts
07.05.2016 02:42-1Ну меня просто смутила фраза "ненавижу всяческие стайлгайды", я рад что ошибся в вашей позиции. А стайлгайды, по-моему наоборот хорошо когда есть, главное чтобы он был один. Нравится ли он или нет — не важно, главное чтобы один, иначе холивары и всё такое. Короче, почти что как сейчас в комментах к посту.
akzhan
06.05.2016 18:43+2приведение к стандарту просто надо выполнять отдельным коммитом imho.
Regis
06.05.2016 21:40Но история всё равно портится.
Я обычно следую правилу, что если приходится править старый код, то форматирование меняю только на уровне отдельных методов, причем если в них изменилась довольно большая доля кода. Т.е. если поправили пару строчек в большом методе — оставляем стиль прежний. Если поправили треть метода или больше — тогда уже можно переформатировать весь метод.m03r
07.05.2016 10:40Мы использовали
git filter-branch
, чтобы привести всю историю к единому стандарту. Заодно избавились от части конфликтов при слиянии.Regis
07.05.2016 12:49+1А, т.е. ретроспективно исправили стиль во всём проекте как если бы он изначально писался с правильным стилем? Интересное решение.
Минус, конечно, что история меняется, но конкретно в плане исправления стиля — вроде и не страшно.m03r
07.05.2016 20:22Поскольку круг людей, которые используют репозиторий, точно известен, переписывание истории прошло довольно спокойно
Regis
07.05.2016 21:29Да, с закрытым проектом такое вполне можно провернуть. И сама идея мне нравится.
Но, увы, если проект публичный, то скорее всего замена истории приведет к слишком серьезным проблемам. Аналогично будут проблемы при наличии интераграции с внешними инструментами (всякие Jira, Stash/FishEye и проч.).
justmara
06.05.2016 18:48+1Есть ещё такая точка зрения: если применять новые стандарты только к новому (изменённому) коду, то со временем будет повышаться читаемость часто модифицируемых кусков. Нетронутыми же останутся те, которые либо "просто работают", либо никому не нужны. Со всех сторон одни плюсы — и без лишних телодвижений и польза в нужном месте.
vyatsek
06.05.2016 19:19+2У нас есть стандарт. Он фиксирует спорные моменты по которым были разногласия, периодически дополняется. К проекту есть еще архитектурные заметки, из серии если вы в коде столкнулись с указанными областями, то с ними следует поступать вот так.
Описывать всё и вся не вижу смысла — всегда проще договориться и найти пример в существующем коде. Чем большее количество человек работают над одной базой кода, тем строже должен быть стандарт.
samizdam
06.05.2016 22:58Отлично выразил, ЕМНП, Роберт Мартин:
не важно какое соглашение о стиле кодирования вы примите, важно чтобы вы его приняли!
PS: Как славно что в PHP есть PSR =) Сколько безсмыленных холиваров этим предотвращено!zelenin
07.05.2016 04:52половина упоминаний о PSR на хабре заканчивается холиварами о кодстайле с аргументацией о том, что psr — рекомендация, а не стандарт. Увы, не все понимают соль принятия единого стандарта (см. цитату Мартина).
snizovtsev
07.05.2016 00:42ИМХО Не так уж важно как пробелы и переводы строк расставлять. Не вижу даже ничего суперстрашного если разные файлы форматированы по разному — все равно все будет понятно. Лучше концентрироваться на сути, например для плюсов: заставлять использовать RAII, создавать новые типы исключений вместо использования системных, порядок инклюдов, запрет using namespace в хедерах, запрет блокирующих вызовов и т.д.
daiver19
09.05.2016 17:59А потом вдруг надо чей-то файл подредактировать, а в нем разрброд и шатание. И вместо того, чтобы сконцентрироваться на коде, ты сидишь и думаешь, как бы так расставить скобочки, чтоб было консистентно с остальным проектом.
snizovtsev
09.05.2016 19:59Кривой/неконсистентный стиль можно быстро и массово поправить каким-нибудь clang-format, а затем сделать проверку стиля в CI. Все. Проблема решена. Зачем ей посвящать столько времени и сил в холиварах?
Куда сложнее читать и фиксить спагетти код функции на 400 строк с однобуквенными переменными без комментариев и кучей хаков. Который еще и не работает если пойти чуть в сторону. Вот за что нужно убивать.
MacIn
07.05.2016 01:08+2Хмм… стандарт разработки — не то же самое, что стандарт форматирования кода кмк.
torrison
07.05.2016 04:44Вечная тема. Если прокрутить Google страницы W3C валидатором, то покажет кучу ошибок… И это Google. Все очень индивидуально.
Важно наверное стандарты безопастности, а остальное уже на любителя.
EndUser
07.05.2016 06:26Если считать, что статья про стиль форматирования, а не про стандарты программирования, то у меня есть примерно такие раздумья:
1) утвердить какой-то любой стиль для коммитов.
2) наладить процесс так, чтобы участник после взятия мог запустить настроенный под себя beautifier.
3) а перед коммитом запустить beautifier обратно на утверждённый стиль (можно даже запретить коммит, если на сервере beautifier вернул код «ошибки»)
4) при этом опасные преобразования, типа уничтожения скобок, запретить по всей команде.
Mixim333
07.05.2016 19:07-2Каждый «стандарт разработки» — это как отдельный язык, на котором говорят люди в разных странах: если Вы живете в Великобритании, то будьте любезны говорить на английском, если в России — на русском. Сам пишу на C# и выработал для себя следующие правила:
1)в начале каждого класса у меня идет #region с необходимыми константами класса;
2)далее #region с полями и свойствами, причем сперва идут поля и только затем свойства;
3)далее — конструкторы;
4)и в самую последнюю очередь — методы.
Этот порядок четко соблюдаю и еще ни в одном проекте, над которым работал, он не входил в расхождения, в серьезные расхождения с ранее используемым, максимум #region'ы не использовались.
До сей поры помню случай, как изучая с помощью Far'а один из CS-файлов проекта скачанного с GitHub, увидел некоторую переменную 'a', которая в теле метода не объявлялась и в имевшемся регионе с полями и свойствами ее тоже не было — секунд 30 потратил на то, чтобы найти ее сразу над телом метода с пометкой вроде: «Костыль, позже избавиться» (мой вольный перевод с англ.)
cyber_genius
09.05.2016 16:05пытаться насаждать свой любимый стандарт стиля программирования — это лишь бесить всех, т.к. все точки зрения субъективны, а вот иметь стандарт структуры когда, куда как важнее
leremin
Я думаю, что все общепризнанные стандарты имеют право на существование. А вот что не надо делать — так это изобретать свои стандарты, например, один мой коллега закрывающие фигурные скобки пишет в одной строке. Читается это противно. А другой коллега напротив КАЖДОЙ строки пишет комментарий, что она делает — по сути дублирование кода на русском языке. Читается еще противнее.
kemply
Вы просто привязались к своему стандарту. Автор хорошо сказал: «сильнее всего на отношение к стандарту влияет знакомство с этим самым стандартом». То, что вы считаете свое мнение константой — в корне неверно.
К примеру мне нравится, когда комментарии есть напротив каждой строки также, как и краткое описание класса до его начала.
leremin
Я привязался не к своему стандарту, а общепризнанным. Даже венгерскую нотацию и то признаю. А теперь представьте реальный проект, в котором напротив каждой строки идет комментарий. Как минимум, это усложняет внесение правок: помимо правки кода, я должен изменить комментарий, а также выровнять их по горизонтали. Комментарий должен пояснять код и разъяснять неочевидные места, а не дублировать весь код:
MacIn
Смотря какой язык. Для мнемокода (ассемблер) это почти стандарт.
leremin
Да, но здесь речь не про асм идет.