Pull to refresh

Comments 73

В целом написаны разумные вещи, но ненависть автора к "затенению" переменных противоречит принципу инкапсуляции. Видимо, сказывается привычка к C/C++.

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

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

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

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

В том и дело, что чтобы осознать, что эта переменная именно локальная, а не прилетела из внешнего контекста, нужно увидеть это в коде и запомнить. Предположим, у нас есть класс с некоторым полем user. Если в коде отдельного метода вводится локальная переменная user, то у программиста, который знает структуру класса и о существовании в нём поля user, но не помнит устройство метода, может возникнуть недоразумение при анаализе работы метода и класса в целом. Он видит null pointer exception при обращении к user.sendEmail, но уверен, что поле user определено, и это может вызвать у него когнитивный диссонанс. Разумеется, это простейший случай.

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

Вы сейчас написали другими словами, что ООП в стиле С++ не очень хорошо соответствует принципу инкапсуляции (собственно, и под инкапсуляцией в "народном" ООП часто подразумевается совсем не первоначальный смысл этого термина). Я с этим не буду спорить, но лично я про ООП не написал ни полслова. Да, наверное, не надо локальными переменными методов затенять члены класса (в тех языках, в которых это вообще лексически возможно), но это очень частный вопрос, а не та общая формулировка, которая приведена в статье.

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

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

Судя по вашим словам, язык с полной поддержкой инкапсуляции всегда должен требовать явно указывать имена, захватываемые из родительского контекста, дабы программист мог явно видеть, что это именно переменная из родителя (кстати такое как раз есть в лямбдах из C++). И то даже это может быть сомнительно. Или мы с вами в принципе о разных вещах говорим? Может быть приведёте свой пример какого-то «правильного» языка?

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

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

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

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

Обозначенная Вами проблема возникла по существу из-за того, что обращение к полю текущего объекта user синтаксически неотличимо от обращения к локальной переменной user (что, на мой взгляд, является ошибкой проектирования языка), а ООП в современном понимании провоцирует постоянно перемешивать такие обращения.

А если, например, вложенные друг в друга функции имеют формальные параметры с одинаковыми именами, то я не вижу в этом никакого криминала.

Так а какая для программиста разница: это user из родительской функции или user из класса? Ему всё равно придётся понять, что тут есть перекрытие, если функции длинные.

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

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

Я не вижу тут существенной проблемы.

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

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

Речь в тексте не про контексты, а про области видимости (scopes). Автор, скорее всего, намекал на this в джаваскрипте, но более внятные примеры будут из языков, в которых everything is expression; вот, например, эликсир:

x = 42
if true, do: x = 0 # shadowing
IO.puts(x) # 42

Ну лексическая область видимости является частью лексического контекста.

Согласен с Вашим примером по форме, но не по сути. Он в данном случае криво написан просто потому, что это бессмысленная последовательность операторов, а не из-за проблемы затенения переменных, как таковой. А так – есть устоявшиеся синтаксические паттерны вроде for i или lambda (x), которые нет никакого смысла гармонизировать с внешним контекстом.

Мне кажется, возникло недопонимание. Хорошо, вот примеры подлиннее.

# ruby naïve factorial
fac = 1
(1..5).each { |i| fac *= i }
fac
#⇒ 120
// javascript naïve factorial
let fac = 1;
for (let n = 5; n > 1; n--) { fac *= n }
console.log(fac)
//⇒ 120
# elixir BOOM
fac = 1
Enum.each(1..5, fn i -> fac = fac * i end)
fac
#⇒ 1 �

То же самое случится и со скоупами внутри if, case и т. д.

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

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

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

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

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

Всё хорошо в меру.

Да вы издеваетесь, что ли, все?

Я где-то что-то говорил про то, что шэдоуинг — это плохо? Я просто продемонстрировал, что такое контекст (scope) и чем он отличается от функции. Да, тут будет ворнинг. Переписать этот код так, чтобы ворнинга не было — вообще не проблема. Но дело не в этом. Я лично вообще везде использую шэдоуинг, просто надо сначала разобраться, что это, а потом принимать решения. В комментарии, на который я отвечал — фигурировали только функции. Я показал, что скоуп — у́же, чем функция. Всё.

Если обращаться к первоосновам, то в Scheme локальная лексическая область видимости – это всегда форма let (в нескольких синтаксических вариантах, несущественных в данном случае), специально для того и предназначенная, чтобы вводить имена переменных и их лексическую область видимости. И производные формы, раскрывающиеся вlet. В каких-то языках из практических соображений решили совместить с функциями или, там, циклами. Но ещё в PL/I были do- и begin-блоки специально для разделения цикломатической логики и лексических контекстов.

Опять же, я согласен с вадимр, шэдовинг это вообще любое присваивание (мутация) или повторная инициализации уже существующего в лексическом контексте имени. А если подробней то шэдовинг это 3 шага:

  1. использование существующего объекта по имени в правой части выражения (опционально)

  2. окончание времени жизни объекта у существующего имени (если нет алиасинга/шаред использования)

  3. инициализация нового объекта и связывание его с именем

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

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

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

Автор подразумевал любое переиспользование идентификатора в дочерних контекстах, которое затрудняет понимание кода. Не только неожиданный shadowing (который приводит к некорректному поведению и будет выловлен при тестировании кода), но и формально корректное перекрытие.

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

У меня есть личный эмпирический метод оценки читаемости кода. А именно: расстояние с которого можно более-менее понять, что делает код.

Анекдот в тему

Докладчик на конференции показывает с экрана код на скале, спрашивает: «Достаточно ясно, или увеличить шрифт?»

В аудитории неразборчивый ропот. Докладчик увеличивает шрифт.
— Так понятнее?

Голос из зала:
— Нет, это по-прежнему код на скале.

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

Не поймите меня неправильно, я считаю, что switch выглядит лучше, чем цепочки if-else, но он тоже не безгрешен

Ваш код не эквивалентен. В варианте с switch, math.sign вычисляется один раз. В варианте с if/elsif несколько. А ведь на месте math.sign моглп быть функция с побочными эффектами или даже без них, но дорогая в вычислении. Поэтому по-честному нужно сохранять вычисление math.sign в локальную переменную, а потом уже с ней работать. А это уже загрязнение скоупа дополнительными переменными.

А что не так с sonar, code smells и старой книжкой Мартина "Чистый Код"? Похоже на велосипед.

Следование "Чистому коду" производит говнокод, вообще забавно что мартин написал две книги о "правильном коде" которые противоречат друг другу.

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

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

Разумеется, типичный инфоцыган.

Q: Что делает код трудным для чтения?

A: Модная тёмная тема.

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

Этот пример из статьи

return graph.nodes(`node[name = ${name}]`)
    .connected()
    .nodes()
    .not('.hidden')
    .data('name');

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

Если где-то есть вложенность, стараюсь её убрать по максимуму. Например вот это для меня трудночитаемая лабуда:

let object = {
  fieldA: "A",
  fieldB: {
    fieldInB: "BB"
  }
}

А вот так мне гораздо лучше и понятнее:

let object = { fieldA: "A", fieldB: { fieldInB: "BB" }}

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

Возможно потому, что инструкции программного кода (в любом ЯП) в принципе описываются «сверху вниз», и любая программа читается человеком аналогично, по тому же принципу цепочку вызовов методов можно разделить на несколько строк (каждый последующий вызов - новая строка), и визуально это будет восприниматься (ИМХО) удобнее, нежели длинная цепочка в одну строку. Лично мне сканить код глазами сверху вниз гораздо комфортнее, чем слева направо. Но согласен, если общая длина инструкции (неважно какой) невелика, то разделение на несколько строк выглядит ужасно)

В общем, как и всегда: it depends

P.S. Касательно приведенных Вами примеров кода: в 1 примере (цепочка вызовов) «столбик», на мой взгляд, лучше/удобнее, во 2-м (создание мэпы) - в одну строку, пожалуй, получше будет

инструкции программного кода (в любом ЯП) в принципе описываются «сверху вниз», и любая программа читается человеком аналогично, по тому же принципу цепочку вызовов методов можно разделить на несколько строк (каждый последующий вызов - новая строка), и визуально это будет восприниматься (ИМХО) удобнее, нежели длинная цепочка в одну строку

Я просто эту цепочку воспринимаю как одну инструкцию. Да, она составная, но результат у неё один, и поэтому я воспринимаю её как одно действие, которое должно занимать одну строку =)

результат у неё один

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

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

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

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

https://habr.com/ru/companies/reksoft/articles/892242/#comment_28060452

Когнитивная сложность кода, на мой взгляд достаточно важная тема, которой, к сожалению, очень многие (на моем опыте, по крайней мере) напрочь пренебрегают. А все начинается с малого. Вот зачем ты пишешь, например, if/else, если у тебя внутри каждой бранчи return, и else можно просто не писать?)

Лично я для себя выработал железное правило: если есть возможность НЕ создавать лишний скоуп - не создавай его. Если есть возможно обработать исключение иным способом, отличным от try/except - используй именно этот способ.

Дополнительные/излишние скоупы - это зло, которое ведет не только к увеличению когнитивной сложности кода, но и к увеличению вероятности ошибок (причем зачастую довольно глупых, типа UnboundLocalError).

>Вот зачем ты пишешь, например, if/else, если у тебя внутри каждой бранчи return, и else можно просто не писать?)

Я прямо про самую первую "картинку" в статье - мне кажется вы противоречите "духу статьи" (?) :) => да, в статье оговорились, что это "вопрос предпочтений" и можно считать "паттерном", но это заставляет "мозг" вставлять "пропущенное слово" и выглядит как "сбивка в однородном синтаксисе": для одного варианта есть "слово", а для другого - нет, а ведь они вроде равноправны...или нет? :)

Нет, это не заставляет мозг вставлять пропущенное слово, если программист знает как работает control-flow :)

>...если программист знает как работает control-flow :)

Вот я именно об этом :(

В статье ("стыдливо" :) умолчали о возможной разнице в "классе" между "писателем кода" и "читателем кода" :)

Там ниже кстати уже упомянули об этом:

https://habr.com/ru/articles/893820/comments/#comment_28085634

"Паттерны" сильно зависят от опыта :(

П.С.

А насчет конкретно "пропущенного 'else'" :) - мне кажется это одна из причин почему "switch" выглядит лучше: стилистически однороден...хотя именно в этом случае и "switch" можно "с пропуском" написать :)

Оценка Cognitive Complexity не учитывает (и не должна учитывать) уровень "писателя" / "читателя" кода. Понятно, что "новичок" будет писать код с обилием if/elif/else, try/except и т.д., и в его сугубо личном восприятии так будет понятнее / проще / более явно и т.д. Опытный же специалист (не все, конечно, но многие) скажет, что этот код "сложный" в контексте восприятия и будет прав с точки зрения оценки Cognitive Complexity, т.к. исходя из критериев данной оценки: чем более "линейно" написан код, тем он умозрительно "проще" воспринимается

По поводу "if vs switch": в Python'е с 3.10 версии завезли паттерн-матчинг, причем "продвинутый", c captured patterns и т.д., и все резко бросились его юзать :) Но лично я, поюзав его какое-то время, понял, что для данного языка это около-ненужная "фича", так сами по себе captured patterns не особо то и применимы (конкретно в Python'e), а сам по себе синтаксис "match / case" обязывает создавать +1 уровень "вложенности"

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

Можно пример правильно реализованного паттер-матчинга?

Пожалуйста. Эликсир, например. Во всех строках, кроме 6 и 9 — использован разный паттерн-матчинг (function clauses, strings, maps, placeholders).

def to_bool(nil), do: false
def to_bool(false), do: false
def to_bool(:none), do: false
def to_bool(atom) when is_atom(atom), do: true
def to_bool(<<_::utf8()>> = s) do
  case s do
    "n" <> _ -> false
    _ -> true
  end
end
def to_bool(%{result: res}) when res in [true, :ok], do: true
def to_bool(_), do: false

Это скомпилируется в одну функцию, которую можно будет вызвать так: to_bool(external_data) и получить назад результат.

Я правильно понял, что в 4 кейсе when эквивалентен if в других языках?

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

А как дела с "неправильно" реализованным паттерн-матчингом? Возьмём тот же C# - он там есть, но на деле это просто синтаксический сахар, который сам по себе загоняется в if / switch / is. Такое имеет право на жизнь?

Вы меня перепутали с прокурором, мне кажется. Я не раздаю мандаты пра́ва на жизнь.

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

Это зависит от набора известных паттернов в голове у читателя.

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

Это частично так. Поэтому часто по прежнему в качестве первого языка опытные преподаватели рекомендуют не питон или какой-нибудь раст, а бэйсик или паскаль. Меньше конструкций --> меньше возможностей запутаться или выпендриваться.

Но есть и второй момент: чаще всего сложность чтения связана не столько с внутренностями одной конкретной функции/метода, а с декомпозицией. Длинные методы --> плохая декомпозиция --> трудно читать и отлаживать. Короткие, но их много, они раскиданы по классам непонятно как, но сильно переплетены, а их назначения не очевидны --> опять плохая декомпозиция. А сделать хорошую - это искусство и время. Плюс надо учесть особенности языка (например паттерны управления памятью или обработки ошибок...) Так что сложность чтения зависит от нескольких параметров: уровень умения автора декомпозировать, язык, время на выполнение задачи, тип задачи и т.д. Причем первое вообще вряд ли поддается каким-либо метрикам, кроме субъективных. Т е если на код ревью большинство ревьюеров признали код читаемым, то он читаемый, а если нет, то ну ШтоШ, не судьба))

Например, эти фрагменты кода на самом деле не эквивалентны!

А может это C# а не JavaScript ?

Спасибо автору-переводчику за перевод статьи и за возможность дискуссии здесь!
В оригинальной ссылке нет форума, а дискуссия здесь - сама по себе уже добавленная ценность :)

П.С.
Да, прошел по ссылке на оригинальную статью и тупо нажал "Translate" в браузере :)
Извините - не удержался от наблюдения про "стилистические особенности перевода" человеком и машиной :(
На примере одного предложения из статьи - оригинал:

For long function chains or callbacks that stack up, breaking up the chain into smaller groups and using a well-named variable or helper function can go a long way in reducing the cognitive load for readers.

"Человеческий" перевод здесь:

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

"Машинный" перевод оригинала в браузере:

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

Ну, что сказать...именно для этой статьи - есть плюсы и минусы "машинного перевода" и "человеческого перевода" :)

Термины и русский слэнг терминов "человек-переводчик" знает лучше "машины": "callbacks" остался "колбэки", а не дословным (хотя и правильным) "обратных вызовов" :)

Но стилистически "машинный перевод" часто здесь более корректен и (для меня :) ближе к стилистически краткой, точной и выверенной манере "оригинального автора".
Я именно про "стилистику", а не про "содержание":

- "For" корректно (и кратко!) переведено "машиной" как "Для", а не "Если в коде есть";
- "the chain" оба перевели коряво -> "машина" потеряла "определенный артикль" в просто "цепочки", а "человек" заставил вспоминать, что такое "их";
- "smaller groups" корректно переведено "машиной" как "сравнение" в "более мелкие группы", а не просто "небольшие группы";
- "well-named variable" понятно переведено "машиной" как "хорошо названной переменной" вместо расплывчатого "понятных имен переменных";
- "can go a long way in reducing" в "машинном" переводе по крайней мере не пропущен "модальный глагол" в "может значительно снизить", а не однозначно как "значительно снижает нагрузку";
- ну и стилистические "трюки", когда перечисление "and" становится "с" вместо просто "и", а множественное число "readers" превращается в единственного "читателя" мешает иногда восприятию.

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

Для себя определил что вариант getOddness2 наиболее понятный и стараюсь везде придерживаться этого правила

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

интересного вопроса Вы коснулись, вероятно зависит от сложности системы, типа если для большого проекта это всегда команда, читать и понимать чужой код становится приоритетом, был опыт и такой, что две независимых команды работу типа дублируют для систем real time, и многократно code review делают друг друга

В риалтайме, кстати, и отладчиком особо не побалуешься.

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

когда-то интересовался как именно первые большие системы (1950х) отлаживали с мин. инструментами, sage например, участники пишут большинство ошибок нашли by eyeballing листингов на ассемблере, типа впечатляет

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

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

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

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

Я буквально учился, читая чужой код, который было восхитительно приятно читать.

Как будто создатели stream в java подтолкнули писать слодный код. Иногда в цепочку заворачивают по два десятка действий.

Sign up to leave a comment.

Articles