Изначально я человек первой культуры и очень долгое время считал вторую несерьёзной. Пару-тройку лет назад я окончательно понял, что ошибался. Многие «старички» ошибаются в ту же сторону, а в последние годы ещё большее число людей ошибаются в обратную. Знакомство с соседней культурой и понимание, почему дела в ней делаются так, как там принято, превратит вас в лучшего разработчика.
Размер кодовой базы
Культура 1: важны большие проекты
Культура 2: важны короткие фрагменты кода, насыщенные содержанием
Возможно, именно из этого различия следуют все остальные. Молодые разработчики не всегда понимают, насколько монструозны проекты первой культуры. Например, современная компьютерная игра AAA-класса, если писать её на C-подобном языке, — это несколько миллионов строк кода, уже больше, чем вы когда-либо сможете даже просто прочитать. Linux — штука более серьёзная, в нём больше 15 миллионов строк; Windows и macOS ещё в несколько раз больше. В интернете пишут, что автопроизводители каким-то образом переплюнули даже эти цифры и в «Мерседесе», помимо собственно автомобиля, едет 100 миллионов строк кода. Я не знаю, стоит ли такому верить, и думаю, даже если стоит, эти строчки кода довольно бессмысленные. В любом случае и 100 миллионов — ничто по сравнению с кодовой базой компании FAANG-класса, в которой могут содержаться буквально миллиарды строк.
Даже довольно «тупой» код типа бизнес-логики оказывается вовсе не тупым, если его приходится писать и читать в подобных количествах. Наверное, больше всего это похоже на написание романа: каждое следующее слово просто берёшь и пишешь, вроде бы. Но на больших масштабах слова или складываются в непротиворечивый работающий сюжет с интересными персонажами, или нет. И если просто графоманить, не придумав себе некоторые высокоуровневые принципы и инварианты, то точно не сложится. И тот факт, что вы уже можете написать короткий рассказ, ещё не означает, что вы научились писать книги.
Чтобы ориентироваться в большой кодовой базе, нужны средства навигации и рефакторинга. И то и другое гораздо проще в одних языках, нежели в других.
- Возьмём, например, простейшую фичу go to definition: в месте вызова функции/метода посмотреть, где её код. В языках первой культуры сделать это тривиально: определение функции имеет чёткий синтаксис; их конечное число; омонимы, даже если есть, в конкретном месте различимы при помощи формальной процедуры проверки типов и областей видимости. VS Code и прочие IDE, конечно, притворяются, что и в Python или в JS тоже можно сделать go to definition, и иногда им это даже удаётся. Но поскольку функции — first class objects, «имя функции» и «тело функции», вообще говоря, относятся друг к другу как «многие к многим».
- Или возьмём разделение доступов к членам класса — private/public. Эта фича на самом деле не имеет никакого отношения к безопасности в смысле security, как могло бы показаться со стороны, а важна она именно для выживания в больших проектах. Она позволяет очертить границы, «внутри» которых можно на обозримом пространстве определить API общения с данным куском кода, поддерживающее все нужные инварианты, а «снаружи» дёргать только это API, не имея физической возможности случайно, по ошибке, их нарушить. Если такого разделения нет, то в проекте на миллионы строк любой код может случайно нарушить работу любого другого и выживание, конечно, будет серьёзно затруднено.
Опять-таки для человека из первой культуры вторая начинает казаться игрушечной. Это совершенно неверно.
В проектах огромных размеров совершенно не важны разовые накладные расходы, поэтому создатели языков первой культуры их и не экономили. В частности, совершенно неважно, сколько занимает программа, выводящая «Hello, World!». Плюсовик начнёт её с заклинания
#include <iostream>
, дальше напишет ещё несколько строк и не видит в этом ничего особенного; джавист должен для начала объектно-ориентированно определить специальный класс. Для питониста всё это дико, почему нельзя просто написать print("Hello, World!")
, как у нормальных людей?«Hello, World!» программист на каждом новом для себя языке пишет ровно однажды, но это же относится ко всем случаям, где мы хотели бы, чтобы в несколько строк происходило нечто достаточно содержательное. REPL и первая культура совместимы очень плохо; скажем, Jupyter-ноутбуки, несмотря на букву J, на Java были бы невозможны (теоретически, наверное, можно изобрести способы, но у людей первой культуры их даже просто придумать не получилось бы).
То же упомянутое выше разделение доступов очень плохо сочетается с концепцией, в которой объекты — просто коробочки с разнородными данными, способные возникать откуда угодно. Например, добавляться уже в момент исполнения, через eval. Это делает систему удивительно управляемой и конфигурируемой в рантайме: вы можете прямо в процессе дебага исправить код, мгновенно заменить его на лету и продолжить исполнение. Или вы можете иметь конфиг системы на том же языке, что и вся система.
Для меня в своё время было шоком узнать, что реализация трансформерных нейросетей (великих и ужасных) занимает порядка 100 питоновских строк. Естественно, это очень высокоуровневый код, и понятно, что такие строчки невозможны без torch, NumPy, CUDA и так далее; тем не менее аналогичная компактность кода в первой культуре непредставима.
Ясно, что при подобной выразительности скорость разработки в подходящих ситуациях драматически растёт.
Скорость
Культура 1: важна скорость работы программы
Культура 2: важна скорость работы программиста
Для программиста на любом, наверное, языке программирования, кроме C/C++, крайне странно слышать, что поведение написанной на них программы может оказаться «неопределённым» (undefined), «неуточнённым» (unspecified), «определяемым реализацией» (implementation-defined) — и это три разные вещи. «Неопределённое» поведение (худшее из трёх) означает, что программист ошибся, но не означает, что программа выдаст ошибку; стандарт официально разрешает ей в этот момент сделать буквально что угодно. Зачем такое вообще могло понадобиться?!
Многие из вас уже знают ответ: это позволяет компилятору делать всякие интересные оптимизации на низком уровне. Например, увидев, что раскрытие макроса или шаблона привело к выражению (x+1 > x), компилятор может смело заменить его на true. Но что, если x == INT_MAX? Поскольку переполнение int'а — undefined behavior, компилятор имеет право проигнорировать этот экзотический случай. На простом примере мы заметили нечто жуткое: при исполнении программы, вообще говоря, не существует момента, в который «произошло» неопределённое поведение; его нельзя «детектировать», так как оно осталось в параллельной вселенной (но испортило нашу).
Если вам не приходится программировать на С/C++, то, скорее всего, вы в ужасе: люди пишут программы вот так?! Верно, пишут уже 50 лет и всё это время регулярно стреляют себе в ногу. Удобство для компилятора важнее удобства для программиста.
Примеров абсолютно противоположного подхода очень много в Python. Начнём с банального:
>>> x = 2**32
>>> x*x
18446744073709551616
>>> x*x*x
79228162514264337593543950336
Никакого переполнения и никаких проблем с ним! Голова программиста немного освободилась: целое число — это просто целое число, думать об INT_MAX больше не нужно. Естественно, у этого есть своя цена: bigint-арифметика намного медленней встроенной.
Вот чуть менее известный пример:
>>> 15 % 10
5
>>> -15 % 10
5
Целочисленное деление в Python подразумевает, что остаток от деления на N всегда будет числом от 0 до N-1, в том числе и для отрицательных чисел. В C/C++ это не так: остаток от деления -15 на 10 равен -5. И опять один подход экономит время и когнитивную нагрузку программиста: как узнать время суток, если у вас есть timestamp? Должны ли вы в этот момент думать о том, может ли текущий timestamp оказаться старше 1970 года? Это не случайность, сам Гвидо ван Россум подтверждает, что выбрал такую семантику именно из подобных соображений. Ну а второй подход лучше ложится на конкретное hardware и потому на сколько-то пикосекунд быстрее.
Чтобы оценить, насколько подобные вопросы волновали создателя Python и насколько тщательно он всё продумывал, последний пример: как думаете, что вы увидите, запустив такое?
>>> round(1.5)
>>> round(2.5)
>>> round(3.5)
>>> round(4.5)
>>> round(1.5)
2
>>> round(2.5)
2
>>> round(3.5)
4
>>> round(4.5)
4
Проверьте. Это не баг, так и было задумано. Попробуйте догадаться (или найти в интернете), почему с точки зрения второй культуры правильно округлять именно так, хотя это и добавляет необходимость в дополнительных проверках внутри реализации функции round.
Наконец, интересно, что при глубоком понимании обеих культур вполне возможен удачный компромисс между скоростью работы программы и программиста. Тяжёлыми вычислениями занимается библиотека, написанная на С++ (а то и на Фортране), снабжённая биндингами для Python или Lua, а уже на последних из этих библиотечных функций, как из кубиков Лего, собираются сложные конструкции. Приведу пример: наверное, самые вычислительно нагруженные IT-проекты современности — обучение и инференс больших нейросетей — сейчас в основном развиваются в рамках второй культуры. «Числодробилка» находится где-то глубоко под капотом; знать о её особенностях всё ещё полезно, но уже не обязательно — даже для получения результатов мирового класса.
Тестирование
Культура 1: можно математически строго доказать, что код не содержит ошибок определённого типа
Культура 2: есть статистические свидетельства, что код практически всегда делает то, чего мы от него ожидаем
После раздела, противопоставлявшего скорость работы программы и программиста, может показаться, что в первой культуре вообще не принято заботиться о безопасности и удобстве. Это обобщение, конечно, неверно (как и вообще любое сверхобобщение). Программисты как раз очень любят, чтобы компилятор проверял за них всё-всё-всё; на идее «а что, если компилятор сможет проверять всё, что только можно» основан целый язык Rust, сейчас стремительно становящийся мейнстримом.
Вообще, в первой культуре от ошибок защищаются статически. К этой группе относятся проверки типов на этапе компиляции, проверки формальных спецификаций и инструменты анализа кода. Кстати, такие проверки можно производить не только над кодом, но и над данными на этапе их «компиляции» в представление, с которым потом работает рантайм-код.
Во второй культуре от ошибок защищаются статистически, с помощью юнит- и регрессионного тестирования, а также ассертов, функциональных тестов, ручного тестирования и некоторых более экзотических практик типа fuzzing.
Как уже можно заметить, строгой границы между культурами нет; некоторые плюсовики старой школы с удовольствием пишут юнит-тесты, а некоторые питонисты — анализаторы кода. И всё же каждая культура тяготеет к своему набору практик. Это замечание верно и для всех остальных отличий между ними.
Свойство, которое отличает эти две группы практик, — доказательность. Скажем, компилятор С++ или Java гарантирует нам, что объект «не того типа» не может быть передан в качестве параметра функции, а у объекта не может быть вызван несуществующий метод. В Python проверить то же самое можно с помощью юнит-тестов, регрессионных и функциональных тестов, а также прославленным в веках методом «несколько раз запустить и посмотреть, не упадёт ли». Хотя оба подхода на практике могут обеспечивать надёжность, которая всех устраивает, отличие между ними довольно строгое: никакие тесты не покроют множество возможных ситуаций целиком. Всегда остаётся крохотная возможность, что пользователь программы сделает нечто экзотическое и скрытый баг вылезет наружу.
Конечно, тут вам не математика, и даже «доказательства» при помощи компилятора не могут быть абсолютно надёжными. Например, в самом компиляторе теоретически может содержаться редкая ошибка. На практике, однако, такой возможностью можно смело пренебрегать. Причин масса:
- В среднем по сравнению с обычными программами компиляторы гораздо надёжнее, и их поведение гораздо строже регламентировано.
- Даже если компилятор «умолчал об ошибке», она вскроется при небольшом изменении в коде.
- Мы говорим не об одном редком событии, а о стечении сразу двух маловероятных обстоятельств (компилятор не заметил ошибку программиста, которая при этом крайне редко проявляется)
- И так далее.
Большой пример
Много лет в разных обсуждениях на эту тему я приводил такой вот пример. Давным-давно, когда я работал в геймдеве над игрой для консоли Nintendo DS в жанре RTS, тестировщики нашли рассинхронизацию в мультиплеере.
Это крайне неприятный тип ошибки. Многопользовательская игра у нас была организована по p2p-схеме: разные DS-ки передавали друг другу только команды пользователей и каждая просчитывает состояние всего мира. Это идеальная для RTS схема, позволяющая организовать богатую фичами игру на основе очень узкого канала передачи данных и минимального «языка общения». Основной её недостаток в том, что вся игровая логика должна быть абсолютно детерминированной, то есть, получая один и тот же набор команд, на выходе всегда иметь одинаковое состояние игрового мира. Нельзя допустить никакого рандома, никакой зависимости от системного таймера, никаких шалостей с битами округления, неинициализированных переменных и тому подобного.
Как только рассинхронизация произошла, она уже фатальна: расхождения, даже начинающиеся с одного бита, быстро накапливаются и скоро оба игрока видят совершенно разные картины происходящего (естественно, оба выигрывают с огромным перевесом). При помощи вычисления контрольных сумм факт рассинхронизации можно установить с точностью до такта просчёта игровой логики, а вот дальше уже нужно целое расследование. Если вы можете себе позволить сериализовать все игровые данные с какими-то аннотациями, а затем сравнить два дампа с разных устройств, расследование несколько упрощается. К сожалению, у нас и этой возможности не было: мы должны были работать на Nintendo DS, консоли с крохотным объёмом памяти.
Вот какое описание бага я получил от тестировщика: «Иногда при невыясненных обстоятельствах происходит рассинхронизация. Что именно к ней приводит, непонятно. Reproduce: создать игру на четырёх человек и играть до посинения, активно пользуясь героями и их возможностями. Если игра закончилась и всё хорошо — повторить. Рано или поздно, сейчас или через день, ошибка случится».
Неприятно. В попытках повторить баг я провёл пару часов, но лишь убедился, что да, оно случается. Что делать?
Хорошо, а почему вообще игра может рассинхронизироваться? Насчёт неинициализированных переменных, к счастью, можно было не волноваться: мы написали собственное управление памятью, геймплейные данные всегда лежали в отдельном месте, изначально заполненном нулями, так что их состояния в отсутствие рассинхронизации совпадали бы до бита, даже если бы неинициализированные переменные встречались. Значит, кто-то совершил святотатство — позвал из игровой логики функцию, которая не обязана работать синхронно, например обратился к интерфейсу. Строго говоря, сделать это не так-то просто: нужно как минимум написать внутри игровой логики#include <../../interface/blah-blah.h>
и ни о чём в этот момент не задуматься. Простой поиск регулярного выражения в коде показал, что такую глупость никто не совершал.
Тут я осознал, что передо мной задача проверки типизации. Не очень, правда, распространённая: меня интересовали не типы в смысле языка, а «логические типы» функций. Какой-нибудь Haskell не видит особенных различий между этими понятиями, и правильно делает, но у нас-то C++.
Итак, все наши функции, методы классов и данные должны делиться на синхронные и асинхронные. При этом синхронные функции могут спокойно звать асинхронные (ради side-эффектов), но не могут пользоваться возвращаемыми ими значениями. Например, геймплейная функция может сказать: «Интерфейс, покажи пользователю сообщение», — но не может спросить: «Интерфейс, какой у пользователя viewport?» И наоборот, асинхронные функции не могут вызывать синхронные с side-эффектами, но могут свободно пользоваться возвращаемыми ими значениями («Геймплей, какой процент здоровья у такого-то юнита?»).
Что здесь важно:
- В коде содержались нарушения этих правил, иначе бы ошибка рассинхронизации не возникала.
- Вовсе не любое такое нарушение будет приводить к рассинхронизации, но если их не будет, то отсутствие рассинхронизаций будет математически строго доказуемо.
Отлично, оставалось поработать заместителем компилятора и явным образом переименовать все фунции doSmth на границе между игровым миром, интерфейсом и графикой либо в doSmthSync, либо в doSmthAsync, параллельно отслеживая, кто кого вызывает. Не прошло и часа, как все нарушения «типизации» были ликвидированы, а я смог восстановить цепочку событий, приводящих к рассинхронизации.
Ошибка оказалась в том месте, где интерпретировалась команда сбрасывания наковальни (как известно, это такой мультяшный аналог cупероружия). Чтобы проверить, сброшена она куда-то в пустую точку игрового поля или же наведена на конкретного юнита, по ошибке использовалась конструкцияisVisible(getCurrentPlayer())
, а неisVisible(игрок, который сбрасывает наковальню)
.
Вот как воспроизводился баг: одному игроку нужно было построить Скаута, сделать его невидимым и пойти на вражескую базу. Второму нужно было построить Снайпера и использовать способность «сбросить наковальню» на точку, в которой стоит (или через которую проходит) невидимый Скаут — вернее, на верхнюю половину его туловища. Эта команда на одной DS-ке означала «сбросить наковальню в точку за торсом Скаута», а на другой — «сбросить наковальню на Скаута» (в точку у него под ногами). Мало того, важно было не подходить череcчур близко, чтобы не «заметить» Скаута и не вывести его из состояния невидимости.
Я не представляю себе юнит-, да и какой угодно ещё тест, способный отловить подобное фантастическое стечение обстоятельств. Чтобы в игре не было подобных ошибок, необходимы живые самоотверженные тестировщики и крайне желательна математическая строгость кода.
Коротко об остальном
Культура 1: проверка типов на этапе компиляции
Культура 2: duck typing
Почему так, уже должно быть примерно понятно по предыдущим разделам. Элементы и того, и другого, применённые к соседней культуре, точечно могут оказаться крайне полезными, если понимать, что «так можно было».
Культура 1: «Я прочитал код всех библиотек, от которых зависит мой проект, и уверен в них»
Культура 2: npm install left-pad
Новый код, который делает что-то новое, — и ценность, и нагрузка. Возьмём гигантский проект, где каждый десяток тысяч строк реализовывает собственную чётко определённую спецификациями функцию (иначе воцарится хаос и всё умрёт). В нём каждая новая строчка кода, особенно написанная не вами, — это боль и потенциальная мина замедленного действия. С другой стороны, иногда вам нужно просто загрузить картинку в png и определить, нарисован ли на ней хот-дог. Вы либо можете сделать это в четыре строчки вот прямо сейчас, не приходя в сознание, либо нет, и люди второй культуры гораздо чаще могут.
Культура 1: статическая сборка
Культура 2: динамическая сборка, включая подтягивание библиотек из неведомых глубин интернета
Есть два вопроса:
- Могут ли незнакомые вам люди внезапно сломать ваш продукт без всяких действий с вашей стороны?
- Могут ли незнакомые вам люди значимо улучшить ваш продукт без всяких действий с вашей стороны (например, закрыть дырку в безопасности, о которой вы и не подозревали)?
Как несложно догадаться, ответы на эти два вопроса максимально взаимосвязаны.
Культура 1: документация ведётся локально
Культура 2: документация находится в интернете (на сайте, Гитхабе, Read the Docs, Stack Overflow)
Казалось бы, мелочь. Какая разница, где хранится что-то, что по сути является текстом (ну хорошо, гипертекстом)? Но и тут различие довольно характерное. В одном случае документация лежит у меня на компьютере, она «моя», точно относится именно к той конкретной версии, которую я использую, не изменится без моего ведома и так далее. В другом случае я живу и развиваюсь вместе со всем миром, имея шанс узнать новое об этой технологии, ровно когда это стало известно хоть кому-то.
Культура 1: формальные языки, алгоритмы поиска, анализ конечных автоматов, хитрые структуры данных
Культура 2: deep learning
В каждой культуре есть свои герои и великие маги. Они делают что-то настолько крутое и наукоёмкое, что вы хотите быть похожими на них. В первой культуре это создатели компиляторов и стандартных библиотек. Как знает каждый, кто читал книжку с драконом, даже простой компилятор — штука на редкость сложная. Написать на С++ очередной контейнер для данных очень просто, но сделать такой, которым будут пользоваться, — уже искусство. Технологическое достижение похоже на доказательство математической теоремы: «Таким образом, мы установили, что существует структура данных с амортизированным временем поиска О(1) и добавления O(ln ln N)».
Во второй культуре герои — люди, которые заставляют всех удивиться тому, что теперь компьютеры умеют ещё и ТАКОЕ. Довольно часто инсайдерам это понятно заранее, что ничуть не умаляет достижение. Например, понятно, что кто-нибудь первым обучит diffusion для генерации видео без хаков, end-to-end. И, скорее всего, это произойдет до наступления Q2 2023, но результат всё равно будет поражать, и тот, кто это сделает, всё равно будет героем.
Средний возраст «продвинутой технологии» в первой культуре — 25 лет, обычно её сделал кто-нибудь из множества {Керниган; Томпсон; Вирт; Хоар; Дейкстра; Торвальдс}.
Средний возраст «продвинутой технологии» во второй культуре — год, а часть её создателей вполне могут оказаться, скажем, бакалаврами MIT. Вторая культура позволяет даже «складывающим кубики студентам» (и, естественно, не только им) сложить их в нечто новое и полезное, пока бородатые гуру разбираются с проездами по памяти. И это не случайность, а следствие набора фундаментальных и мудрых принципов. Например, такого: «Производительность программиста почти всегда важнее производительности программы». Или такого: «Вашу технологию не нужно долго изучать или производить с ней дополнительные ритуалы, чтобы решать базовые задачи». Подобные принципы эту культуру и составляют. И на них тоже можно учиться.
Комментарии (92)
shpaker
11.01.2023 11:36+29Какая-то графомания на давно известные холиварные темы. Только этот пост тот случай когда срачик даже разводить не хочется. Не знаю даже зачем я это прочитал. Да и на мой взгляд, в современном мире, культуры явно не две - бывают еще фронтовики со своими оттенками бегающих в головах тараканов, несколько сортов датасатанистов, программисты только кастующие одни джейсонки в другие и тд, и тп.
PavelMSTU
11.01.2023 15:24+2программисты только кастующие одни джейсонки в другие
я их называю JSON-жонглёрами... :)
speshuric
11.01.2023 11:53+19Большинство программистов находятся в состоянии сложной невырожденной суперпозиции культур 1 и 2. Причём "ширина" диапазона этой суперпозиции, если бы её можно было измерить, нехило так коррелирует с "крутостью" программиста(если бы её тоже можно было измерить - хаха). А у тимлидов, менеджеров, экспертов и архитекторов есть еще разные дополнительные измерения в этой же суперпозиции.
MinimumLaw
11.01.2023 12:14+4Я полностью согласен с тезисами в данной статье. Но при всем при этом, не могу не заметить некоторых ограничений. В моем мире (мир встраиваемых систем) второй путь - это прототипирование. Очень нужная, важная и полезная штука. Но один из самых страшных грехов руководителя (за который потом приходится расплачиваться и инженерам по технике, и организации по имиджевым потерям) - это запуск прототипа в производство.
К сожалению да, есть и обратная сторона. Величайшее искусство художника состоит ровно в понимании того момента когда картина написана. Ибо предела совершенству нет.
screwer
11.01.2023 12:31+9Linux — штука более серьёзная, в нём больше 15 миллионов строк
Это только ядро. Далеко не самая объемная часть. Сравните его собранный размер (обычно менее 30мб) с размером дистрибутива. Прикиньте, из скольки миллионов (миллиардов) строк он собран.
Из этих строк (навскидку) 2/3 это поддержка железа: различные драйвера и архитектуры, в т.ч.настолько специфичные, что вы их не увидите никогда.
в «Мерседесе», помимо собственно автомобиля, едет 100 миллионов строк кода. Я не знаю, стоит ли такому верить, и думаю, даже если стоит, эти строчки кода довольно бессмысленные.
Размер прошивки GSM модема больше линуксового ядра где-то на порядок (уже ~50млн строк) и они совсем не бессмысленные.
Похоже вы сами совершаете свою же ошибку
Молодые разработчики не всегда понимают, насколько монструозны проекты «первой» культуры
и просто не осознаёте сложность современных систем.
Браузеры, драйвера современных видеокарт по сложности сопоставимы со сложностью целой ОС, включая юзермод.
up40k
11.01.2023 16:45+5Размер прошивки GSM модема больше линуксового ядра где-то на порядок (уже ~50млн строк) и они совсем не бессмысленные.
Можете объяснить, в чём сакральный смысл аж три раза оставлять слепок окружения оболочки разработчика в прошивке? Чтобы что?
Чтобы пользователь тянул из сети десятки и сотни мегабайт при каждом обновлении?
┌──(up4k㉿UP4K-X1C)-[/mnt/c/Windows/Firmware/FwSwitchbin] └─$ du -h m2_7360_nand.flz 140M m2_7360_nand.flz ┌──(up4k㉿UP4K-X1C)-[/mnt/c/Windows/Firmware/FwSwitchbin] └─$ du -h FirmwareData/18500.5001.00.05.27.30_5021.11_Secureboot.fls 29M FirmwareData/18500.5001.00.05.27.30_5021.11_Secureboot.fls ┌──(up4k㉿UP4K-X1C)-[/mnt/c/Windows/Firmware/FwSwitchbin] └─$ strings FirmwareData/18500.5001.00.05.27.30_5021.11_Secureboot.fls | grep SSH_CONNECTION SSH_CONNECTION=10.20.45.59 57271 192.168.20.222 22 SSH_CONNECTION=10.20.45.59 57271 192.168.20.222 22 SSH_CONNECTION=10.20.45.59 57271 192.168.20.222 22
screwer
11.01.2023 18:16-1Во-первых, с какого перепугу вы требуете от меня что-то обьяснять по хз каким файлам ?
Во-вторых, налицо непонимание, что такое "строки". Нет, это не выхлоп утилиты "strings". Речь о строках исходного кода.
В-третьих, я понятия не имею даже на каком языке написаны бинарники, по которым вы хотите получить от меня информацию. Я не их автор, и никаким образом с ними не связан. Давайте предположим что это С или С++.
The compiler is allowed, but not required, to combine storage for equal or overlapping string literals. That means that identical string literals may or may not compare equal when compared by pointer.
Т.е. компилятор может зареюзать одинаковые литералы. Но не обязан. А также строки могут быть неконстантными, и представлять собой массив символов в BSS. В таком случае компилятор вообще бессилен сделать реюз (для простоты понимания - представьте что указатель на эту строку отдаётся куда-то наружу, и внешний код может поменять данные по его адресу. Изначально строка-массив просто чем-то заполнена дефолтным, и это значение может (и должно) меняться в процессе работы.
Также неясно что такое "fls" вообще. Это может быть контейнер, из которого достаются отдельные файлы. И три экземпляра - это три различные прошивки, из которых выбирается к загрузке нужная. Это может быть единственная прошивка, а три экземпляра - это три разных функциональных блока, которые могут менять свой набор данных независимо. По крайней мере с т.з. компилятора.
Ну и напоследок. С чего вы, чёрт возьми, взяли, что файл должен обновляться весь целиком ? Уже десятки лет сущетствуют инкрементальные обновления. Которые затронут только изменившиеся части при апдейте. Может здесь применяться подобная технология ? На мой взгляд - запросто.
Наезд на 29 мегабайт - ойвей, 2023 год на дворе, сейчас полное обновление для мобильного телефона(!!!) занимает по 2-4гб.
Вообщем моё мнение - и комментарий не по адресу, и явное отсуствие попытки разобраться в деталях самостоятельно.
up40k
11.01.2023 21:41+4Во-первых, с какого перепугу вы требуете от меня что-то обьяснять по хз каким файлам ?
Бросьте, мой вопрос не в требовательной форме.
Во-вторых, налицо непонимание, что такое "строки". Нет, это не выхлоп утилиты "strings". Речь о строках исходного кода.
Которые в конечном итоге компилируются в бинарь, выполняющийся у пользователя на устройстве, который доставляется по каналам обновления. И да, те 50кк строк исходников, про которые вы говорите, прекрасно могут в течение жизненного цикла отдельного релиза (корявый термин, я подразумеваю последовательность действий от коммита разработчика в прод до доставки пользователю исполняемого кода) ужаться на порядки.
В-третьих
С этим согласен. Думал, из имен файлов будет понятно, о чем речь. Ниже поясню.
Также неясно что такое "fls" вообще. Это может быть контейнер, из которого достаются отдельные файлы. И три экземпляра - это три различные прошивки, из которых выбирается к загрузке нужная. Это может быть единственная прошивка, а три экземпляра - это три разных функциональных блока, которые могут менять свой набор данных независимо. По крайней мере с т.з. компилятора.
FLS - это образ файловой системы для NAND на устройствах от Intel, я не разбирался подробно, но, как я понимаю, он содержит саму ФС, чексуммы и подписи разработчика для безопасной загрузки на конкретном устройстве. В данном случае - модем Fibocom L850-GL на платформе XMM7360.
Конкретно этот файл на 29Мб - это базовый образ для конкретной версии HW и оператора (который целиком прошивается на устройство). Помимо него в пакете распространяются другие файлы, как раз то, о чем вы сказали - оверлейные образы, динамически подгружающиеся (прямо в NAND) в зависимости от оператора сети и (возможно) других условий через ПО в ОС:
Листинг директории всех образов в одном пакете прошивки.
┌──(up4k㉿UP4K-X1C)-[/mnt/c/Windows/Firmware/FwSwitchbin] └─$ du -ah FirmwareData 29M FirmwareData/18500.5001.00.05.27.12_5021.11_Secureboot.fls 29M FirmwareData/18500.5001.00.05.27.16_5021.11_Secureboot.fls 29M FirmwareData/18500.5001.00.05.27.30_5021.11_Secureboot.fls 368K FirmwareData/ATT_cust.201.046_signed.fls 372K FirmwareData/DoCoMo_cust.216.044_signed.fls 368K FirmwareData/DT_cust.119.042_signed.fls 364K FirmwareData/Generic_cust.001.035_signed.fls 376K FirmwareData/KDDI_cust.115.046_signed.fls 368K FirmwareData/Optus_AU_cust.226.036_signed.fls 8.0K FirmwareData/package_info.xml 364K FirmwareData/Saudi_cust.234.018_signed.fls 372K FirmwareData/SBM_cust.218.047_signed.fls 368K FirmwareData/Sprint_cust.102.034_signed.fls 368K FirmwareData/Swisscom_cust.210.037_signed.fls 368K FirmwareData/Telefonica_cust.205.036_signed.fls 368K FirmwareData/Telstra_cust.107.044_signed.fls 368K FirmwareData/TMO_cust.203.013_signed.fls 364K FirmwareData/Turkey_cust.232.010_signed.fls 368K FirmwareData/Turkey_VDF_cust.233.010_signed.fls 372K FirmwareData/Verizon_cust.101.043_signed.fls 368K FirmwareData/Vodafone_Germany_cust.202.038_signed.fls 92M FirmwareData
Ну и напоследок. С чего вы чёрт возьми взяли что файл должен обновляться весь целиком ? Уже десятки лет сущетствуют инкрементальные обновления. Которые затронут только изменившиеся части при апдейте. Может здесь применяться подобная технология ? На мой взгляд - запросто.
Самый первый du в моем первом комментарии - это как раз то, что прилетает в виде апдейта от производителя (через Windows update или fwupd) - ZIP-файл со всеми вариантами прошивки. 140 Мегабайт. И в каждом из вариантов, и во всех оверлеях (!!!) есть те самые слепки окружения шелла со всеми переменными. И не по одному разу.
Наезд на 29 мегабайт - ойвей, 2023 год на дворе, сейчас полное обновление для мобильного телефона(!!!) занимает по 2-4гб.
Да, вот только это не мобильный телефон, а модем. Довольно значительную часть работы которого обеспечивает ОС компьютера, к которому он подключен. Да, там в апдейте помимо образов есть исполняемые файлы для этой ОС, которые следят за тем, какой из образов динамически подгрузить. Это особенности реализации, необходимое зло. Моё недоумение не к этому.
Вообщем моё мнение - и комментарий не по адресу, и явное отсуствие попытки разобраться в деталях самостоятельно.
Чтобы разобраться в деталях того, каким чудесным образом в продакшн-образе ФС прошивки оказались динамические переменные окружения шелла разработчика, нафига они там нужны, а главное - почему они там не по одному разу (натурально, полные слепки переменных во время сборки, включая флаги gmake и вообще всё остальное; я для краткости грепнул переменные SSH, как самые курьёзные/ненужные), нужно либо курить то же, что разработчик (читайте - разбираться в тонкостях и тенденциях прошивкостроительства), либо потратить часы и дни на реверс прошивки, чего мне совсем не хочется. Вы заявили, что раздувание кода прошивки модема имеет смысл - я подумал, что вы в теме, и сможете объяснить, почему китайские...
Китайские же?
┌──(up4k㉿UP4K-X1C)-[/mnt/c/Windows/Firmware/FwSwitchbin] └─$ strings FirmwareData/* | grep USER=| sort -u USER=fujike USER=guantao USER=lihongwei USER=zhaixianni
Почему китайские коллеги так делают. Согласен, это не раздувание исходного кода, это хуже - раздувание бинаря, нет, бинарей ФС прошивки, которые во множественном числе безусловно скачиваются пользователю при апдейте.
Предвижу ваше возражение - вы говорили про исходники. Но позвольте пару риторических вопросов: если у них такое в клозетах, то что у них в головах? И если спагетти-код называют индусским, то как называть такие вот лазанья-бинарники?
В разрезе темы статьи. Автор предлагает совмещать две "культуры" программирования. Я не программист, вижу всё это под иным углом. Для меня люди, которые разрабатывают ПО - это разработчики. Кодеры, архитекторы ПО (software engineers) - всё одно. Люди, которые переводят ТЗ в его реализацию. К процессу перевода можно подходить по-разному. В конечном счете это влияет на business value работодателя (заказчика, коим может быть сам разработчик) и на самооценку разработчика. Не важно, как ты это сделаешь, главное, чтобы все причастные были довольны (включая тебя самого). И к опенсорсу в тч. это относится в полной мере, да. И к 1C. И к PHP.
А если у разраба
низкая социальная ответственностьсамооценка, которая не зависит от производимого им кода - получаем неадекватное ПО. Неадекватное требованиям заказчика, неадекватное потребностям конечного пользователя, неадекватное фазе луны и политическому курсу текущего режима (зачеркните лишнее).
AndreyAf
12.01.2023 08:02Размер прошивки GSM модема больше линуксового ядра где-то на порядок (уже ~50млн строк) и они совсем не бессмысленные.
странно конечно, но вы делаете пропорцию на сравнение опционально собранного бинарника и размера всего исходного кода для всех архитектур.
screwer
12.01.2023 11:18Hе я предложил строками исходника меняться, а автор. Я наоборот подчеркнул, что в конкретном линуксовом ядре бОльшая часть строк не будет задействована.
Pastoral
11.01.2023 12:36+2«Плюсовик начнёт её с заклинания #include » - А я то по наивности всегда считал, что плюсовик начнёт с «import std;».
А когда я слышу слово «культура», я хватаюсь за С.
klirichek
11.01.2023 18:02+2Плюсовик, умеющий в import - это уже шаг в сторону 2-й категории.
А в энтерпрайзе, где "лишний раз не трогай, как бы чего не сломалось" в сторону С++20 с его импортами начнут смотреть только когда протухнут все LTS-версии целевых дистрибутивов (вот ubuntu xenial протухла в прошлом году - и стало можно перейти на С++17).
Вот Go с его возможностью сделать import "github.com/company/package" прямо из текста, при этом с типизацией и прочим - вызывает сильное неравнодушие "да что ты, блин, такое?!!"
Aldrog
11.01.2023 18:35+4import std;
это даже не C++20, а ещё не утверждённый C++23.klirichek
11.01.2023 20:56-1я смотрел доклад Саттера на CppConf, где он изобрёл Cpp2.
Очень-очень круто, минус много-много аргументов у адептов ржавчины (rust), но это вообще не самоцель, а просто самоочевидно, что без холиваров можно плюсы сделать сильно современнее. Но, говоря как пессимист - это то, о чём будут рассказывать уже наши дети... своим внукам...
klirichek
11.01.2023 21:00Вот я совсем вдумчиво и детально не смотрю, но возникло ощущение, что он таки утверждён. (если я неправ - ссылочку бы, что оно не так. Потому что далее кажется только детали реализации, особенно stdlib. Потому что C++17, несмотря на "зрелость", тоже пока что не весь/везде реализован)
gandjustas
11.01.2023 12:44+5Очередная статья про C++ vs Python, вместо Python можно подставить любой современный динамический типизированный язык. Аксиома Эскобара говорит о том, что обе альтернативы так себе.
Надо найти вариант, который содержит лучшее из обоих миров и заимствует как можно меньше плохих качеств.
Как минимум необходимо:
Богатая библиотека пакетов с готовыми решениями задач
Лаконичный синтаксис без `public static void main overhead` и c переопределением операторов
Статическая типизация со структурной в определенных пределах (duck typing)
IDE с богатыми возможностями проверки корректности при наборе, подсказками, сниппетами и навигацией по коду
Я знаю несколько таких языков, которые более-менее соответствуют
C# (с натяжкой и только последней версии)
Rust
TypeScript
Для всех них есть прекрасная IDE - VSCode
Layan
11.01.2023 15:02+9есть прекрасная IDE - VSCode
Он конечно хорош, но далек от IDE.
whoisking
11.01.2023 17:37Смотря для чего, для флаттера, например, возможности просто космос
Layan
11.01.2023 17:58+4Прям из коробки без траты кучи времени на подбор плагинов, их настройку? Иначе так и vim можно IDE называть. Но от этого оно им не станет.
whoisking
11.01.2023 18:21+1Без траты, только плагин флаттера установить, может пути какие-то указать, но телодвижений точно минимум и не больше чем в альтернативе - андроид студии
Layan
11.01.2023 19:38Без траты, только плагин флаттера установить
Так в этом и разница. IDE - готовый продукт. Иначе так браузер тоже можно назвать IDE, открыв веб страницу с редактором текста, или установив расширение.
whoisking
11.01.2023 20:19+3Немного согласен с вашим определением, для меня идэе это когда фич, касающихся разработки там полно и это не просто редактор текста, а рефакторинг, работа с зависимостями, анализ кода, отладка, анализ перформанса и всё это с кнопками в UI. Вимы и другие редакторы с прикрученным LSP действительно полноценной IDE сложно назвать, это что-то посередине, но вот некоторые плагины под vscode действительно делают его сносной штукой под некоторые стеки
gandjustas
11.01.2023 23:19Почему это?
IDE это:
Текстовый редактор, который "понимает" код, обеспечивает навигацию по элементам программы - классам и функциям
Навигация по элементам проекта - в большинстве случаев это навигация по файлам и папкам
Интерактивная отладка
Графические дизайнеры для элементов UI
VSCode не умеет первые три пункта из четырех. Поэтому не подходит для WinForms например, но вполне подходит для веб-разработки.
0xd34df00d
11.01.2023 18:25+6Хаскель ещё. Особенно синтаксис — лаконичнее, пожалуй, только лиспы.
Alexey2005
11.01.2023 19:32+5Это write-only код. Разбираться в чужом коде, написанном в функциональном стиле - это ад, потому что там вместо наследования композиция. То есть программа фактически "собирается" в рантайме, её точная структура напрямую из кода выводится с изрядным трудом.
И не существует способа быстро проверить, при каких условиях выполняется вот именно данный участок кода (и выполняется ли он вообще). Потому что эта функция не вызывается напрямую, а передаётся в качестве параметра (одного из возможных параметров) в другую функцию, а оттуда в третью в составе какого-нибудь списка, который обрабатывается внутри лямбды, где и происходит собственно вызов. При этом ничто в синтаксисе не говорит о том, что вот именно в этой строчке произойдёт вызов искомой функции.
0xd34df00d
11.01.2023 20:18+7Разбираться в чужом коде, написанном в функциональном стиле — это ад, потому что там вместо наследования композиция. То есть программа фактически "собирается" в рантайме, её точная структура напрямую из кода выводится с изрядным трудом.
Подождите, но ведь это при наследовании программа собирается в рантайме! И при этом ваш стандартный ООП-язык имеет слишком слабую систему типов, чтобы ограничить поведение наследников, поэтому там количество вариантов и возможность статического анализа куда хуже.
Поэтому стандартный ООП-код — это нечитаемое месиво, иногда присыпанное попытками использовать паттерны, которые очень редко угадывают, когда и куда код будет расширяться, что и превращает его в это самое месиво со временем.
В то же время я ещё не встречал проекта на функциональном языке (будь то хаскель, идрис, агда…), с которым у меня были бы сложности с именно самой структурой проекта. Непосредственная сложность предметной области — да, но от неё никуда не деться.
И не существует способа быстро проверить, при каких условиях выполняется вот именно данный участок кода (и выполняется ли он вообще).
Зачем вам это знать, если код не имеет эффектов?
А если код имеет эффекты — это есть в типах.
masai
13.01.2023 02:53Haskell и лиспы слишком разные, чтоб их сравнивать. А то так APL победит в лаконичности.
pharo
11.01.2023 23:08+1А, ещё «есть» FreePascal, FreeBasic…
Давайте чтобы расцветали все цветы. :)
P.S. Для FreePascal Инструкция по насторойке VSCode
Учебник по языку FreeBasic.pdf за авторством Стаса Будинова.gandjustas
11.01.2023 23:20-2Паскаль это образец страшного синтаксического оверхеда и по сути ничем не лучше C#. Все что есть в современных версиях паскаля есть и в C#.
vabka
11.01.2023 23:17-1Богатая библиотека пакетов с готовыми решениями задач
Лаконичный синтаксис без `public static void main overhead` и c переопределением операторов
Статическая типизация со структурной в определенных пределах (duck typing)
IDE с богатыми возможностями проверки корректности при наборе, подсказками, сниппетами и навигацией по коду
Кажется, что все критерии выбраны специально под Go, которого почему-то нет в примерах.
> C#
Так у не как раз и есть public static void. Хоть его и убрали из Main - в остальных местах он остался.
Да и VSCode не особо то с ним работает.gandjustas
12.01.2023 00:35Уже год как C# живет без `public static void Main`, а с недавнего времени еще и шаблоны проектов без него. в VSCode работает прекрасно, проверял буквально вчера.
gandjustas
12.01.2023 00:56+2Что касается Go, то я с ним сталкивался последний раз больше года назад и он оставил впечатление языка значительно более низкого уровня, чем C#. Для решения тех же задач что на C#\.NET кода на Go было примерно в 3 раза больше.
Возможно потому что библиотеки такие, возможно потому что язык слаб - генериков нет, анонимных типов нет, лямбды ограниченные, про PM и record types не слышали.
В итоге Go очень многословен. Но не в синтаксисе, а в количестве кода, который надо написать для решения задачи.
mbait
11.01.2023 12:58+18Всё это ерунда по сравнению с другой, смежной проблемой:
Я знаю Python, буду писать на нём.
Кажется, интерпретатор Python слишком медленный для управления роботом в реальном времени.
man cython
,apt install python-dev
О, напишу статью на Хабр, как переписать весь код на Си, но в виде питоньих модулей. Там обязательно будут сырые указатели и доступ к разделяемой памяти. Обязательно назову её "как мы ускорили код на Python в 100500 раз".
"Дорогие коллеги, это был замечательный проект, на котором я узнал много нового, но настала пора двигаться дальше. Поэтому я принял прежложение в компании X. Она занимается разработкой носимого устройства под управлением ATtiny25, там я буду заниматься разработкой ядра встроенной ОС на Haskell."
MyWave
11.01.2023 15:33+3На каком из этих 5 этапов возникает "проблема" и у кого?
TimsTims
11.01.2023 22:44-1По моему, на каждом этапе возникает проблема. То у самого разработчика, то у его работодателя, а в будущем и у их клиентов. И самое забавное - никто не увидит когда была допущена главная ошибка...
nzinov
11.01.2023 13:16+3По-моему, тут смешаны как минимум две разных оси. Различие в подходах "быстрее запрограммировать" и "быстрее работает", о котором речь в статье, и постепенное появление новых и новых уровней абстракции. Программисты на КПДВ совсем не обязательно из разных культур, они просто работают на разных уровней абстракции. Программист, который пишет на плюсах совсем не обязательно полностью разбирается в том, что происходит: например, он наверняка не разбирался в микрокоде процессора и ассемблере, на котором написан компилятор плюсов. В каждый момент времени какие-то стандартные вещи принимаются как черный ящик и над ними строится что-то новое — и это не зависит от описанных двух культур. Точно так же в пассаже про Дейкстру и бакалавров речь скорее не про две культуры, а про эпохи накручивания новых слоев абстракции. Возможно полезнее искать различия двух культур в одном слое абстракции: например в ML часть исследователей клепает эксперименты, объединяя готовые архитектуры, а часть доказывает теоремы о том, почему работает ML.
antarx
11.01.2023 13:51+3Кажется в дихотомии 2 культур самое ценное — найти, где эти культуры пересекаются. Где с одной стороны есть читаемость и удобство второй культуры, и гарантии=сходу понятное для программиста поведение первой. Я пристрастен, но Go как раз об этом — сделан «людьми из множества {Керниган; Томпсон; Вирт; Хоар; Дейкстра; Торвальдс}», и для людей второй культуры, с максимально низким порогом входа. Особенно это характерно для документации, которая хранится в коде, и автоматически подтягивается в онлайн документацию из гитхаба.
Отдельно, обе культуры объединяются в мире микросервисов, где гарантии вызова между сервисами заведомо слабые, а строгая e2e проверка чаще всего невозможна.
Gryphon88
11.01.2023 15:10-1Спасибо за статью. У меня такой вопрос: как на этапе собеседования распознать культуру разработки, принятую в команде? Я ближе к категории 1, и поэтому начинаю дергать глазиком на словах «молодая развивающаяся команда, agile, гибкие методологии», но возможно есть более четкие критерии?
PavelMSTU
11.01.2023 15:32+1Да нет никакой второй культуры... Просто программирование стало массовым явлением. По этой причине потребовались "люди попроще". Вот вам и "вторая культура".
При этом есть серьезные спецы, которые немножко недоумевают. Часть из них становится чуток токсичными и они "защищаются" от всех этих вайтишников и инфоцыган созданием своей "культуры 1".Я не принадлежу ни к одной из них. Люблю вырви мозг алгоритмы и чистый Си, многое пишу на Python. Меньше токсичности, больше дела.
Культура 1: формальные языки, алгоритмы поиска, анализ конечных автоматов, хитрые структуры данных
Культура 2: deep learningВы серьезно? :) Не путайте реальный Data Science, Machine Learning, Deep Learning и "я установил scipy, torch...
Как практикующий DS-ник с большим опытом и работы руками, и руководства, и R&D с патентами... могу сказать следующее: очень много хайпа среди "культуры 2". Сейчас в моде диплёрнинг, через десять лет будет другое... Это просто хайп.
Поэтому правильно писать так:
Культура 1: формальные языки, алгоритмы поиска, анализ конечных автоматов, хитрые структуры данных
Культура 2: хайп (сейчас это deep learning)
cross_join
11.01.2023 15:59Не совсем понятна новизна ситуации. С 1960-х годов, когда были изобретены (но далеко не все внедрены) 80% используемый сейчас технологий, появилось разделение на системных и прикладных программистов. Для работы первых важна надежность и производительность программ. Прикладникам всегда важнее быстрее реализовать запрашиваемую функциональность. Тем более прикладникам "домашней" (in-house) разработки. Та, что раньше велась в отделах АСУ предприятий.
Поэтому всегда были рядом Фортран и Лисп, потом Си и Бейсик (Си++ и Вижал Бейсик), Ява и Ява-скрипт и т.д. и т.п. И было еще множество инструментов "посередине", таких как Паскаль-Дельфи. И появлялись культуры использования операционных систем, вместо их написания. Потом СУБД.
Питон - это прекрасный заменитель старого Бейсика с теми же целями: побыстрее реализовать небольшую по масштабам прикладную задачку. Побыстрее вставить свой кирпич в общую стену-плотину, затыкая потоки прорывающейся воды пользовательских историй.
Речь не о культуре, а о специализации.
Layan
11.01.2023 18:01Для работы первых важна надежность и производительность программ. Прикладникам всегда важнее быстрее реализовать запрашиваемую функциональность.
На самом деле нет. Вопрос в решаемых задачах. А вопросы к надежности - это вопрос исключительно квалификации исполнителей и требований заказчика.
Можно и во ebmedded делать то, что будет зависать регулярно и ни о какой стабильности даже речи не будет. А можно прикладные задачи решать с максимальной стабильностью и производительностью, лишь бы бизнес на это деньги выделял (и в определенных сферах типа финансов это делается).
cross_join
11.01.2023 18:40На самом деле нет. Вопрос не в том, что можно делать, а в том, что нужно. Несмотря на.
Все системные продукты (ОС, СУБД, виртуализация, сервера приложений...), компиляторы, базовые библиотеки, фреймворки по умолчанию имеют повышенные требования к надежности, производительности, развертыванию, зависимостям и прочая. Для прикладных систем нужно дополнительное обоснование.
Layan
11.01.2023 19:35Все системные продукты (ОС, СУБД, виртуализация, сервера приложений...), компиляторы, базовые библиотеки, фреймворки по умолчанию имеют повышенные требования к надежности, производительности, развертыванию, зависимостям и прочая.
Прям все? Кучу продуктов, которые имеют такие названия и работают не стабильно и не надежно вы пропускаете, берете во внимание только те, которые действительно имеют такие требования? У меня вот был давний опыт использования одной БД, которая без утечек памяти не могла проработать неделю и требовалось по крону ее перезапускать. Это другое?
cross_join
11.01.2023 19:44Вы "опровергаете" повышенные требования к СУБД тем, что такие требования есть у вас, как пользователя, а неполное им соответствие вызывает у вас же проблемы эксплуатации?
Выглядит как подтверждение сказанного выше, спасибо.
Aquahawk
11.01.2023 17:50очень холиварно, когда начинал читать подумал что согласен, по мере чтения и применения на себя и коллег, понял что всё очень относительно. Проще хочется сказать: Профессионализм и непрофессионализм. И профессионал может задать вопрос, а что на самом деле нужно бизнесу и где нужно бороться за 2% а где нет. А где составить план что делаем без борьбы, какие метрики оценить и когда включаться в борьбу, попутно ещё и поправку на скорость найма, но это уже даже сильно каждый техлид команды может.
JI0C0Cb
11.01.2023 21:04Увы, но зачастую подходы, якобы облегчающие работу программиста, по факту не очень-то её облегчают.
event1
11.01.2023 21:08+1откуда такая дихотомия? на каком основании те или иные подходы отнесены к первому или второму типу? Перефразируя старый анекдот: программисты делятся на плохих и хороших. А уже плохие делятся на культуру 1 и культуру 2. Или (простите за автоцитату): "Программистам, не постигшим Дао, никогда не хватает места и времени для своих программ. Программистам, постигшим Дао, всегда хватает места и времени для достижения своих целей. Разве может быть иначе?"
slonopotamus
11.01.2023 21:20Неистово плюсую, дихотомия ложна чуть менее чем полностью.
Наблюдаю у себя примерно половину пунктов из первой и примерно половину из второй культур и задаюсь вопросом, о чем вообще автор. Т.е. сами аспекты расписаны хорошо, но вот выводы, которые из этого автор пытается сделать, полная дичь.
И мало того. В зависимости от контекста (что за софт я пишу, для какой аудитории/окружения, на каком языке), приоритеты могут быть разными.
onyxmaster
11.01.2023 21:57+2Про round написано что-то странное.
"Банковское" округление (к ближайшему целому за исключением дробной части 0.5, округляемой к ближайшему чётному) не требует никакой особой логики на большинстве современных компьютеров, потому что является режимом округления по умолчанию в стандарте IEEE-754. То что в Python его реализация сложнее чем просто вызов (на x86/x64) frndint (при условии что текущий режим округления -- стандартный) -- судя по коду в основном результат того, что round там поддерживает округление до произвольного знака, а не просто до целого.
lyadnov
12.01.2023 00:21+3Культура 1 прочно занимает свою нишу потому, что производительность железа упёрлась в фундаментальные ограничения физического мира. Но есть ощущение, что вся движуха в Культуре 2.
vassabi
12.01.2023 01:31+1так ведь пока культура1 фиксит баги предыдущего проекта Культуры2, Культура2 уже продала три новых проекта
Andruh
12.01.2023 09:43Интересно, хотя не согласен с некоторыми деталями. Например, приходится читать, конечно, код используемых библиотек, но я предпочитаю работать с подсистемами через контракты, т.е. через формальное API, ну и бенчмаркинг. Разбиратся как печатает число ryu - интересно, но не продуктивно, к примеру.
Вообще, хотелось бы подняться чуть выше и определить главное мировоззренческое отличие, разделяющее две культуры. В первом случае программирование - цель, во втором - средство. Это примерно как work-life баланс. Не стоит впадать ни в одну крайность (иначе будешь читать весь код Linux или станешь совсем бестолковым раздолбаем, методом тыка комбинирующим высокоуровневые кирпичики), но стоит уметь соединять обе культуры в оптимальных пропорциях в каждой конкретной части работы.
Karl_Marx
13.01.2023 15:00-2Поздравляю, вы заметили чем отличаются интровертная и экстравертная логика. Еще три таких наблюдения, и вы заново изобретете колесо, которое Юнг изобрел еще 100 лет назад.
Karl_Marx
13.01.2023 22:24-1Униженные и оскорбленные моим комментарием относятся к первой группе товарищей, сосредоточенных на своих внутренних представлениях о том, как по их личному мнению должно быть, а не на том, как есть на самом деле.
AndreyMust19
13.01.2023 19:56Вот как раз из-за чуваков 2-ой культуры у нас на компьютерах все и тормозит.
baldr
Ну вот опять начнем холивары на тему "статическая или динамическая типизация", "C или Python", "компилируемые vs интерпретируемые"...
В смысле "несколько лет"? Даже Фортран появился уже лет 70 назад и тенденция пошла уже оттуда.
Простите, но по-моему только студенты и junior-разработчики еще по-серьезному могут сравнивать разработку на Python и на C или C++. Вы не будете красить стену фломастером - вы возьмете краску и валик - разные области применения же!
yafinder Автор
Речь же не о том, "что лучше". Ровно наоборот, основной тезис - в обеих культурах можно подсмотреть полезные принципы и подходы, так что не игнорируйте их.
Кроме того, я считаю, что в нашем деле очень полезно в явном виде формулировать, почему для той или иной задачи подходит тот или иной инструмент, даже если это кому-то кажется очевидным.
shpaker
Вот под таким углом для меня статья новыми красками заиграла. Но я как-то не уловил этого по тексту.
baldr
А в статье этого и нет, практически. Может автор и не хотел этого, но большинство приведенных примеров довольно однобоки и очень спорны.
Ну вот что это такое, а? Что вы там при статической сборке - просматриваете все исходники библиотек всех версий что ли? Вы точно так же из репозитория качаете какой-нибудь libgstreamer, причем, скорее всего, вообще бинарник. А если не бинарник - то он для своей сборки требует столько сторонних библиотек, что leftpad я бы, на вашем месте, постеснялся приводить в пример.
Этот манипулятивный пример из серии:
yafinder Автор
Есть опять же два подхода.
1) Предпочитаем фиксированную версию библиотеки, переходим на новую, только если на это есть серьезные причины. Предпочитаем отсутствие зависимостей, подключаем библиотеку, только если она делает что-то достаточно сложное. В этом подходе просматривать исходники на самом деле не так уж сложно, я так делал.
2) Предпочитаем последнюю версию всех библиотек, фризим версию, только если на это есть серьезные причины. Предпочитаем внешние библиотеки "велосипедам", пишем что-то свое, только если ничего готового, решающего ту же задачу, не нашли. В этом случае, действительно, нужно "все исходники всех версий" читать, и никто этого не осилит делать, конечно.
Ничего даже близко похожего на историю с leftpad в первом подходе произойти, мне кажется, не может. Зато второй защищен от уже исправленных известных уязвимостей старых версий и "велосипедов", потому безопасней в adversarial-смысле (по крайней мере, я много раз слышал такой аргумент)
baldr
Точно так же в серьезных компаниях никто ничего не тащит из интернета даже при разработке на Python или NodeJS. Ставятся внутренние репозитории с заранее одобренными пакетами и все собирается только оттуда.
Еще раз я объясню - ваша статья, на мой взгляд, построена на очень плохих манипулятивных приемах. В комментариях вы оправдываетесь правильными идеями, но от этого только еще хуже выглядит сама статья.
Вы в самом начале постулируете существование "только" 2 разных культур. Причем явно сразу классифицируете их как разработчиков на C/C++ и Python. И дальше по тексту, соответственно получается так что у вас первая "культура" (C/C++?) всегда использует более правильный подход, а вторая что-то там говнокодит.
Это как из анекдота: "Есть 2 категории людей - те кто делит людей на категории и остальные".
Так вот - нет явного разделения на такие "культуры", тем более на две. Основной посыл статьи - сразу неверный, хотя мысли вы, в целом, выражаете иногда верно.
yafinder Автор
"Ну вот что это такое, а? Что вы там <...> - просматриваете все исходники библиотек всех версий что ли?" (с)
Выбор же все равно будет между тем, предпочитать контролируемые фиксированные устаревающие версии и "велосипеды" или постоянные апдейты до свежих и добавление новых зависимостей в ущерб наблюдаемости. Да, так получается, что какие-то программисты склоняются к первому, а какие-то ко второму. По моим наблюдениям, с используемым языком программирования это очень даже коррелирует.
Это не входило в мои намерения, и жаль, что это так читается. Я в последнее время пишу в основном на Питоне (хотя и не продакшн-код), очень одобряю подход "производительность программиста превыше всего" и то, до каких пределов можно её довести при таком подходе, меня как раз впечатляет. Скорость развития современного AI, мне кажется, невозможна была бы в "первой культуре".
baldr
Андрей, пожалуйста, не примите мою критику лично к вам. У вас вполне может быть больше опыта чем у меня. В силу моего опыта - мне не нравится как вы изложили ваши мысли в статье, хотя я примерно понимаю теперь что вы хотели сказать.
На мой взгляд - статья плохая по многим пунктам, я выборочно озвучил лишь часть своих претензий, спорить по всем мне не очень интересно.
yafinder Автор
peace!
Tresimeno
Вам не кажется что этот подход имеет до боли много общего с "х-к,х-к и в продакшн + И так сойдет", которое аукается в виде изобилия одноразового вторичного продукта на рынке, причем не только в ИТ?
PrinceKorwin
Извините. Не удержался влезть в ваш диалог.
Таки да. У нас и свои репы и код библиотек просматривается. Особенно пристально стали это делать после того как после недавних событий зловредный код для РФ в первых появился.
И статические анализаторы проверяются и анализаторы на зловреды. И часть глазами проверяется.
WebConn
Насколько я видел (и представляю себе) применение тех же нейронок, которые были натренированы в Jupyter, после фазы research в больших проектах наступает фаза внедрения, когда получившуюся обученную сеть вводят в инфраструктуру, откуда она улетает в production.
А эта инфраструктура обычно больше похожа на проект "культуры 1" (даже когда написана на Python), потому что там надо учитывать кучу тонкостей вроде миграции с версии на версию, разворачивания на разных типах железа и так далее.
Мне тоже кажется, что предложенное вами разделение на "культуры" больше похоже на две фазы разработки продуктов, где сначала происходит r&d (где на качество кода в целом можно закрывать глаза), а потом внедрение (где разработка происходит скучно и медленно, потому что надо не поломать прод)
morijndael
Это вы со стороны программиста одобряете :D
Со стороны пользователя вы скорее всего проклянёте разработчиков до девятого колена /half-joking
btw, по поводу питона, и его производительности для программиста — вопрос очень спорный. Там, где мощная система типов отловила бы всё на уровне линтера в IDE, в питоне просто вылетит исключение в самый неожиданный момент. Быстрее пишешь — дольше дебажишь. inb4: тайпхинты решают от силы половину таких случаев, и только если они используются. Они не спасут от рантаймовой магии с
**kwargs
P.S. Естественно, речь про сложные системы. Для скриптов, где всё можно удержать в голове питон достаточно хорош
vkni
И превращают язык в недо-С++, органично совмещая недостатки обоих подходов. Казалось бы, надо использовать Хиндли-Милнера...
Вообще, народ массово не понимает, что разные языки должны использоваться для задач разного масштаба. (См. пойнт статьи).
DEADMC
Это точно про Яндекс? :D
AlexBonel
То есть названия поста и самых первых предложений:
недостаточно?
vkni
Сказали вы, и в статье напрочь проигнорировали языки функционального программирования со статической типизацией и proof assistant's, растущие из них: OCaml/SML/Miranda/Clean/Haskell/Idris/Agda/Coq...
;-)
Они как раз растут из идей Lisp, который и ввёл вот эту совершенную безбашенность кода (см. макросы и недавно мною переведённую статью про quasiquotation) и REPL. При этом, языки из семейства ML совмещают гибкость Lisp и строжайшую статическую типизацию.
Зато вы упомянули Rust, который является наследником Clean, OCaml и C++! Это хорошо! ;-)
А вы, оказывается, всё некинокритикуете. :-) Спасибо за статью!
johnfound
А художники регулярно именно так и делают. В самом широком смысле (не всегда фломастером и не всегда стену), если понятно о чём я. ;)
QDeathNick
Если у меня есть много фломастеров, то что мешает соорудить из них большую кисть и быстро покрасить стену :)
johnfound
Ну, это не ко мне. Я сам крашу фломастером – пишу все на ассемблере. И мне такое нравится.
vkni
Да, типа Lisp vs FORTRAN.