Pull to refresh
4
0.1
Send message

Потому что обрабатывать пользовательский ввод приходится самим, коль скоро нужна уникальная логика. Да, здесь тоже есть место для проблем, и применяется то же правило: либо вы делаете всерьёз, либо полагаетесь на принцип Неуловимого Джо (возможно плюс минимальная защита от script kiddies, хотя с пришествием языковых моделей этот "минимум" тоже может оказаться чертовски высоко).

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

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

К сожалению нет. Есть фундаментальное различие между ошибками функциональности и ошибками безопасности: первые могут быть обнаружены при обычном использовании, и обычно достаточно легко предъявить тест который демонстрирует проблему вне разумных сомнений. Вторые никак не сказываются на нормальной работе ПО (за совсем клиническими исключениями), и даже демонстрирующий уязвимость proof-of-concept вполне можно не понять или убедить себя что проблема несущественна.

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

В вузе легко получить доступ к экспертам

Так это верно только до тех пор, пока в ВУЗе занимаются передачей знаний, а не налаживанием социальных контактов. А то вы получаете возможность "в безопасной среде тестировать интересные гипотезы", а что получает эксперт? Зарплату преподавателя и честь быть объектом тестирования?

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

Negative long-term effects on the brain, especially in younger users.

The brain will reach full development at 25.

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

Справедливо, но это по-моему как-то совсем в сторону от приоритетов при найме. И опять же, одно дело кодировать только happy path как получится потому что функционал нужен уже позавчера, другое - в процессе этого самого кодирования класть себе грабли без которых можно обойтись сравнимыми усилиями (тут примеры зависят от языка, скажем в C++ писать в подобном "одноразовом" коде new вместо std::make_unique).

"Никто не заметит если я сделаю плохо" - вообще довольно странное основание чтобы делать плохо, даже если отвлечься от того, в каком хрустальном шаре вы подсмотрели "этот код не будет вызван с этими параметрами никогда в будущем" (а если это действительно так - вы поставили assert(n<=20, "size restriction violation")?).

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

Опять же, "утечки" - часто следствие той же ментальности "и так сойдёт, ресурсов много". А потом я с интересом узнаю что у движка Blink есть лимит на количество сущностей, и приложение умудрилось его исчерпать.

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

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

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

Нравится мне этот квантор "всем". Абстрактно, в режиме "скажи пароль" - "самобалансирующееся дерево", может и да. А на практике не далее как на прошлой неделе я поймал коллегу на попытке написать код с квадратичным временем там где есть очевидное n*log(n). При том что во многом другом человек вполне компетентный.

очередного инфоцыганина типа Роберта Мартина

Что именно вложено в "инфоцыганина"? Насколько я помню, его советы не были вполне самоочевидными или вредными при разумном применении (понятно что любой совет способен навредить если применять его без мысли, но это, ИМХО, не претензия к совету).

вы можете прочитать весь четырёхтомник Кнута, все книжки Таненбаума, все статьи от индусов на Medium... Но какой от этого толк? Это никак не улучшает вас в вашей непосредственной профессиональной деятельности

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

Да, очевидной связи "я прочитал книгу Х, и затем сделал всё по буквальной инструкции со страницы 53, абзац 4" обычно нет. Будете смеяться, у инженеров Boston Dynamics её нет тоже.

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

Даже если мы вызовем эту функцию со всеми возможными комбинациями типов, у нас переменная a сравнивается с 0, поэтому должна иметь тип int.

Не должна. Проверка условия [3, "tree", []] == 0 в Python вполне корректна, условие не выполнено.

И, например, мы можем проверять типы и вызывать всё необходимое перед вызовом функции sum.

Можете, но как только вы перетащили работу с типами во время исполнения - код от компилятора C++ весело помахал вам ручкой и скрылся в голубой дали.

def surprise(a):

if a==0:

return 42

elif a==1:

return "48"

elif a==2:

return [4, 2]

def sum(a, b):

return a + b

Вызов должен сделать Int+Int, либо String+String, либо T1+T1, либо T2+T2, либо T3+T3, либо ..., либо T10+T10. Двенадцать разных вариантов (для типов T1-T10 результат вычисляется операциями над полями с разными смещениями, поэтому код заведомо не взаимозаменяем).

def foo(a, b, c):

if a == 0 :

return sum(b, c)

return sum(b, a), c

Совместное распределение типов имеет 155 разных вариантов. Отдельно обращает на себя внимание вариант (Int, Int, Int), потому что он возвращает то ли Int, то ли Tuple[Int, Int], и как прикажете компилировать код возврата из этой функции?

Ну так прикол в том, что я могу написать десяток-другой своих типов с перегруженным +. Что ещё хуже, к моменту когда компилятор добрался до sum , эти типы ещё могут быть не объявлены. Это примерно как требовать сразу инстанцировать шаблонные функции в C++ для всех наборов параметров, которые могут иметь смысл: комбинаторный взрыв плюс невозможность компилировать программу по частям плюс потенциальные бесконечности (a может быть функцией int->int, int->(int->int), int->(int->(int->int)) и так далее, множество допустимых типов бесконечно):

def foo(a) :

def bar(b) :

return a(b)

return bar

Тесты вообще сами по себе верифицируют примерно нифига. Они осмысленны только в сочетании с какими-то ожиданиями о реализации, иначе для проверки целочисленного сложения пришлось бы писать отдельно тест sum(2,3) == 5, отдельно тест sum(2,4) == 6 и далее ad nauseam.

Мгм.

def caller(a, b, c, d):

print sum(a, c)

print sum(d, b)

Если у нас... ну пусть пять типов, то предлагаете компилятору сразу генерировать 625 вариантов функции caller? А если типов 50 (в Python вообще-то свои классы можно создавать)?..

Ну так ровно потому, что эффективный двоичный код и нетипизированный язык - штуки несовместимые.

def sum(a, b):

return a+b

И во что прикажете это компилировать? В add eax, ebx, в call string_concat или во что-то третье?

При массовом засилии всех этих типов, разве Вы редко видите надписи "случилось неожиданное"?

Я несколько раз пробовал что-то писать на JavaScript, в разных контекстах и по разным поводам. Каждый, каждый раз по достижении пары тысяч строк у меня начинались ошибки "тут integer интерпретируется как массив" (или наоборот). После того как освоил TypeScript, жить стало проще, даже с добавленным геморроем "напиши заголовки для этих пяти библиотек". Поэтому я довольно сильно уверен что если бы у меня не было этой страховки, я бы такой хрени в кодовых базах на сотни тысяч строк видел бы намного больше, чем вижу сейчас.

а что если пришедшая строка прошла валидацию через jsonschema/иной вариант валидаторов входных данных?

Тогда price должен быть уже int. Иначе откуда я знаю, что она действительно прошла, а не свалилась с другого логического пути, на который программа попадает при вот такой уникальной комбинации условий? (И такие баги я ловил - когда автор кода почему-то посчитал, что строка прошла через операцию экранирования, а она в каком-то случае нет, и оп-паньки, получился невалидный JSON. Замечу что разделение типа "строка" на типы "экранированная строка" и "неэкранированная строка" позволило бы поймать баг до коммита в main.)

или другой вопрос: а что если этот код упадёт, и пользователь получит "случилось нечто неожиданное". Пожмёт плечами и сделает рефреш.

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

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

То есть математику можно так же программировать, как и торговлю. А можно программировать, например, игры в покер.

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

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

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

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

price.parse::<f64>()

Если price - строка, то в ней может быть "сюрприз" . Я обязан чётко видеть, что именно должна сделать моя функция со строкой такого содержания. (А что если в ней -500? А если произведение приводит к целочисленному переполнению?)
Здесь можно задать архитектурный вопрос, а не надо ли вынести санацию значений куда-нибудь в район их получения, но локально обязанность ясно понимать поведение функции при каждом физически возможном входе - это самый минимум для того, чтобы хотя бы начинать надеяться, что итоговая программа не является полным, абсолютным барахлом.

Программирование — это искусство. А искусство не терпит правил.

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

Статическая типизация не спасает от дурака.

Меня она от себя спасала много раз (следует ли это считать подтверждением что я не дурак?..). От банального "присвоить ID типа A переменной, которая во всём остальном коде интерпретируется как ID типа Б" до "заметить что я использую координаты в неверной проекции" или "это не переведённая текстовая строка, пользователю её показывать не надо".
И мне чаще приходится сталкиваться с логическими ошибками именно потому, что в функции передаются структуры с типизированными полями, а не массивы "третий компонент обозначает время по Дубаю, четвёртый - число снегоуборочных машин, пятый - бинарное представление PNG с номерным знаком автомобиля".

У Лукьяненко был рассказ, "Временная суета". Субъективно, я не нашёл его хорошим самим по себе (скажем, в сравнении с "Поездом в тёплый край") и не нашёл его ухватывающим дух "Понедельника".

Хороший вопрос. Я изначально думал что это из каких-то кавказских легенд. Поисковик напомнил что есть и русскоязычное ("Мастер и Маргарита"):

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

Но утверждается что на МиМ.

Information

Rating
3,449-th
Registered
Activity