понедельник, 10 мая 2010 г.

[prog.flame] Почему жесткий coding style guide ущемляет индивидуальность

Отвечаю на вопрос ув.тов.Stan в комментарии к заметке о форматировании кода в Java:

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

Я вижу две главные причины:

Комфортность. Уже давно говорят, что программистам нужно обеспечивать комфортные условия работы. По крайней мере об этом пишут ДеМарко и Листер в “Человеческом факторе” (первое издание вышло в 1987).

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

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

Например, за годы работы у меня выработалась привычка ставить открывающую круглую скобку сразу после ключевого слова или имени функции. Т.е. я привык писать “if(“ или “add_item(“, а не “if (“ и “add_item (“. Эта привычка сказывается во время чтения исходников. Т.е. я уже автоматом выделяю синтаксические конструкции при беглом просмотре года. Но, когда я попадаю в чужой код, набранный в другом стиле, скорость восприятия снижается, я уже не вижу знакомых мне конструкций.

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

Право выбора. Способность придумывать конструкцию изделия – это способность выбирать. Факториал можно подсчитать рекурсивно, а можно итеративно. Какой способ предпочесть в данном конкретном случае? Очередь транзакций можно хранить в ОП, можно в файле на диске, можно во встраиваемой СУБД, можно в клиент-серверной СУБД. Что выбрать?

Чем опытнее становится разработчик, тем более важные решения ему придется принимать. Самостоятельно принимать и нести за это ответственность. Но с чего все начинается? С мелочей. Разработчик сам должен научиться делать выбор между именами i и index в разных контекстах. Точно так же он должен сам решить для себя, удобнее ли ему делать пробел между if и открывающей круглой скобкой и должна ли открывающая фигурная скобка располагаться на той же строке или на следующей. Человек делает свой выбор и берет на себя ответственность.

Право выбора и комфортность дают еще одну важную возможность – возможность смены предпочтений. Время идет, мы меняемся. Например, я с 1992-го по 2001 год использовал стили CamelCase и camelCase. Но потом перешел на lower_case, поскольку стали сильно уставать глаза при работе с camelCase – больше нужно было усилий на восприятие идентификаторов вида IllegalIndex, в отличии от варианта illegal_index. Затем, уже используя lower_case я менял свои предпочтения по поводу переноса длинных выражений на новую строку и по поводу блоков в фигурных скобках. Было время, я писал так:

if( is_valid_trx_id( context_storage.get_current_context(), trx.id(),
   user_manager.find( trx.user_id() ) ) ) {

   try_enqueue_trx( trx );
   start_new_child_process_if_necessary();
}

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

if( is_valid_trx_id( context_storage.get_current_context(), trx.id(),
   user_manager.find( trx.user_id() ) ) )
{
   try_enqueue_trx( trx );
   start_new_child_process_if_necessary();
}

Еще спустя какое-то время я пришел к другому стилю:

if( is_valid_trx_id( context_storage.get_current_context(), trx.id(),
   user_manager.find( trx.user_id() ) ) )
   {
      try_enqueue_trx( trx );
      start_new_child_process_if_necessary();
   }

А потом и к такому:

if( is_valid_trx_id(
      context_storage.get_current_context(),
      trx.id(),
      user_manager.find( trx.user_id() ) ) )
   {
      try_enqueue_trx( trx );
      start_new_child_process_if_necessary();
   }

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

Все это было возможно благодаря тому, что я не был связан рамками жесткого coding style guide.

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

if( is_valid_trx_id(
      context_storage.get_current_context(),
      trx.id(),
      user_manager.find( trx.user_id() ) ) ) ...

if (is_valid_trx_id (context_storage.get_current_context(),
      trx.id(),
      user_manager.find (trx.user_id()))) ...

if (is_valid_trx_id (context_storage.get_current_context(), trx.id(),
      user_manager.find (trx.user_id()))) ...

if(is_valid_trx_id(context_storage.get_current_context(), trx.id(),
      user_manager.find(trx.user_id()))) ...

Прочитать такой код можно (некоторые фрагменты чуть проще, некоторые чуть сложнее). Но все это приемлимо.

А вот за такое нужно уже давать по рукам:

if(is_valid_trx_id(context_storage.get_current_context(),trx.id(),user_manager.find(trx.user_id()))) ...

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

Поэтому я лично считаю, что в оформлении кода нужно поступать с программистами как с маленькими детьми. Давать свободу выбора, но из нужного нам набора вариантов. Объясняя при этом, откуда возникает каждое ограничение. Что-то вроде: “На улице сегодня прохладно, поэтому под штанишки нужно одеть колготки. Можно одеть эти или эти, выбирай, какие ты оденешь. И мы сегодня пойдем в новых ботиночках, поскольку старые тебе уже маленькие и если ты наденешь их на колготки и носки, то тебе будет больно ходить.” При таком стиле общения мы и своей цели достигаем (не выпускаем чадо на улицу плохо одетым), и даем ребенку возможность почувствовать себя личностью с правом выбора.

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

Кстати говоря, выбирать все равно будут далеко не все. Изрядное количество возьмет за основу рекомендованный стиль и не будут париться. Зато меньшинство, которое по причине каких-то собственных комплексов захочет выебну создать свой стиль, вот они, как маленькие дети, почувствует себя личностями с правом выбора ;)

4 комментария:

Blob комментирует...

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

Было бы интересно послушать. То есть - принципиально понятно, но как говориться "из первых уст..." :)

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

eao197 комментирует...

>Было бы интересно послушать. То есть - принципиально понятно, но как говориться "из первых уст..." :)

http://eao197.blogspot.com/2010/05/prog_11.html

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

К этому нужно добавить еще и переносы так, чтобы длина строки не превышала 80 символов.

Stan комментирует...

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

А что произойдёт, если в компании одновременно работают человек 200 и каждый пишет не только свои проекты, но и, при необходимости, может править любой файл? Это просто невозможно, чтобы был выбор для каждого CamelCase или lower_case.

eao197 комментирует...

>Но вопрос, который остался за кадром - а как эволюция вашего стиля отражается на тех, кто работает совместно с вами. Они тоже переходят на ваш стиль, или в вашей компании много различных стилей?

Приходится следовать за мной ;)

Если серьезно, то должен быть сделан твердый выбор -- либо CamelCase, либо lower_case, либо какой-то еще. После чего все должны ему следовать.

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

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

У нас для C++ проектов был принят стиль, которым я когда-то пользовался. С тех пор он стал более "широким" но сохранил основные черты: http://eao197.narod.ru/desc/lower_case.htm (кстати говоря, его уже пора подправить, а то он слишком категоричен местами).