Pull to refresh

Comments 31

При работе над поддержкой незнакомой мне кодовой базы я трачу кучу времени на поиск строк при помощи grep.

А теперь вопрос: что будет. если разработка системы ведётся на одном языке программирования, а реализация — на другом языке программирования? В этом случае. очевидно, вся кодовая база будет под рукой.

разработка на одном а реализация на другом это как? Много разных языков как раз удачно женятся грепом, а ещё ack и вообще ide с нормальным поиском.

TypeScript в сорцах => JavaScript в билде

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

И на фронте в бандле тоже ничего существенно не меняется?

А как и зачем вы фронт грепаете? Натравливаете его на выхлоп curl?

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

const criteria1 = calculateCriteria1();
const criteria2 = calculateCriteria2();
if (criteria1 && criteria2) {
  return true;
}
return false;

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

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

А ещё я могу написать код вида (...)

Вы не поверите, но я именно в целях отладки почти дословно делаю то же самое на

слабонервным не смотреть

древнем фортране

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

Удачи вам дебагить многопоточность приложение в дебаггере. Только хардкор- только лог файлы.!

Которые тоже вносят точку синхронизации.

Никакого холивара. Он полностью прав. Холивар может быть о следствиях этой статьи. Ну там Джава читаемее и грепабельнее Раста и значит лучше в общем случае. Котлин в общем случае не нужен. И все такое.

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

Тогда вы с лёгкостью можете находить ключи, ссылки на которые предположительно будут выглядеть так: t('auth.login.title')

Зато итерирование по ключам разных уровней превращается в занимательные приседания.

Вы эти ключи все равно читаете в какую-то мапу при старте приложения. Ну и положите в такую структуру чтобы удобно итерироваться было. Да, можно хранить обе структуры при необходимости. Этот расход памяти настолько незначителен что можно не обращать внимания. Или можно перекладывать в нужную структуру если она используется редко. Это в общем случае настолько быстро что не повлияет ни на что.

Код он для чтения человеком, а не чтобы итерироваться удобно было.

Ну вот мне как человеку гораздо удобнее видеть иерархическую структуру в коде, а не сплошной плоский дамп всех мыслимых значений. Ну да, грепать сложнее, но, во-первых, настолько ли большая проблема грепать по фразе ['auth']['login']['title'] вместо auth.login.title? Во-вторых, если это по своей природе иерархическая структура, то наверняка в коде будут не обращения напрямую к auth.login.title, а предварительное формирование объекта auth и обращение уже к конкретным его дочерним полям. Такое фиг погрепаешь, несмотря на казалось бы грепабельную исходную систему хранения данных.

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

на поиск строк при помощи grep.

А с другой стороны, это говорит о том, что нет специализованного инструмента вместо простого поиска по тексту.

Но когда расследуешь инцидент в котором данные по всему пути проходят через 20-30 реп

Правда тут и текущий workflow IDE не поможет, потому что сначала надо подгружать проект, чтобы стал доступен поиск по типам...

С другой стороны существуют поисковики по кодовой базе как https://searchfox.org/, https://livegrep.com/search/linux, https://sourcegraph.com/docs/code-search/queries, но и они не токенизируют код, а просто дают удобный интерфейс с regexp.

И напомню про наличие грепалок в самом git.

const getTableName = (addressType: 'shipping' | 'billing') => {
    if (addressType === 'shipping') {
        return 'shipping_addresses'
    }
    if (addressType === 'billing') {
        return 'billing_addresses'
    }
    throw new TypeError('addressType must be billing or shipping')
}

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

Офигенная оптимизация (нет). КГ/АМ.

Тоже самое про второй пункт. Когда у вас вложенная структура, то вам надо имя родителя менять в одном месте. Когда плоская — в десятке, и легко проглядеть. Особенно, когда они идут не одним блоком, а кто-то там добавил в конце еще один ключ.

"auth.login.title": "Login",
"auth.login.emailLabel": "Email",
"auth.login.passwordLabel": "Password",
"auth.register.title": "Login",
"auth.register.emailLabel": "Email",
"auth.register.passwordLabel": "Password",
"auth.register.name": "Password",
"auth.login.name": "Login",

Вот такая структура при переименовании login рискует превратиться в

"auth.auth.title": "Login",
"auth.auth.emailLabel": "Email",
"auth.auth.passwordLabel": "Password",
"auth.register.title": "Login",
"auth.register.emailLabel": "Email",
"auth.register.passwordLabel": "Password",
"auth.register.name": "Password",
"auth.login.name": "Login",

Где у auth никакого name нет, потому что оно осталось у login. Иерархическая схема такого не позволит сделать принципиально. Классическое "удачной отладки, суки".

А еще можно не писать billing, оставить только b.

Код - он для чтения, «экономия» времени «на написание» никогда не является экономией.

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

Речь о том, что сделать ошибку в десяти перечислениях гораздо проще, чем в двух.

Поэтому что первый, что второй варианты плохие. Названия таблиц и полей (если уж хочется динамически собирать запросы) должны быть определены как константы в одном единственном месте. Тоже самое и для addressType. Ну и во втором варианте хотя бы ошибка выскакивает, а в первом черт знает что может получится.

Вот у меня примерно те же мысли.

Трудно спорить, что при прочих равных "грепабельность" лучше её отсутствия. Но жертвуя при этом надёжностью при модификациях – не, спасибо, не хочется.
Взять этот самый пример с shipping | billing – с двумя опциями это выглядит ок. Но мы же понимаем, что это стерильный пример, а в реальности их окажется 10 или 20 или хз сколько. Аналогично с ключами опций.

И да, доведённый до крайности DRY тоже штука сомнительная, но здесь, как мне думается, пока ещё не этот случай.

Проблема этого подхода не столько "грепабельность", сколько внедрение неявной зависимости между БД и кодом. Тип адреса и название таблицы имеют один паттерн по случайности. Завтра мы захотим на фронтенде сделать галочку "Same as shipping address" и у нас проблемка

"это все придумал черчилль в восемнадцатом году"

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

Применительно к себе могу сказать, что обычно на 3й итерации переписывания код получается достаточно Clean - т.е. другие могут с ходу читать и понимать, включая меня самого через несколько лет.

Да, это интересная метрика не знал о ней ранее

const getTableName = (addressType: 'shipping' | 'billing') => {
    return `${addressType}_addresses`
}

Не знаю, выдумал ли это автор, но я такой код прям видел и не раз. И каждый раз за авторством джунов после довольно неплохих вузов. Где-то в недрах высшего ИТ образования сидит поехавший вредитель с идеей "короче - значит круче". Мол если утрамбовал 10 строчек в один лихой однострочник, то сразу }{@ck3R

Так понимаю, тернарный оператор вам тоже не нравится?

Тогда добро пожаловать в клуб :)

Нет, обычный тернарник меня не огорчает. Вложенный - да, уже тяжелее. Самая дичь это что-то типа

fun0(fun1(fun2(fun3(fun4(agrs.reduce {x,y -> reduceFun(x,y)}))))

Кто-то считает, что это прям шик. Я считаю, что это невозможно дебажить.

Замечание по переводу. Лучше выглядит (да и как-то стандартей, если поискать похожие примеры) "грепабельность" без удвоения согласной. По-английски, ясен пень, удвоение нужно, чтобы сохранить закрытость слога.

Ха ха, прям флешбеки - в приложение используется проперти типа map.get("a.b.c.d")

Ну и я пытаюсь найти строку "a.b.c.d". А её нигде нет! Но благо я уже к тому времени был знаком с yaml конфигами так что искал "a" в yaml файлах. А потом уже искал в том файле b и так пока не нашёл нужное. Ппц как неудобно! Для примера property файлы - ищешь "a.b.c.d=" и тебе сразу в окне поиска показываются все дефинишны этой проперти.

Sign up to leave a comment.

Articles