Три правила хорошего программирования

В последнее время я видел мало действительно хорошего кода, много посредственного и очень много — плохого. (Много того, что я писал раньше — особенно, когда я только начинал — относится к последним, увы.) Читая случайные статьи в интернете и профессиональные книги, я пришел к выводу, что писать хороший код — легко. Невероятно трудно, но в то же время легко. На самом деле, это настолько просто, что сводится к трем правилам.

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

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

Пишите код для людей, а не машины

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

Многие программисты пытаются писать то, что на их взгляд работает быстрее, уменьшает размер приложения — словом, “облегчает” работу компьютера. Это замечательно, но не забывайте, что гораздо важнее писать код, который будет легко читать и поддерживать другим людям.

Ваш компилятор (или интерпретатор) может обрабатывать абсолютно разные стили кода. Для него n и numberOfObjects — это одно и тоже. Для людей — нет. Они будут долго вчитываться в код, даже если предназначение переменной кажется вам очевидным.

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

Если вы делаете что-то неочевидное, чтобы оптимизировать часть кода, опишите в комментарии, что именно она делает. Но не забывайте, что в большинстве случаев вы не сможете оптимизировать программу лучше, чем компилятор. Реальность такова, что он умнее вас. Это факт: компиляторы улучшались в течение десятилетий тяжелой работы профессионалов. Бывают и исключения, но они лишь подтверждают правило.

Пишите код, понятный людям.

Не повторяйте себя

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

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

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

Этот же принцип может быть применен и к данным. Если вы часто используете одни и те же переменные, перенесите их в отдельный класс или тип данных.

Если соблюдать это правило, все изменения будут универсальными — вам не придется перерабатывать десятки мест, чтобы внести небольшую поправку.

Не повторяйте себя.

Каждый фрагмент кода должен выполнять одну задачу

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

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

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

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

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

Каждый фрагмент вашего кода должен выполнять одну задачу.

Заключение

Написание хорошего кода — это тяжелый труд. Я занимаюсь программированием в течение четырех лет — не так долго, но вполне достаточно, чтобы увидеть довольно много проблем, в том числе и своих. Мне стало понятно, что мы усложняем разработку, не соблюдая этих простых правил. Соблюдать их правильно — сложно: не всегда ведь очевидно, где нужен отдельный класс или метод. Это навык. На то, что бы стать хорошим программистом, требуется много времени. Но если мы не будем следовать этим правилам, написание и поддержка кода станут еще сложнее.



Перевод статьи Good Programming in 3 Simple Rules.
Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 20
  • +28
    Все читают теги!
    • +13
      Не сказать, чтобы автор был неправ, но… Это констатация очевидных фактов. И, увы, код от их очередного прочтения едва ли улучшится.
      • +5
        В третьем пункте главное не начать заводить по отдельному классу на каждый чих :)
        • 0
          Тогда уже по два класса. Chih extends AbstractChih
          • +1
            implements ChihInterface use ChihTrait :)
            • 0
              Давеча на работе встречал. Масштабы изменены в целях понятности и конспирации.
              AbstractChihFactory<X extends ChihProcess, AbstractChihResult<ChihProcess, Y extends ChihProcess, Z extends ChihProcess>>, 
              HumanChihFactory<X extends ChihProcess, AbstractChihResult<ChihProcess, Y extends ChihProcess, Z extends ChihProcess>>, 
              AnimalChihFactory<X extends ChihProcess, AbstractChihResult<ChihProcess, Y extends ChihProcess, Z extends ChihProcess>>, 
              AbstractBigChih<X extends ChihProcess, AbstractChihResult<ChihProcess, Y extends ChihProcess, Z extends ChihProcess>>, 
              AbstractSmallChih<X extends ChihProcess, AbstractChihResult<ChihProcess, Y extends ChihProcess, Z extends ChihProcess>>, 
              HumanBihChih<X extends ChihProcess, AbstractChihResult<ChihProcess, Y extends ChihProcess, Z extends ChihProcess>>, 
              AnimalBigChih<X extends ChihProcess, AbstractChihResult<ChihProcess, Y extends ChihProcess, Z extends ChihProcess>>, 
              HumanSmallChih<X extends ChihProcess, AbstractChihResult<ChihProcess, Y extends ChihProcess, Z extends ChihProcess>>, 
              AnimalSmallChih<X extends ChihProcess, AbstractChihResult<ChihProcess, Y extends ChihProcess, Z extends ChihProcess>>
              
        • +1
          Прямое следование второму совету может привести к функциям/методам, в которых большую часть кода выполняет анализ флаговых параметров для выбора того или иного нюанса поведения. Потому нужно ввести ещё правило — не бойтесь разъединять код, который однажды объединили по причине того что он был одинаковый в нескольких местах, а потом оказалось, что должен немного отличаться.
          • 0
            Прямое следование третьему правилу приводит к тому, что создается куча мелких функций/классов, размеров в 5 строк.
            • 0
              Некоторые считают это вполне нормальным, хотя как по мне, то перегиб в большинстве случаев.
          • +1
            <offtop>
            Знаю команду одного очень успешного it-проекта, которая неожиданным образом следует принципу DRY: копипастят код исключительно друг друга (и друг на друга ссылаются в случае проблем со свежескопированным кодом).
            </offtop>

            По сути, основной принцип один: нужно писать код, соответствующий общей идеологии проекта, и, по возможности, поддержка которого не вызывает желание убить автора. Бывают скрипты, которые нужно написать быстро, которые используются одним способом, и требования к которым не меняются годами. Бывают задачи типа «максимально быстро запилить эксперимент/прототип, чтобы не жалко было выбросить». В таких случаях рабочий императивный говнокод вида «поток сознания» вполне пригоден и полностью соответствует идеологии задачи, и, более того, нередко хорошо поддаётся рефакторингу.
            • +3
              Правда в хорошем коде есть одна засада. Чем сильнее вы прогрессируете как программист, тем выше становятся ваши требования к хорошему коду и тем сложнее эти требования вам удовлетворить.

              Очень забавно: вначале вы осознали проблемы своего кода, после этого повысили к нему требования, а потом потратили кучу времени, чтобы научиться эти требования удовлетворять.
              • 0
                Это нормально. Хороший код — это как кунг-фу. Вначале ты не умеешь его использовать, потом ты умеешь его использовать, а в конце ты умеешь его не использовать.
              • +3
                А теперь ждем, когда весы качнутся в другую сторону и появится статья с доводами писать рабочий код и быстрее запускать его в продакшн, а рефакторинг и лучшие практики оставить, когда появятся на это ресурсы.
                • +1
                  А потом появится статья с доводами писать оптимизированный код, который, скорее всего, будет плохо читаться.
                • +1
                  Первый принцип описан у Брукса в Мифическом человеко-месяце.

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

                    И да, очень плохо, что читая ассемблерный вывод компилятора мы не видим правила хорошего тона, именования переменных, и паттерны.
                    Вообще непонятно как компилятор работает без Agile и Scrum.
                    Что-то в этом есть неправильное.

                    Имхо принцип, при котором программы пишутся не для клиентов, не для работоспособности, а для читающих код — это как раз ключевой момент в переходе от объективных характеристик к субъективным оценкам. Т.е. базирующихся не на истине, а на консенсусе.

                    Алан Кей писал, что современное программирование — это не инжиниринг а поп-культура.
                    Мы же не инженеры, это скучно, мы же Художники, ага, с большой буквы.
                    Вот так вот художниками и останемся, и остальных тоже хотим сделать художниками.

                    Когда устранили великое Дао, появились «человеколюбие» и «справедливость». Когда появилось мудрствование, возникло и великое лицемерие. Когда шесть родственников в раздоре, тогда появляются «сыновняя почтительность» и «отцовская любовь». Когда в государстве царит беспорядок, тогда появляются «верные слуги».


                    Если красивый код важнее, чем работоспособный — значит что-то у нас не то.
                    За последние пять лет не видел тимлида не проводящего регулярно ревью стайлинга. И при этом ни один из них (!) не знал про существование uncrustify — специально спрашивал.
                    Сапожники без сапог, ага.

                    Увеличивая разницу в требованиях к человеку и к программе — мы ликвидируем всякую возможность заменить человека программой в ближайшем будущем.
                    Может это выгодно авторам таких статей?
                    Платят ведь неплохо…

                    • +2
                      Невероятно трудно
                      но в то же время легко
                      На самом деле, это настолько просто, что сводится к трем правилам
                      Проблема в том, что это очень трудно

                      Код Шредингера — настолько простой, что невероятно трудный.
                    • 0
                      Еще слышал одно правило для написания кода (к сожалению забыл кто автор, по-моему кто-то из классиков):
                      Пишите код так, как будто его будет сопровождать маньяк-психопат, который знает, где Вы живете.
                      • +4
                        Три правила хорошего борща.

                        В последнее время я видел мало действительно хороших супов, много посредственных и очень много — плохих. (Много того, что я готовил раньше — особенно, когда я только начинал — относится к последним, увы.) Читая случайные статьи в интернете и профессиональные книги, я пришел к выводу, что варить хороший суп — легко. Невероятно трудно, но в то же время легко. На самом деле, это настолько просто, что сводится к трем правилам.

                        1. Варите суп не для кастрюли, а для себя.
                        2. Не кладите одни и те же ингридиенты два раза.
                        3. Каждый ингридиент должен выполнять одну задачу.

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

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

                        Самое читаемое