Неправильно именуйте непеременные

    brainFuckProgrammImage Все началось лет 8 назад. Я тогда писал одну программу для математических расчетов, и мой преподаватель указал, что я неверно именую переменные. Он был прав: x, xx, xxx сложновато различить в коде. После переименования они превратились в redSegment, greenSegment, blueSegment (в контексте задачи именование было подходящее). Потом были «Рефакторинг» Фаулера, «Совершенный код» Макконнелла, «Паттерны проектирования» банды четырех… каждый день я погружался все глубже в бездну.

    В моей текущей компании никто не упоминает о правильном именовании переменных, это несерьезно. Мы обсуждаем с коллегами стили именования тестов, стоит ли использовать TestCase атрибут в nUnit, спорим о целесообразности #region в C#, пишем кастомные анализаторы для своих проектов и пьем смузи вообще всячески наслаждаемся жизнью.

    Осознание проблемы началось с тестового задания одного кандидата (весь код в публикации изменен, NDA).

    foreach (Dot d in dots)
    {
        WriteToOutput(d.X, d.Y);
    }

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

    Через пару часов я наткнулся на код в sql скрипте

    select u.* from Users u

    Еще через несколько минут в соседнем скрипте обнаружился кусок

    select u.UserName, b.Locked, d.PropertyValueStrings
    from Users u
    join Bindings b on b.UserId = u.UserId
    join Dossiers d on d.UserId = u.UserId
    where u.UserName = 'USERNAME'

    Последовал диалог с автором:

    — Почему ты используешь u,b,d вместо нормальных имен?
    — Так короче.

    Вы знаете, этот аргумент абсолютно верен. Действительно, так короче.

    А как насчет

    select bi.BusinessIdentifir, bia.SSAFA, IsNull(bia.Bullshit, ‘Bullshit’), bis1.*, bis2.*, bis.*
    from businessItems bi 
        inner join businessItemsArtifacts bia on ...
        inner join businessItemsSegment bis1 on ...
        inner join businessItemsSegment bis2 on ...
        inner join businessItemsSegment bis3 on ...
    where 
    bia.Date = @creationDate
    and bi.Staus = ‘RFW’
    AND
    (
    	(bis1.SignIn = ‘Europe’ and ss2.Limit = 42 and bis3.Connection not in (‘Towel’, ‘Galaxy’))
    OR
    	(bis1.SignIn = ‘USA’ and ss3.Limit = 146 and bis2.Connection not in (‘Trump’, ‘Klinton’))
    	OR
    (bis1.PNH = ‘SP’ and ss2.Limit = 21 and bis3.Connection not in (‘Stan’, ‘Kyle’, 'Cartman'))	
    )

    Запрос и так полон специфических констант и фильтров, неужели нужно усложнить его bis1, bis2, bis3?

    Но добил меня

    SELECT 
         MFID# as MemberId,
         TRIM(ACX) as FirstName,
         TRIM(ABX) as LastName,
         TRIM(FGS) as Suffix,								
         TRIM(c.DSC) as Country,
         TRIM(mm.CCC) as CountryCode,								
    FROM {0}.mailfl
        LEFT OUTER JOIN BDSMTAMDRT.MEMFLT mm ON MFID# = mm.MMID#
        LEFT OUTER JOIN BDSMTAMDRT.CTRCOD c ON mm.CCC = c.CCTRY
    WHERE mfid# = ?

    Автор дал вменяемые имена выбираемым полям, но не поименовал таблицы.

    Знаете, откуда берется эта привычка у шарпистов? Из учебной литературы.

    Открываем Шилдта:

    var posNums = nums.Where(n => n > 0).Select(r => r);

    Открываю msdn:

    IEnumerable<int> squares =
        Enumerable.Range(1, 10).Select(x => x * x);

    Открываю Троелсена:

    List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);

    Metanit, professorweb — везде вылезает

    numbers.Select(n => ...), teams.Select(t => ...)

    А потом в коде возникают артефакты вроде

    team.Select( p => p.Age > 18);

    Еще одна причина появления «коротышек»: изменения в коде. Был однострочный запрос с таблицей Products, городить именования не особо нужно, оставили p. Потом добавили join на ProductGroups, появилась pg, просто чтобы не изменять текущий стиль. Потом кусок кода скопипастили в другой запрос, там join на Profiles, в итоге имеем p, pg, pr.

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

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

    Встречаются ли вам плохо именованные объекты?

    Мешают ли вам плохо именованные объекты?

    Поделиться публикацией

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

      0
      Это еще ладно, самое противное когда называют члены класса и локальные переменные(а еще хуже аргументы) в методе этого-же класса одинаковыми идентификаторами.
        0
        Это характерно для конструкторов, лечится через style-guide. Переменные\параметры именуются как var, закрытые поля — _filed, открытые поля Field.
        Кстати, вы в чем разрабатываете? VS + R# вроде подсвечивают конфликты такого рода.
          +1
          До недавнего времени C++, сейчас C# скрипты для игр на Unity
            0
            Тогда только style-guide. Или Rider посмотреть, авторы вроде хотели его для Unity программистов предлагать.
              +1
              Ирония в том что, как раз таки те люди, которые используют райдер, допускали себе такие пошлости.
              Гоовря о таких ситуациях, хотелось бы сказать что такие осмысленные названия в некоторых ситуациях создают весьма неоднозначный код в котором легко запутаться из-за одинаковых идентификаторов, и порой лучше бы они назывались одной буквой внутри метода, чем аналогично члену класса.
                0
                дать имя переменной одна из двух самых сложных проблем программирования :)

                всегда по этому поводу пользуюсь переводчиками Гугла и Яндекса в поисках синонимов, что бы одна переменная от другой отличалась не приставкой / суффиксом, а целям корнем слова.
        +10
        Справедливости ради многие такие «сокращения» — условно принятые сообществом соглашения. Например:
        for(int i = 0; i < 10;i++){}
        

        Сокращение? Да.
        Осмысленное? Нет.
        Условно принятое, как норма и применяемое повсеместно? Да.
        Также и с лямбдами x.

        Лично для себя выработал правило — если переменная «локальная» для одной функции (или даже для части функции — можно и сократить Dot до d. Но если уж сократил так в одном месте — давай точно такое-же сокращённое имя в других местах кода. Если переменная относительно глобальная (например, публичное свойство объекта класса) тут уж будь добр — дай осмысленное имя.
          0
          Циклы — да, это уже норма. Хотя во вложенных циклах «коротышки» уже могут напрягать.

          По второй части: однострочники имеют обыкновение дополняться со временем. И переменная-«коротышка» может жить и в разросшемся скрипте.
            +1
            однострочники имеют обыкновение дополняться со временем. И переменная-«коротышка» может жить и в разросшемся скрипте

            А вот это уже проблема кодинга. «Спагетти-код», «Божественный объект» и другие антипаттерны.
            Если работа с «коротышкой» усложняется — изменить имя не сложно. Главное этот момент определить, вот только в состоянии «потока» это сделать сложно. Как-раз для этого и надо периодически выделять время на рефакторинг.
              0

              Это проблема того кто дополняет.
              Пока область видимости переменной — одна строка, ей ни к чему быть длиннее одного символа.
              Если программист расширил область видимости переменной, то подбор более подходящего имени — его и только его ответственность.

                0
                Это вы знаете. Это я знаю. Но не факт, что это знает программист расширяющий область видимости. Не факт, что у него над душой не стоит менеджер и не твердит: «дедлайн, премия, дедлайн, премия». Не факт, что этот расширяющий программист не подумает «сохраню исходный стиль». Не факт, что он вообще использует имена длиннее одной буквы в LINQ\SQL. Не факт…

                Переименовать переменную — дело минуты. Часть этих «нефактов» сразу же отпадет. Плюс вы скинете часть ответственности с того, кто будет изменять ваш код, взяв её на себя (тут я полагаю что пишущий новый код программист понимает его лучше того, кто будет этот код изменять, а значит и ответственность на нем).

                В комментариях уже указали ситуации, когда хватит «коротышек». Но если противопоказаний нет — лучше использовать нормальные имена.
                  0
                  Но не факт, что это знает программист расширяющий область видимости. Не факт, что у него над душой не стоит менеджер и не твердит: «дедлайн, премия, дедлайн, премия». Не факт, что этот расширяющий программист не подумает «сохраню исходный стиль». Не факт, что он вообще использует имена длиннее одной буквы в LINQ\SQL. Не факт…

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


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

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


                  Но если противопоказаний нет — лучше использовать нормальные имена.

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

              0
              Осмысленное — исторически из математики, как и программирование из математики.
              И всегда означало integer значение и/или index.
              image
                0
                Сдаётся мне, что и в математике это не более, чем соглашение. Как, кстати, и фигурирующая тут-же N.
                  +1
                  А также i-тератор, хотя с прогрессом текущих ЯП итераторы переросли в нечто большее.
                    0
                    Программисты далеко не всегда используют i для суммирования. На матане мы иногда интегралы через I обозначали. Так скорее i-index просто традиция с корнями из математики.

                    Я как-то пробовал код
                    for(int index = 0; index < 10; index++){}
                    

                    довольно непривычно было первое время.
                  +6

                  Всё зависит от области видимости переменной. Если это однострочник из 10 символов, то почему бы и нет? И это вполне читаемо:


                  IEnumerable<int> squares = Enumerable.Range(1, 10).Select(x => x * x);
                    –2
                    Однострочники иногда разрастаются. См. выше.
                      +9

                      Никто не запрещает переименовать "короткую" переменную при "разрастании" однострочника. Это косяк не того, кто однострочник составил, а того, кто его развернул.

                        –3
                        Довольно спорно.
                        Зачем оставлять потенциальную проблему? Фикс занимает секунды.
                        Честно говоря, я не понимаю почему локальные переменные нужно именовать нормально, а параметры в запросах-однострочниках — необязательно.
                          +5
                          почему локальные переменные нужно именовать нормально, а параметры в запросах-однострочниках — необязательно

                          ))) как известно в программировании есть 3 основных проблемы — наименование переменных и ошибка на единицу.

                          Иногда проще написать условную x, d или i, которая используется в 1-2 строчках, чем тратить время на придумывание и набор осмысленного имени.
                            +6
                            Честно говоря, я не понимаю почему локальные переменные нужно именовать нормально, а параметры в запросах-однострочниках — необязательно.

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

                              0
                              Я бы добавил еще один фактор: чем больше переменных задействовано в скоупе, тем больше резона именовать их правильно (чем больше коротышек i, j, k, l тем легче ошибиться).
                                0
                                чем больше переменных задействовано в скоупе
                                У однострочника (и его локальных переменных) скоуп — одна строка.
                                Чем больше переменных в скоупе — тем больше резона разбить его на несколько.
                              0

                              .

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

                                Вот когда разрастется однострочник — отрефакторите. А решать несуществующие проблемы — это преждевременная оптимизация, которая корень сами знаете чего. Или вы думали, что оптимизация бывает только по скорости?
                          0
                          А мне короткое имя в лямбдах в одно выражение вполне ок. В котлине вон есть it и тоже неплохо для простых выражений.
                            +5

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


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

                              +8
                              Потому что это удобно. Потому что в среднем запросе каждую таблицу надо упомянуть десяток-другой раз, и если каждый раз писать BusinessItemsArtifact — запрос превратится в нечитаемую кашу. Обычно в пределах запроса таблиц не очень много, и контекст User u, Person p или BusinessItemsArtifact bi1 не вызывает сложностей.
                                +1
                                Добавлю к каше масла :-). Достаточно попробовать написать schema bound view или функцию в MSSQL, где обязательно указывать имя схемы. И будет не User.Name, а MySchema.User.Name для каждого поля. А ещё табличка с пробелом в названии. Вроде dbo.[Order Details].ProductID. Короткие алиасы весь этот визуальный шум убирают.

                                И при изменении структуры БД удобнее рефакторить — меньше изменений делать нужно.
                              0
                              There are only two hard things in Computer Science: cache invalidation and naming things.

                              Phil Karlton

                              Ну, в приницпе, всё зависит и от типа переменной и от длины кода.


                              Если x'ом назвать экземпляр класса, то почему нет? Ничего страшного в этом я не вижу для читаемости. С учётом того, какие атрибуты икс будет иметь и что у него будет вызываться и так будет понятно что это за переменная.


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

                                +4
                                Да, в SQL сокращают имена таблиц до одной или нескольких букв. Это традиция, она устраивает большинство — добро пожаловать в одну из самых консервативных областей разработки. Устраивайтесь поудобнее, с опытом привыкнете. Если вы внезапно забыли настроить свой инструмент разработки, чтобы он вам удобно подсвечивал расшифровки сокращений — самое время это сделать.
                                  0
                                  Мне кажется, эта традиция имеет некие основание: структура таблиц меняется мало, а кода для работы с ними пишется много, и одно-двух-трехбуквенные сокращения становятся общеупотребительными на проекте.
                                    +1

                                    дело в том, что например в Oracle есть жесткие ограничения на длину идентификатора. Если использовать нормальные имена — на раз вылетаешь за ограничения.

                                    +7

                                    Где-то читал (может у Макконнелла), что короткие имена дают техническим переменным, которые нужны для организации работы программы, а не для моделирования предметной области. И на мой взгляд это правильно. Чем меньше у них семантики, тем меньше они обращают на себя внимание. Алиасы таблиц в SQL относятся сюда же, они используются для указания движку, какое поле имеется в виду.


                                    Сравните:


                                    for (int i = 0; i < users.Count; i++)
                                    {
                                        var user = users[i];
                                        ...
                                    }
                                    
                                    foreach (var user in users)
                                    {
                                        ...
                                    }

                                    Поменяли тип цикла, техническая переменная ушла в движок языка.

                                      0
                                      Поддерживаю. Зашел сказать, что алиасы-коротышки в SQL — это хорошо и правильно: объявлены рядом, область действия — только сам запрос. Иначе они вообще не нужны — кроме случая нескольких джойнов к одной таблице можно просто использовать само имя таблицы.
                                      +1
                                      применение в качестве альясов супер обрезанных имён в SQL запросах это тупо экономия места на экране.
                                      учитывая что альяс имеет смысл только внутри запроса, и не имеет смысла внутри программы, как бы не сильно сложно на время чтения запроса в голове уложить пару сокращений.

                                      Если не сокращать, то будет у меня имя альяса 20 символов и имя поля ещё 20 символов, итого 40 символов на ровно одно поле, это пол строки, для экрана широкоформатника — треть, на мой вкус жирно, для одного поля столько от строки отъедать.
                                      Запросы не всегда в две строки пишутся, для меня это как правило джоины от двух до пяти таблиц, иногда с формулами во фразе SELECT, и подзапросом или двумя во фразе WHERE, раздуть всё это полными именами таблиц? ещё и так что бы альяс был говорящим в рамках запроса?
                                      Нет я конечно очень не доволен ORACLE SQL где имя объекта ограничено в 30 символов и очень рад PostgreSQL где можно использовать аж 64 символа, но не настолько что бы в запросе повторять все 64 символа для каждого поля таблицы.

                                      Другое дело в коде программы, там другое дело, там счётчик это counter / index / element и другие более осмысленные именования, потому что тоже вложенность бывает на пару уровней и если использовать «i», «j», «k» очень легко сбиться.
                                        +1

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


                                        -- var posNums = nums.Where(n => n > 0).Select(r => r);
                                        posNums = filter (>0) nums
                                        
                                        -- IEnumerable<int> squares = Enumerable.Range(1, 10).Select(x => x * x);
                                        squares = fmap (^2) [1..10]
                                        
                                        -- List<int> evenNumbers = list.FindAll(i => (i % 2) == 0);
                                        evenNumbers = filter even list
                                        
                                        -- team.Select( p => p.Age > 18);
                                        _ = filter ((>18) . age) team
                                          0

                                          Это вас в общем случае не спасет. Да и в частном — не сильно лучше ваши варианты. Вы думаете, назначение even всегда будет таким уж очевидным? Ну т.е. идея-то правильная, а вот примеры не очень удачные.

                                          0
                                          Пишу на Delphi. Придерживаюсь простого правила. Все значимые переменные обязательно именую. Незначимые — временные, циклические и т.п. пишу короткими (как правило — одно-буквенными). Мне кажется оптимальный вариант.
                                            0
                                            Если у точки координаты X, Y, Z, то никто в здравом уме не будет их называть как координатаПоОси<имя оси>. В этом плане мне мешают плохо именованные объекты – много писанины, за которой смысла не видно.

                                            А вот сущности, описывающие объекты предметной области, не грех и обозвать подробнее, тем более, что название и придумывать не надо, оно уже есть в предметной области. Это действительно улучшает понимание кода.
                                              0
                                              Какие то некорректные примеры «вредных» книжных примеров приведены. Если в лямбдах переменные называть длинно, то их еще и на несколько строк придется разбивать, а ведь переменная даже за их пределы не выйдет. Другое дело — какие-нибудь большие вложенные циклы, сравнимые с вашими 15ти строчными sql запросыми, где привычные i и y тоже могут боком выйти. Это тоже самое, что и с паттернами и уметь определять где с таким именованием будет лучше, а где вредно.
                                                +1
                                                На C# привык в простых лямбдах ставить знак _ вместо буквы (особенно, когда без разницы — какую букву использовать). Потому, что так лучше видно, какие методы/свойства задействованы в лямбде. Из Лиспа что-ли это тянется…
                                                  0
                                                  Могу предположить, что в будущем IDE научатся сами сокращать/восстанавливать имена переменных в зависимости от настроек. Например, когда закончил работать над каким-то методом, все его внутренние переменные и структуры коллапсируют во что-то более компактное. Так, чтобы он занимал меньше места на экране и лучшим образом показывался алгоритм/структура метода. А когда нужно снова поработать над кодом — он обратно распаковывается в читаемые осмысленные переменные и структуры.
                                                    0
                                                    зачем? это была бы лишняя работа. все эти имена нужны только человеку. Для машины — это адреса памяти. Тратить ресурсы на преобразование текста кода туда-обратно бессмысленно.
                                                      0
                                                      Скоро мощность среднего десктопа приведёт к тому, что IDE перестанут тормозить. Это недопустимо.
                                                        0
                                                        зачем?

                                                        Ответ и так дан выше:
                                                        … чтобы он занимал меньше места на экране и лучшим образом показывался алгоритм/структура метода…


                                                        Тратить ресурсы на преобразование текста кода туда-обратно бессмысленно

                                                        Тратить ресурсы на раскрашивание текста кода бессмысленно — из той же оперы?
                                                          +1
                                                          … чтобы он занимал меньше места на экране и лучшим образом показывался алгоритм/структура метода…

                                                          И каким же образом автоматическая замена осмысленных имён на всякие x, y, z улучшит читаемость кода и понимание алгоритма?
                                                            0
                                                            И каким же образом автоматическая замена осмысленных имён на всякие x, y, z улучшит читаемость кода и понимание алгоритма?

                                                            Просите у авторов языка APL: P←{0~¨⍨a⌽⊃⌽∊¨0,¨¨a∘!¨a←⌽⍳⍵}

                                                              +1
                                                              давайте ещё к авторам Brainfuck`а за простотой и наглядностью синтаксиса сходим
                                                              –1
                                                              И каким же образом автоматическая замена осмысленных имён на всякие x, y, z улучшит читаемость кода и понимание алгоритма?

                                                              По аналогии с тем как работает «мини-карта» кода в современных текстовых редакторах:


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

                                                              Мне кажется нечто похожее на то что предложено выше — может позволить взглянуть на код более абстрактно.
                                                        0
                                                        Когда в выражении более одного сокращения — это уже плохо.

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

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