company_banner

Ещё один однострочный пакет npm сломал экосистему JavaScript

    В субботу обновление маленькой библиотеки is-promise вызвало переполох в значительной части экосистемы JavaScript. Зависимости от сломанной библиотеки встроены в 3,4 миллиона проектов. Это уже второй раз, когда крошечный проект JavaScript вызывает проблемы такого масштаба.

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

    declare function isPromise<T, S>(obj: Promise<T> | S): obj is Promise<T>;
    export default isPromise;

    Предназначение библиотеки состоит в проверке, что JavaScript-объект является типом Promise, то есть представляет собой конечное завершение асинхронной операции и её результирующее значение.

    При использовании в продакшне функция isPromise возвращает логический результат yes или no.

    Хотя это всего две строки кода, выполняющие базовую проверку, библиотека is-promise является одним из самых популярных на сегодняшний день пакетов JavaScript npm. По данным GitHub, библиотека является частью более чем 3 431 756 проектов и используется в качестве зависимостей 767 другими библиотеками JavaScript.

    В минувшие выходные библиотеку обновили, чтобы реализовать поддержку работы в качестве ES-модуля — стандартизированной модульной системы JavaScript.

    Но оказалось, что is-promise версии 2.2.0 не соответствует стандартам модуля ES. Как только вышло обновление, проекты, которые использовали is-promise внутри цепочки сборки, начали выдавать ошибку при сборке из-за неправильной поддержки ES-модуля (1, 2, 3, 4, 5, 6, 7, 8, 9, 10).

    Сбой библиотеки мгновенно отразился на множестве проектов: от проприетарных проектов с закрытой кодовой базой до некоторых крупнейших опенсорсных проектов экосистемы JavaScript.

    Ошибка распространилась, в том числе, на следующие проекты: приложение Create React от Facebook (стандартный шаблон для создания приложений React), фреймворки Angular.js, Serverless (AWS), Firebasse-tools, Nuxt.js, AVA и многие другие.


    Ошибка не привела к сбою работающих проектов, поэтому не произошло никаких фактических простоев, но она мешала разработчикам компилировать новые версии своих проектов.

    Мейнтейнеры is-promise оперативно выпустили обновление, но оно не смогло исправить проблему. В конечном итоге пришлось отказаться от поддержки модулей ES в следующей версии 2.2.2, выпущенной через несколько часов после того, как начались массовые сообщения об ошибках из зависимых проектов.

    Это второй инцидент, когда крошечная библиотека JavaScript вызвала проблемы во всей экосистеме JavaScript. Нечто подобное произошло в марте 2016 года, когда автор библиотеки left-pad (из 17 строк кода) решил ни с того ни с сего отозвать свою библиотеку, чуть не сломав подобным образом тысячи проектов. Код left-pad просто заполняет левую часть строки нулями или пробелами. Несмотря на простоту модуля, в тысячах приложений менеджер NPM не смог установить зависимости между пакетами. Чтобы в экстренном порядке исправить ситуацию, технический директор и сооснователь NPM Лори Восс (Laurie Voss) пошёл на беспрецедентный шаг (так он назвал его в твиттере) и, «учитывая серьёзность и распространённость проблемы», отменил отзыв модуля. Была опубликована старая версия модуля, аналогичная последней.

    В инциденте с is-promise отзыва модуля не произошло, но он опять вызвал вопросы и породил обсуждение — действительно ли в экосистеме JavaScript необходимы однострочные библиотеки.

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

    Вот что по этому поводу считает Дэвид Хейни (David Haney), ведущий инженер-программист Stack Overflow:

    «Создаётся впечатление, что участники экосистемы NPM взрастили фетиш микропакетов. Вместо того, чтобы написать любую функцию или код, они как будто предпочитают установить зависимость на что-то, написанное кем-то другим. Мне кажется, что вся работа программиста в экосистеме NPM сводится к написанию как можно меньшего количества кода, чтобы связать вместе существующие библиотеки, вместо создания чего-то нового, функционирующего уникальным образом, для личных или коммерческих нужд.

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

    Во-вторых, даже если логика в пакете правильная, меня поражает тот факт, что разработчики устанавливают зависимости на однострочные функции, которые должны сами уметь писать с закрытыми глазами. Если ты не можешь написать функцию left-pad, is-positive-integer или isArray за пять минут (включая время на поиск в Google), то ты вообще не умеешь программировать.

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

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

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

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

      +8

      Принцип DRY, доведённый до абсурда...

        +13

        Т.е. импортируют пакет ради проверки is-a? Напоминает старую шутку про jQuery a+b.

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

            Мне кажется, это не простота, а неуверенность в своём кругозоре. Слишком часто бывают ситуации, когда забыл предусмотреть какие-то варианты. Например, в домашнем проекте мобильный браузер можно определить по наличию ontouchstart у document.body, а на работе нужно учесть, что бывают компьютеры и ноутбуки с сенсорным экраном и ещё какие-то варианты, и поэтому "какой-нибудь дядя, который занимался этим вопросом настолько, что сделал отдельный пакет, всяко лучше меня подумал над вопросом", а ещё "есть очень большой шанс, что тот дядя был из числа людей, которые обычно пишут отзывы, что программист за 30 тысяч рублей ничего не умеет, а то получал бы в 10 раз больше, и следовательно, этот дядя точно лучше знает, как решать любую задачу".

              0

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


              Потому что любая зависимость — потенциально +1 уязвимость.

                +4
                Копировать к себе, не зная нюансов лицензирования — плохой обычай. В частности, ЕМНИП, ответы со stackoverflow защищены авторским правом, и в серьезном проекте за их копирование можно влететь. А переписывать свой код по чужим чертежам — ну, тоже спорное решение. Применимо только к микрозависимостям вроде этих. Я думаю, что самым верным решением будет просто не обновлять зависимости автоматически, а делать это вручную время от времени, заодно мигрируя свой код при наличии breaking changes в обновленных зависимостях.
                  0

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

                    +1
                    Немного уточню: Весь код на SO автоматически лицензируется по CC BY-SA в соответствии с условиями: stackoverflow.com/legal/terms-of-service#licensing
                    +1

                    Тогда не будешь получать обновлений опыта «дяди», а в JS это важно в связи с тем, что постоянно появляются новые API и особенности браузеров

                      0

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

                        +1

                        Мой аргумент в общем про подход, а не про конкретный пример. Но даже в нём могут вскрыться нюансы. Например, судя по исходному JS-коду этой функции, она не проверяет наличие в объекте метода catch. Может оказаться, что из-за её отсутствия происходит баг, и автор репозитория починил это баг. Вручную проверять актуальность подобных мелочей — та ещё рутина.

                          0

                          Что является промисом — объяснено в доках, например, на MDN. Написать свою функцию или на крайний случай проверить чужую — дело минут. Обновление такой функции не понадобится. Проверять актуальность не понадобится.


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


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

                          +1
                          Если стандартное API обновится и вдруг появятся новые определения промиса, на крайний случай можно будет потратить несколько минут на обновление функции.


                          Для начала вам нужно будет понять когда и что и где делать и делать ли или это обновление ваш код минует. Это не несколько минут. Одно только понимания факта что нужно обновить, одно только заметить что нужно обновить — это уже куча времени.

                          А теперь посчитайте сколько таких вещей в вашем коде.

                          0xf0a00
                          Ну да, ведь код какого то нонейма из npm 100% надежен, протестирован и в нем точно все учтено. И он точно лучше чем тот код который написали вы лично сами зная свою специфику проекта…


                          Как минимум он уже существует. Что экономит вам время. Немного тут, немного там. В итоге огромная экономия.

                          Представьте себе, что это код вашего коллеги, что работает в соседнем кабинете. Примерно то же самое по надежности.

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

                            0
                            Ну да, ведь код какого то нонейма из npm 100% надежен, протестирован и в нем точно все учтено. И он точно лучше чем тот код который написали вы лично сами зная свою специфику проекта…

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

                            «Ах что это, мне ж придется писать код, проверять самому..» Да *лять! Тебе деньги платят за то что ты код пишешь! Понимаю что не хочется, хочется полениться и в ютубик позалипать, но ты уж будь добр попиши хоть чуть чуть, чем таскать такую элементарщину из npm.

                              +3
                              Когда пишешь свою реализацию, то сам в одиночку должен следить за кодом всех своих проектов всех годов выпуска. А когда исполльзуешь библиотеку, то за этим же самым кодом следят, возмножно, сотни других людей, которые не 5 лет назад им пользовались, а делают это прямо сейчас, а значит, могут найти какие-то свежие проблемы, которые возникли в новых браузерах или что-то ещё такое.
                              Да и обновление отдельной функции или двух, но в 20 проектах, может быть сложным, если эти функции не вынесены в отдельный файл. Придётся лезть в файл, где они объявлены, и вручную вносить правки. Да, не у всех код настолько хорошо структурирован, особенно был годы тому назад.
                              Зато, если это сторонний пакет, то он просто обновляется весь.
                              +2
                              нужно будет понять

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


                              Представьте себе, что это код вашего коллеги

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


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


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


                              Вот Вы привели пример коллеги. Представьте, если бы мы наняли этого коллегу, чтобы написать в проекте 2 строчки кода. Ну не глупо ли это? Нужен определённый объём кода, чтобы достоинства превысили недостатки.


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

                            +4
                            Эти микропакеты — следствие отсутствия нормальной стандартной библиотеки и бардака в языке, когда даже простейшие операции вроде «является ли X объектом» требуют проверки кучи каких-то идиотских кейсов (а то и философских рассуждений что считать объектом).

                            stackoverflow.com/a/8511332

                            А как насчёт такого isPromise — «это promise если там есть метод „then“, но вообще вам не надо знать, что это promise». Даже учитывая, что прошло 5 лет — вечность по меркам JS — но всё равно, WAT?

                            stackoverflow.com/a/27746324

                            (За 5 лет, кстати, существенно ничего не изменилось. Упомянутый пакет делает то же самое)

                            return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof 
                                obj.then === 'function';
                            }

                            Бег по граблям как национальный вид спорта.
                              0

                              Несомненно, в языке есть изъяны. Это немного другой вопрос, но у я бы хотел высказать мнение, что в JS есть много странностей, потому что в отличие от многих других языков у него серьёзные требования к обратной совместимости. Производители браузеров не могут просто выкатить JS2, в котором выкинуто всё странное легаси, потому что практически все сайты перестанут работать.


                              В любом случае, изъяны надо как-то обходить. Мини-пакеты это не идеальный способ обходить их.

                          +3

                          Я не JS программист, но ведь в общем это действительно справедливо:


                          1) Проверка чего бы то ни было — только на первый взгляд тривиальна. Если вам кажется что это не так, то вы или неглубоко копнули, или ваш проект однодневка и не предполагается, что он будет работать лет 10 хотя бы. Хотя конечно, можно и самому нагородить однострочных функций в какой-нибудь базовый модуль, но за 10 лет таких функций накопится столько, что нужно будет нанять еще одно программиста просто поддерживать это месиво.


                          2) Програмиист за 30 тысяч рублей СКОРЕЕ ВСЕГО ничего не умеет. Это конечно может быть очень скромный самородок, которого нагло эксплуатирует компания, или же человек всецело преданный какой-то идее внутри сферы, в которой работает. Всё это редкость и чаще всего мы имеем именно ситуацию, когда программист за 30 тысяч действительно не очень (еще, потому что джун, или совсем, причин масса). Иначе он бы давно уже нашел себе работу получше)

                            0
                            А сколько штук получали программисты в компании, которая в РФ ГосЛинукс сделала? Вики говорит, что они не так сильно испортили CentOS и там даже работал «Apache Tomcat».
                          +22
                          Более того, в npm есть модуль для проверки на нечетные числа is-odd, подключающий модуль для проверки на четные числа is-even и вызывающий его через отрицание
                            +4
                            Ага, а ещё is-odd подключает is-number. И вообще имеет примерно пол миллиона скачиваний в неделю. Благо падает потихоньку.
                              +2
                              У меня в топе лидеров пекедж net (и да, разработчики его зачем-то подключили в проект, поэтому и узнал про него, пытки паяльником выявили только то, что «что-то не работало»).
                              +1

                              А нужно было скопировать is-even, изменить в нем один знак и объявить самостоятельным модулем?
                              Сорри, я не потроллить, я не из мира JS и действительно не понимаю.

                                0
                                IMHO, оба эти модуля просто не должны существовать, вот и всё.
                                  0

                                  Чтобы каждый разраб изобретал этот велосипед снова и снова.

                                    +2

                                    Што.
                                    Определение того, чётное число или нет, Карл.

                              0
                              Я в изучении jQuery дальше основ JS+CSS не дошел, но скажем на каком-то языке нужно ввести класс «3-мерный вектор».
                              И переопределить для него операции "+", "-" и X (векторное умножение [a,b]).
                                +1

                                Нет, не про это. То ли байка, то ли правда. Появляется на стэковерфлоу вопрос "Как сложить на JS два числа?".
                                Куча ответов в стиле "Напиши плагин для JQuery" с примерами кода как написать этот плагин а потом использовать.
                                А самый простой ответ "a + b" оказывается самым заминусованным.

                                  –1
                                  А самый простой ответ «a + b» оказывается самым заминусованным
                                  Потому что он не правильный. На JS 1+1 иногда бывает 11 :-)
                                    0

                                    Вполне нормальный ответ, ведь "бытие определяет сознание"(с).
                                    Эти люди мыслят фреймворками и лучше нас умеют подключать модули.

                                +14

                                В JS не хватает общепринятого дополнения стандартной библиотеки, куда и должны бы уйти подобные однострочники. Например в Java есть Guava и Apache Commons, там можно найти почти всё, что угодно. Думаю, это решило бы львиную часть подобных проблем.

                                  –3
                                  И тянуть её в браузер всю? Спасибо, но нет.
                                  (Да я знаю про тришейкинг, но всё равно нет).
                                    +7

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


                                    Правда была бы другая проблема — производители браузеров воевали бы за развитие stdlib, как в ситуации с CSS (я про все эти префиксы типа -moz-support-for-some-modern-non-standartized-bullshit).


                                    Благо сейчас CSS3 поддерживает уже такую кучу функций, что новые особо никому и не нужны.

                                      0

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

                                    0
                                    Вообще Guava совместима с GWT и нашпигована его поддержкой, что снижает её эффективность по сравнению с Apache Commons.
                                    Не надо использовать Guava если вы не используете GWT.
                                    Тестировал в jmh года полтора назад.
                                      0

                                      lodash/underscore/ramda

                                        0

                                        Ну, одно время было что-то подобное ввиде JQuery. Но сейчас в экосистеме JS любая stdlib просуществует не дольше месяца, пока очередной школьник ни накропает свою с блекджеком и пр...

                                          0
                                          Как насчет kotlin stdlib?)
                                          +10
                                          Нечто подобное произошло в марте 2016 года, когда автор библиотеки left-pad (из 17 строк кода) решил ни с того ни с сего отозвать свою библиотеку, чуть не сломав подобным образом тысячи проектов.

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

                                            0
                                            "Твой код это не твой код если ну очень хочется"

                                            Код был открыт (на момент удаления под WTFPL), так что именно это не такая большая проблема.
                                            Это плохо смотрится с точки зрения доверия к npm, но первая часть той истории с отжимом kik выглядит настолько сильно хуже, что только полный пофигизм js-разработчиков все ещё держит npm на плаву

                                              +2

                                              Спасибо за уточнение. Тогда да, "юридически всё норм". Но очень грязно.

                                                –1
                                                Грязно — это опубликовать пакет под свободной лицензией, дождаться пока все будут его использовать, а потом сказать, мол я передумал, нельзя вам больше его использовать.
                                                  +1

                                                  Он, насколько я помню, был убран из одного-единственного пакетного менеджера. И только.
                                                  Закрыт — не был.

                                            +3
                                            [пойду создам maven-зависимость isFuture для обертки instanceof Future]
                                              +2

                                              Кстати про instanceof. В CScript под Windows нет JSON парсера. Я взял хак со StackOverflow который:


                                              var htmlfile = WSH.CreateObject('htmlfile'), JSON;
                                              htmlfile.write('<meta http-equiv="x-ua-compatible" content="IE=9" />');
                                              htmlfile.close(JSON = htmlfile.parentWindow.JSON);

                                              Так оказалось что:


                                              var a = JSON.parse ("[1,2,3]");
                                              WScript.Echo (a instanceof Array ? "Array" : "Not Array") // Not Array
                                              
                                              WScript.Echo (JSON.stringify ([])) // {}
                                              0
                                              Ошибка не привела к сбою работающих проектоа, поэтому не произошло никаких фактических простоев, но она мешала разработчикам компилировать новые версии своих проектов.

                                              Компилировать новые версии своих проектов? Разве сборка без обновления зависимостей сломалась?

                                                0
                                                куча народу до сих пор не знает что такое npm ci и вообще зачем package-lock.json создан
                                                  +1
                                                  Тоже не всегда панацея. У меня в одном проекте был модуль webpack-obfuscator, залоченный на версию 0.18.0, но грабли прилетели, откуда не ожидалось — в одном из минорных билдов ноды ветки 12.х сломали совместимость с предыдущими версиями ноды в рамках одной и той же ветки (а именно, немного поменяли EventEmitter в 12.16.0). А казалось бы, что хотя бы в LTS такого быть не может и обратная совместимость будет обеспечена в рамках ветки, но нет, залоченные пакеты могут оказаться вдруг несовместимы с новой версией самой ноды, даже если это минорный билд. Что делать, для каждого проекта держать отдельную версию ноды?
                                                    0
                                                    Ну в рамках фантастики программист говорит админу сервера «Этот код работает на PHP 7.4. При обновление версии сервера не гарантирую, что код будет правильно работать». В Вашем примере это был видимо переход с Node.js 12.x на 13.x.
                                                    Если на проекте разные команды программистов и одна уже хочет в прод брать фичи 13.x, а остальные не успевают проверить совместимость старого кода — нужно как-то решать.
                                                      +1
                                                      Та нет, это был переход с 12.15.0 на 12.16.0. Если бы был переход на другую мажорную версию, я бы не удивился сломанному коду.
                                                      Набросал простенький пример, демонстрирующий эту проблему: github.com/SagePtr/test-issue452
                                                  –2

                                                  Если вместо использования npm ci кто-то использует npm i то вот они и попались. Больше людей научится использовать package.lock или yarn

                                                    0

                                                    npm i учитывает файл package-lock.json. npm ci для другого.

                                                      0

                                                      Расскажите пожалуйста, как npm i (install) учитывает package-lock файл (особенно для плавающих зависимостей вида ~ ^.

                                                        +1

                                                        В package-lock.json указаны конкретные номера версий всех зависимостей и подзависимостей без модификаторов типо ^, ~ и *. К тому же там указаны ожидаемые хэш-суммы пакетов. NPM устанавливает ровно те версии, которые указаны в этом файле.


                                                        https://docs.npmjs.com/cli/install
                                                        https://docs.npmjs.com/files/package-lock.json

                                                          +3

                                                          Спасибо за ссылки (без иронии, спасибо). Я минут 15 пытался понять что со мной не так, потому что npm ci возник не просто так.


                                                          Потом вспомнил, что npm не всегда был таким умным


                                                          И вот еще ссылки в тему:
                                                          https://github.com/npm/npm/issues/18103
                                                          https://github.com/npm/npm/issues/17979


                                                          Поэтому, с моей точки зрения, если нет уверенности в версии npm (у себя, у коллеги), лучше использовать npm ci
                                                          Это гарантировано не изменит package-lock файл.

                                                  0
                                                  Интересно, трояны и ботнеты уже строили поверх этой особенности? Чтобы купить или взламать доступ к репе с распространенным пакетом, и сунуть туда гадость? Аналогично, как постоянно идет скупка у авторов старых заброшенных расширений для хрома.
                                                  +5
                                                  Библиотека is-promise состоит из двух строк кода, а разработчики могут использовать её в своих проектах с помощью однострочного вызова.

                                                  declare function isPromise<T, S>(obj: Promise<T> | S): obj is Promise<T>;
                                                  export default isPromise;

                                                  Извините, а где здесь вызов? Тут вообще нет ни одной строчки Javascript-кода.
                                                    +10
                                                    Верно, автор статьи почему-то заголовочный файл тайпскрипта привел в статье вместо реализации. Должно было быть вот это:
                                                    function isPromise(obj) {
                                                      return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
                                                    }
                                                      0
                                                      del
                                                      +20
                                                      Статья по технической части на двоечку с плюсом.

                                                      Во-первых, проблема не в коде is-promise (благо этого кода в нём ровно одна строка и он не менялся), а в смене технологии модуля. Проще говоря, is-promise изменил packaging, и в зависимых пакетах из-за этого кое-где (не везде) сломались импорты/require.

                                                      Во-вторых, корневая причина ситуации — головотяпство авторов, выпустивших это изменение как minor version, в то время как по нормативам semver это должна быть major version (с breaking changes).

                                                      Собственно, сейчас они уже исправились, выпустив это всё как 3.0.0. Как сразу и надо было сделать.

                                                      Объем кода в is-promise тут вообще не при чём. Это мог быть и однострочник, и большой пакет, и вообще что угодно.

                                                      ЗЫ: В-третьих, учитывая, что модульных оберток в экосистеме JS с учётом ноды — сейчас 4 штуки (AMD, UMD, CommonJS, ES modules), эта ситуация совершенно обыденная, на самом деле. Разве что бомбануло чуть шире обычного из-за количества зависимостей.
                                                        +5
                                                        корневая причина ситуации — головотяпство авторов, выпустивших это изменение как minor version, в то время как по нормативам semver это должна быть major version (с breaking changes)

                                                        Да, но нет. Всё-таки, если человек не хочет поломок из-за зависимостей, он должен фризить версии полностью — вплоть до минорных (а ещё точнее — фризить с проверкой хэша).

                                                        Полагаться на семантическое версионирование — полагаться на честность и отсутствие ошибок у посторонних людей. Делать это стоит только, если код покрыт тестами.
                                                        +7
                                                        Отличная идея, давайте писать всё с нуля каждый раз. А то, что в «однострочнике» может быть проверка на какой-нибудь неочевидный случай — какая разница, верно? Подумаешь, новые баги будем плодить каждый раз.

                                                        Проблема не в зависимостях. И не в однострочниках. Что, если следующий баг будет в более крупной библиотеке? Реальная проблема состоит из трех частей:

                                                        1. Недостаточно точные версии зависимостей. Судя по файлам вида yarn.lock в большинстве репозиториев, на фронте наконец-то догадались сохранять точные версии используемых либ.
                                                        2. Централизация. Если упадет npm, то навернутся вообще все CI, кроме тех, которые у себя держат реестр используемых пакетов. В общем-то, аналогичная ситуация и с докером.
                                                        3. Нельзя менять уже существующие версии пакетов. Опубликовал в опен-сорс либу версии 1.0.0 — всё, хоть ты тресни, тебе больше нельзя её удалить или поменять там что-то, не увеличив перед этим номер версии. Не хочешь, чтобы люди ей пользовались — ну извини, надо было раньше думать.


                                                        Ну и реальная проблема есть, что в js маловата стандартная либа. Что-то вроде lodash давно пора включить во все сборки браузеров. На других-то языках пофиг, а тут каждые 10кб важны, и приходится раз за разом заново писать отдельные функции, вместо того чтобы тупо импортировать либу целиком.
                                                          +3
                                                          Что-то вроде lodash давно пора включить во все сборки браузеров.

                                                          Современный стандарт языка делает lodash на 90% не нужным вообще, а оставшиеся 10% — то, что нужно далеко-далеко не всем. Так что вообще-то уже де-факто включили.
                                                            –4

                                                            Ложь!


                                                            lodash умеет работать с объектами там где js только с массивами.
                                                            .map, .filter и так далее.


                                                            Современный стандарт языка делает lodash на 90% не нужным вообще

                                                            Откройте документацию лодаша и посмотрите что скорее наоборот, es6 может только 10% функций lodash реализовать в такое же количество строк. Я выделил жирным важную часть


                                                            Также лодаш "умнее / гибче" и может во многие функции принимать в качестве предиката строки, например _.filter(users, 'isActive') аналог users.filter(user => user.isActive)


                                                            Что согласитесь слегка удобнее. А еще удобнее передать туда объект
                                                            _.filter(users, {name: 'Alex', age: 12}) вместо users.filter(user => user.name === 'Alex' && user.age === 12)

                                                              +4
                                                              lodash умеет работать с объектами там где js только с массивами.
                                                              .map, .filter и так далее.

                                                              В 2020 году вам уже стоит открыть для себя Object.keys(), Object.values(), и главное — Object.entries().

                                                              Также лодаш «умнее / гибче» и может во многие функции принимать в качестве предиката строки, например _.filter(users, 'isActive') аналог users.filter(user => user.isActive)

                                                              Это достаточно бессмысленный сахар, который только затрудняет чтение кода, экономя максимум десяток-другой букв. Более того, хоть и это можно тайпчекать TS (чтоб не сломать код, опечавшись в строке), то рефакторить средствами IDE это всё равно нельзя, если вы захотите isActive переименовать, то все ваши строки будете переименовывать отдельно через text replace, а если захватите реплейсом что-нибудь лишнее — ну, удачи.
                                                                –4
                                                                В 2020 году вам уже стоит открыть для себя Object.keys(), Object.values(), и главное — Object.entries().

                                                                то есть скажем вот это
                                                                Object.values(users).filter(user => user.name === 'Alex' && user.age === 12)


                                                                удобнее чем
                                                                _.filter(users, {name: 'Alex', age: 12})
                                                                ?

                                                                  +7
                                                                  Конечно. Первое и читается однозначно прям там же, и тайпчекается гораздо проще, и отрефакторится тоже «само».
                                                                  –1

                                                                  Или вот что насчет _.mapValues?


                                                                  В случае Object.entries нам придется массив потом обратно в объект превращать, а я не уверен что для этого есть просто способ в js


                                                                  Или например такие функции как intersection, take, uniq (только не надо мне скидывать ужас с [...Set()] это на конкурс ребусов по разгадке древнегреческого), countBy, groupBy, partition, sampleSize, shuffle, curry, memoize, throttle, cloneDeep, meanBy, minBy (тут конечно есть около адекватный Math.min(...arr.map(it => it.something)))


                                                                  и много других функций.


                                                                  Это достаточно бессмысленный сахар, который только затрудняет чтение кода, экономя максимум десяток-другой букв

                                                                  Дело не в буквах, дело в том что код становится более легким для восприятия. Ну согласитесь что вот это user => user.name === 'Alex' && user.age === 12 сложнее чем {name: 'Alex', age: 12}


                                                                  А что делать если интересует ключей больше?
                                                                  user.name === 'Alex' && user.age === 12 && user.role === 'admin && user.signinDate === someDate
                                                                  против


                                                                  {name: Alex, age: 12, role: 'admin', signinDate: someDate}


                                                                  то рефакторить средствами IDE это всё равно нельзя, если вы захотите isActive переименовать

                                                                  Частично согласен, но разве IDE понимает что в стрелочных функциях свойства относятся к кому-то?


                                                                  Я имею в виду, будет ли работать рефакторинг в IDE если у нас есть две разные сущности с одинаковым свойством


                                                                  users = [{isActive: true}, ...]
                                                                  accounts = [{isActive: true},...]


                                                                  И где-то есть стрелочная функция которая работает с этим свойством
                                                                  users.map(user => user.isActive ...)
                                                                  accounts.map(account => account.isActive ...)


                                                                  То если мы изменим isActive только в юзере, но не к аккаунте, разве IDE сможет изменить стрелочную фунцкию user => user.isActive ?

                                                                    +3
                                                                    В случае Object.entries нам придется массив потом обратно в объект превращать, а я не уверен что для этого есть просто способ в js

                                                                    Object.fromEntries()

                                                                    Или например такие функции как intersection, take, uniq (только не надо мне скидывать ужас с [...Set()] это на конкурс ребусов по разгадке древнегреческого), countBy, groupBy, partition, sampleSize, shuffle, curry, memoize, throttle, cloneDeep, meanBy, minBy (тут конечно есть около адекватный Math.min(...arr.map(it => it.something)))

                                                                    Часть этих вещей как раз входит в оставшиеся 10%, а другая часть тривиально пишется в 1-2 строчки самостоятельно.

                                                                    Дело не в буквах, дело в том что код становится более легким для восприятия.

                                                                    const compare = (a: object, b: object): boolean =>
                                                                      Object.entries(b).every(entry => entry[1] === a[entry[0]]

                                                                    Надо ли мне ради такого кода влеплять lodash в зависимости, или его всё-таки можно самому написать, как вы считаете?

                                                                    Частично согласен, но разве IDE понимает что в стрелочных функциях свойства относятся к кому-то?

                                                                    В TS IDE будет рефакторить по типам TS — так что там, да, всё будет отрефакторено правильно. В JS — как повезет, но вообще современные IDE делают статанализ по JS крайне неплохо, и в прямолинейно написанных случаях скорее всего отрефакторят всё как надо.
                                                                      0
                                                                      Object.fromEntries()

                                                                      Спасибо, не знал.


                                                                      Но все равно, то что лодаш делает в один этап
                                                                      _.mapValues(users, user => user.age)


                                                                      без лодаша будет делатся в три этапа, из объекта в массив, мап, из массива в объект.


                                                                      Object.fromEntries(Object.entries(users).map((userKey, user) => [userKey, user.age]))


                                                                      согласитесь, это выглядит жутко.


                                                                      Надо ли мне ради такого кода влеплять lodash в зависимости, или его всё-таки можно самому написать, как вы считаете?

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


                                                                      Более того, представьте что вам нужно глубокое, а не поверхностное сравнение, что тогда?


                                                                      В TS IDE будет рефакторить по типам TS — так что там, да, всё будет отрефакторено правильно

                                                                      окей, в любом случае лодаш позволяет, но не заставляет использовать _.map(filter, 'string'). Кому-то это нравится, кому-то нет. Где-то это удобное, где-то нет.

                                                                        0
                                                                        Более того, представьте что вам нужно глубокое, а не поверхностное сравнение, что тогда?

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

                                                                        К слову, если вы ничего «такого» не держите в своих объектах, то глубокое сравнение (и глубокое клонирование) через JSON.stringify будет значительно быстрее лодаша. Если держите — зависит от того, что вы именно держите. На проксях и прочих очень нетривиальных вещах лодаш сфейлится.

                                                                        Я считаю что на любом проекте будет несколько таких «однострочников» которые лодаш заменяет лучше. Ради одного раза разумеется не стоит.

                                                                        В том-то и дело, что он их не «заменяет лучше», а буквально делает то же самое. Тащить себе сторонние пакеты уровня left-pad и is-promise — моветон, даже если это не один условный is-promise, а сразу 10. Всё равно оно остаётся тривиальным однострочником, 10 которых написать самому займет примерно столько же времени, сколько подключить лодаш и убедиться, что он три-шейкается.

                                                                        согласитесь, это выглядит жутко

                                                                        Ничего жуткого. Чуть длиннее — но это всё та же формальная конструкция-однострочник, которую можно всё так же вынести в функцию с красивым названием, если очень хочется.

                                                                        Проблема привлечения таких вот библиотек как раз в том, что вы потом так просто не скажете, где у вас из них взяты простые и очевидные однострочники, как тот же mapValues, а где вы уже залезли в длинную витиеватую логику с множеством граничных случаев, как то же клонирование объектов.
                                                                          0
                                                                          Но все равно, то что лодаш делает в один этап
                                                                          _.mapValues(users, user => user.age)

                                                                          Вы под «капот» заглядывали? Загляните — после этого перестанете так утверждать. Только в этом методе используется куча подвызовов
                                                              +2
                                                              Пожалуйста, ради любви ко всему, что представляет собой программирование, самостоятельно напишите проклятые базовые функции

                                                              Уважаемый Девид Хейни по-моему слишком много на себя берёт. Он говорит так, будто бы единственная проблема, которую решает установка сторонних модулей — это нежелание написать простой код. А помимо этого модули также решают как минимум проблему поддержки и повторного использования кода. Ну вот нужна мне функция isPromise. Причём нужна мне она в паре десятков проектов. Мне что, в каждом из них создавать, простигосподи, папку utils? И из проекта в проект копипастить эту одну строчку кода? Конечно нет. Да я как только поймаю себя на мысли о том, что я собираюсь создать папку utils, тут же либо полезу искать готовый npm модуль, либо, если не найду его, создам его сам и опубликую в npm, что бы во следующем проекте установить его же. Это нормальный эволюционный путь.
                                                              А Девид Хейни, вроде взрослый человек, ведущий инженер StackOverflow, а как ребёнок рассуждает.
                                                                0
                                                                Но ведь «создам и опубликую» — это примнрго оно и есть. Никто этот пакет просто так не удалит и не изменит, а если такое и произойдёт, то он будет ещё и в гите.
                                                                  +3
                                                                  Но зачем мне создавать и публиковать заново то, что уже сделал кто-то за меня?
                                                                  Если я так буду распыляться, то я проекты буду делать раза в два дольше, а если каждый так делать будет, то в NPM будут тысячи одинаковых библиотек от разных разработчиков.
                                                                  У NPM и так есть эта проблема, только в гораздо меньшем масштабе. Зачем раздувать её ещё сильнее?
                                                                  Всегда приходится чем-то жертвовать ради чего-то. В данном случае разработчики пожертвовали стабильностью ради того, что бы сэкономить 3-4 часа жизни, а то и все 8. И это только ради одной библиотеки is-promise. А вообще подобных мелких библиотек множество, и каждый день разработчики устанавливают разные мелкие библиотеки, экономя время до выхода на рынок. И то, что в одном из миллиона случаев это привело к краху, причём совершенно не критичному, не значит, что надо отказываться от этого подхода и срочно всем клепать по своей собственной библиотеке с блэкджеком.
                                                                  Прикол в том, что на деле-то ничего страшного не произошло. Да, масштабно, по всему миру прокатилось, но никаких последствий от этого не было. А раздули будто бы банковскую систему положили на несколько недель.

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

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