Делай как вGoogle
Скачать 5.77 Mb.
|
ЧАСТЬ III Процессы ГЛАВА 8 Правила и руководства по стилю Автор: Шайндел Шварц Редактор: Том Маншрек В большинстве организаций, занимающихся программной инженерией, есть правила, применяющиеся к кодовой базе. Они определяют, как в компании принято хранить файлы с исходным кодом, форматировать код, присваивать имена, использовать паттерны, исключения и потоки выполнения. Большинство инженеров-програм- мистов действуют в рамках набора политик, управляющих их работой. В Google для управления базой кода мы поддерживаем набор руководств по стилю, которые определяют наши правила. Правила — это не предложения или рекомендации, а обязательные для выполнения законы. При необходимости их можно игнорировать только в особых утвержденных случаях. Руководства, в отличие от правил, содержат рекомендации и описание пере- довых практик, следование которым желательно, но не обязательно. Мы собираем правила и в наших канонических руководствах по стилю программиро- вания определяем допустимое и недопустимое поведение. Термин «стиль» в данном случае можно понять не совсем правильно — как набор вариантов форматирования. В действительности наши руководства по стилю — это нечто большее: они включа- ют полный набор соглашений, управляющих нашим кодом. Это не значит, что эти руководства носят строго обязательный характер. Правила в них могут предлагать, например, использовать имена «настолько описательные, насколько это возможно, в пределах разумного» ( https://oreil.ly/xDNAn ). Скорее они служат основным источ- ником правил, которым следуют наши инженеры. Мы поддерживаем отдельные руководства по стилю для каждого языка программи- рования 1 . Все они преследуют одну цель — обеспечить устойчивость кода, но между ними есть множество различий по охвату, длине и содержанию. Языки программиро- вания имеют разные сильные стороны, особенности, приоритеты и причины исполь- зования в Google. Поэтому руководства для разных языков отличаются. Например, руководства для Dart, R и Shell лаконичны и сфокусированы на нескольких общих принципах, таких как именование и форматирование. Руководства для C++, Python 1 Многие из наших руководств по стилю имеют внешние версии, которые вы можете найти по адресу https://google.github.io/styleguide . В этой главе мы приведем примеры из этих руководств. Создание правил 149 и Java включают гораздо больше деталей, углубляются в особенности языка и пред- ставлены более подробными документами. В одних руководствах больше внимания уделяется типичному использованию языка вне Google — например, руководство для Go очень короткое и добавляет лишь несколько правил к правилам и методам, из- ложенным во внешних общепризнанных соглашениях ( https://oreil.ly/RHrvP ). В других содержатся правила, принципиально отличающиеся от внешних норм, — например, наши правила для C++ запрещают применять исключения — возможность языка, широко используемую за пределами Google. Большая разница даже между нашими руководствами не позволяет точно описать, что должно включать руководство по стилю. В разных организациях могут быть раз- ные требования к устойчивости, для поддержки которой создаются правила. В этой главе мы обсудим принципы и процессы, определившие подходы к разработке правил и руководств в Google, и рассмотрим примеры, в основном из наших руководств по стилю для C++, Python и Java. Зачем нужны правила? Итак, зачем нужны правила? Цель правил — поощрить «хорошее» поведение и пре- пятствовать «плохому». Интерпретация понятий «хорошо» и «плохо» во многом зависит от организации и ее целей и потребностей. В одних организациях «хоро- шими» могут считаться модели, предполагающие использование минимального объема памяти или ориентированные на потенциальные оптимизации во время выполнения. В других «хорошим» поведением считается применение новейших возможностей языка. Третьи организации усиленно заботятся о согласованности и считают отклонения от паттернов «плохим» подходом. Сначала мы должны понять, что ценится в организации, а затем использовать правила и рекомендации, чтобы поощрять желательное поведение и препятствовать нежелательному. По мере роста организации установленные правила и руководства формируют об- щий словарь разработки, который помогает инженерам сосредоточиться на том, что должен делать их код, а не на том, как заставить его это делать. Поддерживая словарь, инженеры действуют «хорошо», даже не замечая этого. То есть правила помогают компании развиваться в выбранных направлениях. Создание правил При определении набора правил ключевой вопрос заключается не в том, «какие правила мы должны иметь?», а в том — «какую цель мы преследуем?» Концентрация внимания на цели помогает определить, какие правила ее поддерживают. В Google, где руководство по стилю является законом, определяющим нормы кодинга, мы спрашиваем не «что входит в руководство по стилю?», а «почему что-то входит в ру- ководство по стилю?» Что получает организация от набора правил, регулирующих написание кода? 150 Глава 8. Правила и руководства по стилю Руководящие принципы Рассмотрим конкретную ситуацию: в Google работает более 30 000 инженеров с раз- ными навыками и опытом. Ежедневно в репозиторий с базой кода, насчитывающей более двух миллиардов строк, отправляется около 60 000 изменений, которые, веро- ятно, будут продолжать оставаться в репозитории десятилетиями. Наши продукты уникальны, но мы сталкиваемся с типичной организационной проблемой — как со- хранить устойчивость инженерной среды при росте компании с течением времени. В этом контексте цель наших правил — помочь нам справиться со сложной средой разработки, обеспечить управляемость кодовой базы и поддержать продуктивность инженеров. Здесь мы идем на компромисс: большой свод правил, помогающий до- стичь цели, ограничивает свободу выбора. Мы теряем некоторую гибкость и даже можем обидеть кого-то, но мы выбираем выигрыш в согласованности и уменьшении числа конфликтов, обеспечиваемый официальным стандартом. Мы выработали ряд основополагающих принципов, которыми руководствуемся при разработке правил: y необременительность; y оптимизация для читателя кода; y поддержка единообразия кода; y предотвращение использования необычных или способствующих ошибкам конструкций; y при необходимости — оценка практической целесообразности применения правила. Необременительность Руководство по стилю не должно включать слишком много правил. Чтобы все инже- неры в организации изучили и начали выполнять новое правило, требуется время. Если правил окажется слишком много 1 , инженерам будет трудно их запомнить, а новичкам — изучить. Кроме того, чем больше свод правил, тем сложнее и дороже его поддерживать. Мы сознательно решили не фиксировать очевидные правила. Руководство по сти- лю в Google не носит юридический характер: отсутствие в нем явного запрета на действие не означает законность этого действия. Например, руководство для C++ не содержит правил, запрещающих использование goto . Программисты на C++ уже 1 Особую важность здесь приобретают инструменты. Понятие «слишком много» определяет не количество правил в руководстве, а количество сведений, которое должен запомнить инженер. Например, в старые темные времена, предшествовавшие появлению clang, инже- неру требовалось помнить массу правил форматирования. Эти правила никуда не делись, но благодаря появлению современных инструментов соблюдать их стало намного проще. Мы достигли точки, когда кто-то может добавить любое количество правил, и никто не обратит на это внимания, потому что инструмент применит правило автоматически. Создание правил 151 склонны избегать этого оператора, поэтому добавление правила, запрещающего его, приведет к ненужным накладным расходам. Один или два инженера могут по ошибке использовать goto , но это не повод увеличивать нагрузку для всех созданием нового правила. Оптимизация для читателя кода Еще один принцип, которому следуют наши правила, — оптимизация кода для читателя, а не для автора. С течением времени код будет читаться другими людьми гораздо чаще, чем автором. Мы предпочитаем, чтобы код был более утомительным для ввода и менее трудным для чтения. В нашем руководстве по стилю для Python мы отмечаем, что условные выражения короче операторов if и удобны для ввода, но читателю кода бывает трудно разобраться в них, поэтому мы ограничиваем их использование ( https://oreil.ly/ftyvG ). Это компромисс: мы понимаем, что инженерам сложнее снова и снова вводить длинные описательные имена переменных и типов, но готовы заплатить эту цену за удобство всех будущих читателей. В соответствии с этими приоритетами мы также требуем, чтобы инженеры остав- ляли явное доказательство предполагаемого поведения кода. Читатель должен четко понимать, что делает код. Например, наши руководства для Java, JavaScript и C++ требуют использовать специальную аннотацию или ключевое слово всякий раз, когда метод переопределяет соответствующий метод суперкласса. Без явного подтверждения читатели, вероятно, могут понять намерение автора, но это займет у них больше времени и усилий. Предупреждение о неожиданном поведении еще более важно. В C++ бывает сложно понять, что владеет указателем, просто прочитав фрагмент кода. Если указатель передается в функцию, поведение которой неизвестно, мы не можем уверенно пред- сказать ожидаемый результат. Вызывающий код по-прежнему владеет указателем? Ответственность за указатель взяла на себя функция? Можно ли продолжать ис- пользовать указатель после возврата из функции или он стал недействительным? Чтобы ответить на эти вопросы, наше руководство для C++ требует использовать std::unique_ptr ( https://oreil.ly/h0lFE ), когда предполагается передача права владе- ния указателем, или конструкцию unique_ptr , когда в каждый конкретный момент существует только одна копия указателя. Когда функция принимает аргумент типа unique_ptr и намеревается стать владельцем указателя, вызывающий код должен явно использовать семантику перемещения: // Функция, которая принимает Foo*. // По заголовку этой функции невозможно сказать, вступает ли она во владение // указателем void TakeFoo(Foo* arg); // Вызов функции не позволяет читателю понять, как изменится право владения // указателем после возврата из нее Foo* my_foo(NewFoo()); TakeFoo(my_foo); 152 Глава 8. Правила и руководства по стилю Сравните этот фрагмент со следующим: // Функция принимает std::unique_ptr void TakeFoo(std::unique_ptr // Любой вызов этой функции явно показывает, что ей передается право // владения указателем и unique_ptr нельзя использовать после возврата из нее std::unique_ptr TakeFoo(std::move(my_foo)); Данное правило в руководстве по стилю гарантируют, что все инструкции вызова будут содержать четкие доказательства передачи права собственности, когда это при- менимо, и читателям кода будет не нужно понимать поведение каждого вызова. Мы предоставляем в API достаточно информации, чтобы дать возможность рассуждать о его взаимодействиях. Такое четкое документирование поведения в точках вызова гарантирует, что фрагменты кода остаются читаемыми и понятными. Мы стремимся локализовать рассуждения, касающиеся событий в точке вызова, и избавить чита- телей от необходимости искать и читать другой код, включая реализацию функции. Также мы добавили в руководство по стилю множество правил комментирования. Документирующие комментарии (блочные комментарии в начале файла, а также перед определениями классов и функций) должны описывать структуру или назна- чение следующего за ними кода. Комментарии внутри реализации (разбросанные по всему коду) должны описывать или выделять неочевидные варианты, объяснять сложные фрагменты и подчеркивать важные части кода. У нас есть правила, охваты- вающие оба типа комментариев, которые требуют от инженеров давать пояснения для читателей кода. Поддержка единообразия кода Наше понимание единообразия в кодовой базе похоже на философию, которую мы применяем в офисах. Команды с большим числом инженеров часто занимают несколько офисов, каждый из которых имеет свою уникальную индивидуальность с учетом местного колорита, но во всех них неизменно присутствует все, что нужно для работы. Пропуск гуглера распознается всеми считывателями, любые устрой- ства в Google подключаются к WiFi, аппаратура для видеоконференций в любом конференц-зале имеет одинаковый интерфейс. Гуглеру не нужно тратить время на изучение особенностей использования устройств, и перемещаясь между офисами, он может быстро приступать к работе. Такого же единообразия мы стремимся добиться в исходном коде — любой инженер должен быстро вникать в незнакомый код компании. Локальный проект может иметь свою индивидуальность, но его инструменты, методы и библиотеки должны быть узнаваемы и понятны. Преимущества единообразия Несмотря на то что запрет на настройку устройств чтения пропусков или интерфейса аппаратуры для видеоконференций ограничивает творческую свободу, с его помощью Создание правил 153 мы получаем преимущества единообразия. То же верно и для кода: правила, требую- щие обеспечивать единообразие, позволяют большему числу инженеров выполнять больше работы с меньшими усилиями 1 : y Когда база кода единообразна по стилю, инженеры, пишущие и читающие код, могут сосредоточиться на смысле кода, а не на его оформлении. Также единообра- зие позволяет формировать фрагменты для экспертной оценки 2 . Когда мы решаем задачи, используя одинаковые интерфейсы, и единообразно форматируем код, эксперты быстро понимают, что этот код делает. Единообразие также упрощает деление кода на модули и помогает избежать дублирования. По этим причинам мы уделяем большое внимание единообразию в именовании, использовании паттернов, форматировании и структуре. В руководстве по стилю есть множество правил, добавленных исключительно для гарантии единообразия, например опре- деляющих количество пробелов в отступах или ограничивающих длину строки 3 Главной ценностью таких правил является их наличие, а не содержание. y Единообразие облегчает масштабирование. Инструменты — это ключ к масшта- бированию организации, а единообразный код облегчает создание инструментов, способных понимать, редактировать и генерировать код. Представьте инструмент, который обновляет исходные файлы, добавляя отсутствующие инструкции импорта или удаляя инструкции включения неиспользуемых заголовков. Если в разных проектах к спискам импорта применены разные политики сортировки, для некоторых проектов такой инструмент может оказаться бессильным. Когда все команды используют одни и те же правила, открывается возможность инве- стировать в создание инструментов, работающих повсюду, и в автоматизацию многих задач по обслуживанию. y Единообразие также помогает масштабированию человеческого ресурса в компа- нии. По мере роста организации увеличивается число инженеров. Поддержание единообразия в коде облегчает их переход между проектами, сокращает время на их включение в работу и дает организации возможность гибко приспосабли- ваться по мере изменения потребностей в численности персонала. В растущей организации также будут появляться люди, взаимодействующие с кодом не как разработчики, например инженеры по надежности, инженеры по библиотекам и «уборщики» кода. Благодаря единообразию эти люди могут работать сразу над несколькими ранее не знакомыми проектами. 1 Спасибо Х. Райту за фактическое сравнение, сделанное при посещении примерно 15 разных офисов Google. 2 «Формирование фрагментов» — это когнитивный процесс группировки элементов инфор- мации в значимые «фрагменты», а не их индивидуальный учет. Напоминает рассмотрение общей картины на шахматной доске вместо оценки позиций отдельных фигур. 3 См. разделы: «4.2. Block indentation: +2 spaces» ( https://oreil.ly/jaf6n ), «Spaces vs. Tabs» ( https:// oreil.ly/1AMEq ), «4.4. Column limit:100» ( https://oreil.ly/WhufW ) и «Line Length» ( https://oreil. ly/sLctK ). 154 Глава 8. Правила и руководства по стилю y Единообразие также обеспечивает устойчивость. Со временем инженеры покида- ют проекты, собственники меняются, а проекты объединяются или разделяются. Единообразие кодовой базы гарантирует низкую стоимость переходов и дает нам практически неограниченную текучесть как кода, так и инженеров, работающих с ним, упрощая долгосрочную поддержку. В МАСШТАБЕ Несколько лет назад наше руководство по стилю для C++ строго запрещало нарушения совместимости со старым кодом: «В какой-то момент могут появиться весьма веские ар- гументы для изменения правил стиля, но мы постараемся сохранить все как есть, чтобы обеспечить единообразие». Пока база кода была небольшой и в ней почти не было старых пыльных углов, в этом за- прете был смысл. Но по мере разрастания и старения кодовой базы сохранение совместимости перестало быть приоритетной задачей. Такое изменение было (по крайней мере, для арбитров руководства по стилю для C++) осознанным: мы явно заявили, что база кода на C++ никогда больше не будет полностью единообразной. Было бы слишком обременительно обновлять правила по мере развития современных практик и требовать применения этих правил ко всему, что когда-либо было написано. Наши инструменты и процессы крупномасштабного изменения позволяют нам обновлять почти весь код и приводить его в соответствие почти к каждому новому паттерну или син- таксису, поэтому большая часть старого кода соответствует самому последнему одобрен- ному стилю (глава 22). Однако такой подход не идеален: когда база кода становится такой большой, как наша, невозможно гарантировать, что каждый фрагмент старого кода будет соответствовать современным практикам. Требование идеального единообразия достигло той точки, когда оно стало обходиться слишком дорого. Установка стандарта. Мы склонны концентрироваться на внутреннем едино- образии. Иногда локальные соглашения возникают раньше глобальных, и порой нецелесообразно подгонять внутренние правила под внешние. Мы выступаем за иерархию единообразия: нормы, принятые в данном файле, предшествуют нормам данной команды, предшествующим нормам более крупного проекта, которые, в свою очередь, предшествуют нормам всей кодовой базы. Руководства по стилю содержат правила, которые явно относятся к локальным соглашениям 1 и считают локальное единообразие важнее научно-технического. Однако не всегда достаточно создать и соблюдать набор внутренних соглашений. Иногда необходимо принимать во внимание стандарты, принятые внешним со- обществом. Если внутренние соглашения уже существуют, желательно привести их в соответ- ствие с внешними правилами. Для небольших, самодостаточных и недолговечных проектов это, вероятно, не будет иметь смысла — внутреннее единообразие важнее 1 Примером может служить использование констант ( https://oreil.ly/p6RLR ). |