company_banner

Недочёты, часто встречающиеся в программировании, которых стоит избегать

Автор оригинала: Daan
  • Перевод
Люди, по своей природе, склонны к совершению ошибок. Однако множества недочётов, характерных для разработчиков, можно избежать. Если программист способен избавиться от распространённых оплошностей, о которых речь пойдёт в этом материале, он сможет писать более качественный и чистый код.



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

Вот некоторые распространённые недочёты, которых стоит избегать программисту.

1. В функции выполняется слишком много действий


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

Причина важности того, чтобы писать функции, сосредоточенные на решении единственной задачи, заключается в том, что такой подход делает код более надёжным. Предположим, что в вышеописанном случае данные загружаются из некоего API. Если этот API изменится, например — выйдет его новая версия, то высока вероятность того, что работать перестанет вся функция. Код, загружающий данные, загрузит что-то не то, это повлияет на обработку и представление данных. Для решения проблемы придётся сначала найти её, выяснив, что дало сбой, а потом править код большой функции. Если же загрузка, обработка и представление данных разбиты на несколько функций, решать подобные проблемы будет гораздо легче.

2. В проекте имеется закомментированный код


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

Подобные фрагменты кода стоит просто удалять. И если в самой свежей версии кода этих удалённых фрагментов не будет, а они при этом кому-то реально понадобятся, их можно будет найти в системе контроля версий. Отмечу, что это лишь моё мнение по данному вопросу.

3. Неудачные имена переменных


Однажды я написал статью, в которой представлены простые правила подбора хороших имён переменных. Качественные имена переменных — это крайне важно. Дело в том, что программисты обычно работают над проектами не в одиночку. Их коллегам нужно понимать тот код, который они пишут. Понятные имена переменных способствуют улучшению восприятия чужого кода.

Повторю то, о чём я говорил в вышеупомянутом материале: «Выбор хороших имён переменных отнимает время, но это экономит больше времени, чем отнимает».

4. «Магические числа» и строки


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

Из Википедии можно узнать, что «магическими числами» называют плохую практику программирования, когда в исходном тексте встречается числовое значение и неочевидно, что оно означает.

Взглянем на следующий пример кода:

for ($i = 1; $i <= 52; $i++) {
    ...
}

Здесь значение 52 — это «магическое число». Никто не знает о том, почему это — именно число 52, и о том, что оно означает. Почему 52? Почему не 64? Может — это количество недель в году?
Этот код станет гораздо понятнее в том случае, если переписать его так:

$cardDeckSize = 52;
for ($i = 1; $i <= $cardDeckSize; $i++) {
    ...
}

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

То же самое имеет отношение и к работе со строками:

if (userPasswordIsValid($user, "6yP4cZ".$password)) {
    ...
}

Что это за 6yP4cZ? Выглядит как случайный набор символов.

Перепишем это:

$salt = "6yP4cZ";
if (userPasswordIsValid($user, $salt.$password)) {
    ...
}

А вот теперь всё оказывается гораздо понятнее.

5. Неаккуратное форматирование кода


Неопрятное форматирование кода — это «болезнь» начинающих программистов. Многие разработчики, имеющие некоторый опыт, обычно утвердительно кивают, отвечая на вопрос о том, знают ли они какого-нибудь тестировщика или дата-сайентиста, который плохо форматирует код. Причина этого заключается в недостатке опыта. Исключение составляют лишь те программисты, которые пользуются языком наподобие Python, в котором форматирование кода влияет на выполнение программы.

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

6. Значения, жёстко заданные в коде


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

Жёстко заданные значения не позволяют легко конфигурировать программу. Они, так сказать, «высечены в камне». Подобное считается анти-паттерном, или, как минимум, указывает на явные проблемы в коде.

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

Например, много жёстко заданных значений можно увидеть в некоторых примерах кода, ответственного за аутентификацию на внешних сервисах или API. Лучше так не делать.

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

Уважаемые читатели! С какими недочётами в коде вам приходилось сталкиваться?

RUVDS.com
1 602,86
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

Комментарии 70

    +5
    Python, в котором форматирование кода влияет на выполнение программы.

    Слишком громкая фраза.

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

    Короче питон — не исключение, там тоже можно напачкать.
      –17

      С одной стороны вы правы, но сталкивались вы когда-либо с НАСТОЯЩИМИ юниорами?
      Когда мы наняли одного такого в команду, то через неделю его уже не было, ибо мы делаем отступы чисто табами(так удобнее) и ситуация.
      Мы передали код в котором отступы указаны табами, повсюду…
      Возвращают нам код и… там пробелы.

        +4
        (так удобнее)

        А вот это вещь субъективная. Вот если там был жесткий стиль кода (что использовать, как форматировать и т.д. и т.п.), тогда совсем другое дело.


        P.S. Какой язык-то? Вот например, в уже упомянутом Python-е, в PEP 8 так и написано:


        Spaces are the preferred indentation method.
        Tabs should be used solely to remain consistent with code that is already indented with tabs.

        (Другие языки — Rust, Ruby, например — тоже не приветсвуют использование табов в коде. Хотя есть и противоположные примеры (Go)).


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

          –10

          Речь идет о смешении табов и пробелов.
          У нас в команде договоренность о своеобразном "поклонении табам").
          При приеме в команду мы об этом предупреждаем.

            +1
            Речь идет о смешении табов и пробелов.
            У нас в команде договоренность о своеобразном «поклонении табам»).
            При приеме в команду мы об этом предупреждаем.

            Как много слов, которые сокращаются в два слова, если бы вы владели программерской терминологией.
            Эти два слова — «Code conventions».
            И не нужно никого предупреждать. Новому члену команды просто даётся ссылка на статью «Code convention» в Confluence. Или даже не даётся — он сам её ищет и читает.
            +6
            1. Смешение табов и пробелов куда хуже чем отдельно табы или пробелы.

            2. Табы «в целом» скорее уходящая CodeStyle конвенция. Но например в Linux Kernel Code Style они родимые (не заменяются пробелами, да ещё и по 8 символов) — а оттуда зачастую попадают в CodeStyle контор занимающихся системным ПО.

            ПС
            Абсолютно с вами согласен, что увольнять! джуниара! вместо того чтобы объяснить! — это совсем какой-то моветон.
            Джун на то и джун, чтобы учиться на работе, а не с ходу выдавать желаемый код.
            +16
            т.е. вы уволили человека, потому что не смогли дать ему принятый в команде форматтер?
              +5
              Или забили на вводный инструктаж… И не предоставили .editorconfig для (оговоренной перед началом работ?) IDE

                –3

                .editorconfig — это формат, специально разработанный для того, чтобы не зависеть от IDE.


                Ну и что это за «оговоренная перед началом работ IDE», простите? А браузер, которым сотрудники почту читают, вы тоже «оговариваете перед началом работ»?

                  +1
                  > для того, чтобы не зависеть от IDE

                  Именно, так что вопрос «табы или пробелы» не возник бы:
                  indent_style = space
                  indent_size = 4

                  Общаться надо, не думать что «все всё и так знают». Не знают. Особенно человек со стороны внутреннюю кухню проекта.
                    +1

                    Я в курсе. Вот эта ваша фраза «.editorconfig для (оговоренной перед началом работ?) IDE» подразумевает, что IDE надо оговаривать. Не надо. Надо просто держать .editorconfig в корне проекта, и не заставлять людей пользоваться какой-то там специальной IDE.


                    .editorconfig в 2020 понимают все (кроме совсем экзотики).

                      +1
                      Про «оговоренной» было в скобках и под вопросом, если что.
                    +1

                    Ну вот старые IDE про .editorconfig могут просто не знать.


                    А ещё, для развивающихся языков, от IDE может зависеть максимальная поддерживаемая версия языка. Например, писать на C# 7.0 в Visual Studio 2015… можно, но в Блокноте это делать проще.


                    А ещё у IDE может быть какая-то особая интеграция со сборочными системами. К примеру, Visual Studio 2019 — это первая студия, которая идёт без своих плагинов к MSBuild для сборки веб-проектов.


                    Пальма первенства по кривоте тут, кстати, у Talend MDM Studio, где в зависимости от установленных плагинов будет отличаться поведение скомпилированного кода. Это единственная IDE из известных мне, где пришлось включать в гит пользовательские настройки.

                      +1

                      Ну, ОК, для такой экзотики может существовать черный список редакторов, но никак не оговоренная IDE.

                        0

                        Если проект начат на такой вот "экзотике" — то именно она и становится той самой оговоренной IDE. Просто потому что любая другая окажется не вполне совместимой с проектом.

                +1
                Когда мы наняли одного такого в команду, то через неделю его уже не было, ибо мы делаем отступы чисто табами(так удобнее) и ситуация.
                Мы передали код в котором отступы указаны табами, повсюду…
                Возвращают нам код и… там пробелы.


                У каждого могут быть свои личные предпочтения.
                Современные IDE прекрасно умеют отображать код в части табов/пробелов именно так, как надо именно тебе.

                Единственный затык мог быть в git/etc, где в коммит попадало бы гораздо больше строк, чем надо. Но это прекрасно решается хуками git, после чего табы/пробелы преобразуются автоматически.

              +6
              1. В функции выполняется слишком много действий

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

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

              2. В проекте имеется закомментированный код

              Чтобы искать в истории надо знать время и место. Закомментированный код показывает место, по нему же можно узнать время.
              Или можно делать как в офисном холодильнике — если за n месяцев старый код не понадобился — удаляем его.
                –3
                Любой код можно и нужно разбивать на блоки. Да, из-за «Принципа единственной ответственности» иногда приходится долго прыгать по коду продвигаясь вглубь на низкие уровни, если проблема порылась там. Но зато при этом гораздо более понятно, за что отвечает та или иная функция и тот кусок кода, который в ней находится. Ну, если код нормально написан. А если у вас длинная портянка, делающая кучу вещей разом, то чтобы её поменять и не испортить при этом что-то ещё вам придётся разбираться во всей этой портянке и тщательно продумывать все свои действия с ней. Конечно, можно и по SOLID-у умудриться говнокод написать со всякими «наведёнными эффектами», но это надо будет специально постараться.
                  0
                  Всегда стоит разбивать функции по принципу единственной отвественности. Потому что одна функция должна выполнять роль только порученой ей фунции. Это по крайне мере логично. С другой стороны, если нужно изменить функцию то меняется не что-то глобальное, а что-то конкретное и изолированное.
                  Тесты упадут не все, а только отосящиеся к выделенной функции.
                    +2

                    Можно написать отдельные функции на действия. А саму логику реализовывать в какой-то одной основной функции. Там будет проще на алгоритм смотреть, особенно когда названия функций/процедур корректные. А сами функции а-ля одно действие можно использовать и в других реализация. Но я думаю нового здесь ничего не написано, это у всех в практике используется, для этого и создаются библиотеки, классы, пакеты и т. д. В книге Макконнелла об этом тоже много написано.
                    А константы тоже можно отель выделить, создав функцию, где они все заданы и там будет проще всего настройку менять, чем по коду скакать.
                    Единственное непонятно как это на производительности будет сказываться, если всё на элементарные действия разбить.

                    0
                    Позвольте вас поправить:
                    userPasswordIsValid($user, $salt.$password)

                    «Соль» нужна для увеличения стойкости пароля к перебору по словарю / брутфорсом. Если склеивать «соль» в начало пароля, то вся суть «соли» теряется, т.к. можно предварительно вычислить значение хэш-функции для данной «соли».
                      0
                      Основная суть соли не теряется. Да, если злоумышленник узнает соль и она будет в начале, то на скорость брутфорса она не повлияет. Но если она не известна, то от перебора по словарю она по-прежнему спасает.
                        +2

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

                        0
                        По-моему, нет вообще никакой разницы, с какой стороны соль пристыковывается к паролю. Хоть в конец, хоть в начало, хоть в середину.
                        update А, я понял что имелось в виду.
                          +3
                          Ничего не теряется, неважно куда «соль» подставлять, в начало или в конец, она по-прежнему будет препятствовать перебору по словарю, по всяким хеш-таблицам, и т.п.
                            0
                            К взлому по радужным таблицам способ может и останется столь же стойким, но вот для перебора по словарю / брутфорсу становится менее стойким. Поскольку при компрометации соли и хэша пароля в случае склейки соли спереди, можно «предвычислить» значение хэша, и стойкость пароля становится такой же как и без соли.
                          • НЛО прилетело и опубликовало эту надпись здесь
                              0
                              Таки не вторая, а 1-я и единственная!
                              • НЛО прилетело и опубликовало эту надпись здесь
                            +1
                            7. Неактуальные комментарии
                            Это самое зло, потому что может просто взорвать мозг.
                              0
                              магические числа

                              однако есть само документированные константы


                              0, 1, 10, 100, 1000


                              60, 3600, 86400 — время
                              404, 400, 403 — ошибки http


                              и так далее
                              оформление многих из них как константы понижает читабельность

                                +1
                                HTTP_404_NOT_FOUND = 404,
                                HTTP_400_BAD_REQUEST = 400
                                и т.д. не только не понижает читабельность но еще и дает описание кода состояния
                                  –3

                                  и еще загромождает пол экрана

                                    0
                                      0

                                      очень плохой пример.


                                      проблема кода из примера в том, что константы перечислены в привязке к какому-то классу с публичными и приватными методами


                                      а необходимость упомянуть код ответа http в программе может возникать и без привязки к http респонзу


                                      то есть если уж константами тут всё обмазывать, то константы выносить в отдельный независимый ни от чего файл


                                      Upd: кроме всего прочего тут не хватает минимум двух кодов

                                        0
                                        Какая разница какие там еще есть методы в этом классе? Эти константы публичные и не требуют создания объекта. Если у вас в проекте не используется symfony/http-foundation, можно взять к примеру yiisoft/http, там нет ничего лишнего. Есть куча других похожих проектов, для разных ЯП. Константы в классах удобны тем что не засоряют глобальный неймспейс и загружаются автоматически. Т.е. не нужно инкудить файл самостоятельно.
                                  0

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

                                    0
                                    Да странно вообще, что в статье только кусочек принципов SOLID рассматривается. Брать принципы, так уж все :)
                                    +1

                                    В какой-то известной книге читал)

                                    • НЛО прилетело и опубликовало эту надпись здесь
                                    • НЛО прилетело и опубликовало эту надпись здесь
                                        +1
                                        Я Читала, что код портит переизбыток циклов.А один программист советовал использовать только for each.Не знаю, насколько это правильно.


                                        В такой формулировке — «всегда портит, использовать только» — это ложь.
                                        Имеет значение контекст, где именно.

                                        Где-то желательно следователь данным установкам, а где-то — прямо-таки наоборот.
                                          +2

                                          Можно даже обобщить.


                                          Формулировка «так всегда плохо, надо только эдак» — всегда неверна, и свидетельствует об узком кругозоре оратора :)

                                          0

                                          Foreach не даст вылезти за край списка и чётче выражает суть кода.


                                          For гибче и особенно полезен при работе с абстрактными числами а не списками. Но его сложнее понять.

                                            0
                                            `for..of`(JS), `for..in` (Rust) тоже не даст вылезти за пределы.
                                              0

                                              Это и есть for each же.

                                              0
                                              Foreach не даст вылезти за край списка [...]

                                              Массива, а не списка. В языках, в которых присутствует конструкция foreach обычно из коробки списков нет, только массивы.

                                                0

                                                В языках. в которых присутствует конструкция foreach, она обычно умеет работать с любыми коллекциями и последовательностями.

                                              0
                                              Если нет четких и ясных аргументов не слушайте их и делайте как вам нравится. Когда надо, программа сама даст понять когда и как делать лучше. Например тот же forEach (если мы о JS) не сможете использовать для: остановки перебора, использования ключевого слова `await`
                                                0

                                                Поэтому я люблю ФП-языки: там вообще нет циклов.

                                                  0
                                                  Это какие, совсем без циклов? А как остановить перебор без цикла?
                                                    0

                                                    Хаскель, например. Там все через рекурсию делается.

                                                      +2

                                                      Это некорректно, потому что вы сравниваете потроха хаскеля с интерфейсом другого языка.


                                                      Всякие high order until, iterate и пр. — сокрывают рекурсию, предоставляя сходный циклу интерфейс, а как там в PHP (или где) под капотом foreach сделан — может и рекурсивно — интимное дело создателей языка.

                                                        0

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

                                                          –1

                                                          Еще раз: foreach под капотом может быть реализован рекурсивно. Наверняка, для linked lists он так и сделан.


                                                          Под капотом — рекурсия, снаружи: выглядит, как цикл и квакает как цикл. Комбинаторы ведут себя точно так же.

                                                        –1

                                                        собственно поэтому на хацкеле полезные программы никто не пишет, а на Php работает половина интернета :)

                                                          0

                                                          Ага. А ещё там экзамен по теории категорий сдать надо перед тем, как до хелловорлда допустят.

                                                            +1

                                                            И то, не факт, что разрешат в I/O сразу; сначала, я слышал, джуны по полгода pure хелловорды пишут.

                                                  0
                                                  2, 4 и 6 — не проблемы для программы, узкозаточенной на решение одной вполне конкретной задачи.
                                                    0

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

                                                      0

                                                      Которые встречаются скажем в 10 местах и вдруг нам надо поменять значение, что будете делать.

                                                        +1
                                                        Которые встречаются скажем в 10 местах и вдруг нам надо поменять значение, что будете делать.


                                                        А если не встречаются в 10 местах?
                                                        А если никогда не придется «вдруг» поменять значение?

                                                        Переусложнение программы ради ситуаций, которые никогда не возникнут — это тоже нередкий недочёт в программах.
                                                          0
                                                          Вы можете гарантировать, что нигде больше не встретится?
                                                          Вы можете гарантировать, что значение никогда не придется менять?
                                                      0
                                                      $cardDeckSize = 52;

                                                      Всё-таки лучше магические числа/строки засовывать в константы через define или const (если внутри класса), а не в переменные. А если они таки могут меняться хотя бы теоретически, то выносить в конфигурацию.
                                                        0

                                                        Пункт 2 это болезнь кода, который поддерживался при помощи контроля версий именуемой: Folder Copy-Paste


                                                        Пункт 5 — это не только у неопытных. Сейчас встречаю код, который писали опытные программеры, но на форматирование забили большой болт. Кошмар, а не чтение :(. Лечится введением нотаций форматирования и шпынянием этих самых "опытных" волшебными пенделями.

                                                          0
                                                          Да, например, сейчас читаю код, наполненный константами 0.2, 0.5, 0.8, а также 20, 50, 80 и соответствующими переменными t20, t50, t80 и т.д. Интересно, кто-нибудь тут сможет догадаться, что они значат?
                                                            0

                                                            Гауссово распределение, как его понимал автор с очень дискретным воображением?

                                                              0

                                                              очевидно это вёрстка css?

                                                                +1
                                                                А точно не 0.25, 0.5 и 0.7?
                                                                  +1

                                                                  И литр?

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

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