Pull to refresh

Comments 50

Лично мне кажется, что copy-paste кода библиотеки лучше работает, чем включение этой библиотеки через Composer. Может быть, я неправильно что-то считаю, но у меня (пример ситуации) выходит так, что использовать код StringHelper из Yii Framework напрямую (удалив из него код, зависимый от HTMLPurifier) выходит выгоднее по времени, чем использование композера. На удаление лишних методов и зависимостей из кода я потрачу один раз несколько минут, и код раз за разом будет нормально работать. Но если я буду что-то включать через Composer, то через какое-то время получится, что на операции обновления зависимостей через него у меня затрачивается больше времени — оно [время] просто накапливается. Сегодня ушло 20 секунд на то, чтобы Composer прочекал все зависимости, завтра ушло ещё 20 секунд. Послезавтра ещё 30 (в новой версии билиотеки, в методе, который я никогда не буду использовать, появилась зависимость от другой библиотеки, которая, конечно же, загрузится и потянет за собой ещё какие-нибудь зависимости). В какой-то момент получится, что я мог потратить с самого начала 2 минуты на copy-paste и удаление ненужного кода, но, в итоге, я потратил за 2 месяца 30 минут только на ожидание, пока композер всё прочекает и обновит. (В итоге, за всю карьеру накапливается куча бесполезно проведённого времени, которое можно было бы потратить гораздо веселее — например, сметнуться в Новую Зеландию за презиками.)

(Теги, конечно же, не читал, потому что их никто не читает)
Ну это пока пакет не обновляется а коллеги берут код с FTP, если же вы хотите обновляемый пакет, то математика работает в обратную сторону — 10 минут ручной работы против 20 секунд композера.
Про надёжность того что вы там на удаляли я вообще молчу — зачем перекладывать бремя тестирования пакета на себя? Да и пулл реквест сделать уже будет проще.
Кстати, если композер у вас долго работает — возможно его стоит обновить? У меня он весьма быстр.
Если у вас проблема с композером — обновляйтесь пока ходите за чаем.
если же вы хотите обновляемый пакет

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

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

зачем перекладывать бремя тестирования пакета на себя?

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

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

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

Ну это пока пакет не обновляется а коллеги берут код с FTP

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

Зачем тогда вам "операции обновления зависимостей" при использовании composer? Загружайте точные версии, не запускайте composer update, и всё.

При таком подходе, ИМХО, придётся следить за развитием билиотеки в обход композера, потому что иначе можно будет пропустить какое-то обновление безопасности (или просто фикс какой-то ошибки). Это кучу времени будет отнимать.

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

Лично для меня моя концепция лучше работает. Я её никому не навязываю. Она мне даёт больше плюсов, чем минусов.
придётся следить за развитием библиотеки в обход композера

А в чем разница с вашим подходом?

В том, что я не слежу за развитием библиотеки и вытаскиваю из неё, скажем, 5% кода, который мне реально потребуется, а остальное выкидываю из кодовой базы. А то, что осталось, я дополнительно тестирую самостоятельно. И если вижу ошибки, то я их могу мгновенно исправить, а не отправлять в цепочку: «создать issue на github» → «дождаться фикса» → «загрузить себе через композер», сидя на некорректно работающей библиотеке всё время, пока эта цепочка не завершится.

Но ведь и в вашем подходе можно пропустить какое-то обновление безопасности.


И если вижу ошибки, то я их могу мгновенно исправить

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


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

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

Я, в общем-то, особенно тоже не убеждаю. Просто я вижу минусы и их озвучиваю. Кто-то видит минусы в моём подходе и тоже их озвучивает. Лично меня такие диалоги хорошо развивают и дают возможность переосмысливать собственные подходы к разработке.

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

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

Конечно, можно отнаследоваться, но может получиться так, что, например, в классе, от которого я наследуюсь, статичный метод, который я хочу заменить, вызывается как self::method(), а не как static::method(), и в результате вызовы пойдут мимо метода, который я пропатчил в дочернем классе. Придётся тогда и те куски кода тоже в дочернем классе переопределять. А это приведёт к тому, что я больше не буду вызывать часть оригинального кода. Если в эти части придут какие-то изменения, я ими не буду пользоваться, а они могут быть полезными, либо могут как-то значительно влиять на полезность других методов, которые я использую. Мне в таком случае придётся дополнительно следить за кодом, который приходит при обновлениях, чтобы соответственно реагировать в моём дочернем классе. Это снижает ценность подключения и обновления библиотеки в автоматическом режиме — тратится больше времени.
Если в эти части придут какие-то изменения, я ими не буду пользоваться, а они могут быть полезными, либо могут как-то значительно влиять на полезность других методов, которые я использую. Мне в таком случае придётся дополнительно следить за кодом, который приходит при обновлениях

Зачем? Вот есть код, вы его скопировали и не обновляете. Вот есть тот же код, вы зафиксировали нужную версию в управлении пакетами и тоже не обновляете. Разницы нет, но во втором случае есть возможность обновить и проверить код автоматически, а в первом нет. Запустили тесты, все работает значит все ок, следить не надо. Тесты упали, значит либо откатили либо поправили пару мест, следить тоже не надо. А много мест быть не должно, потому что semver.

Я на примере кода поясню, что я имел ввиду, потому что мне кажется, что вы уже немного о другом говорите.

Пример кода
class ComposerLibDependency {
    public static function usefulMethod($a, $b) {
        return floor($a * $b);
    }
}

class ComposerLib extends ComposerLibDependency {
    public static function useMoreA($a, $b) {
        return self::usefulMethod($a * 3, $b);
    }

    public static function useMoreB($a, $b) {
        return self::usefulMethod($a, $b * 3);
    }
}

class MyLibPatch extends ComposerLib {
    public static function usefulMethod($a, $b) {
        if (!is_numeric($a) || !is_numeric($b)) {
            throw new Exception('Incorrect input');
        }

        return intval(parent::usefulMethod($a, $b));
    }
}

var_dump(MyLibPatch::useMoreA(2, 3.5));
var_dump(MyLibPatch::useMoreB(2, 3.5));



Метод MyLibPatch::usefulMethod() здесь в итоге не используется. Чтобы этот мой фикс использовать, нужно заодно переопределить методы useMoreA и useMoreB, по сути, заменяя в них только self на static. Если потом в них придут изменения из ComposerLib, они не отразятся, потому что я эти методы переопределил. Получается, мне нужно будет мониторить внешние зависимости, чтобы в эти переопределённые методы своевременно вносить изменения. Или, даже, если не вносить, то просто удостоверяться, что такое переопределение ничего не ломает в новых версиях билиотеки из композера.

Это просто к вопросу об отнаследовании.
Если потом в них придут изменения из ComposerLib

Откуда там появятся изменения? Ни в скопированном коде, ни в фиксированной версии никаких изменений нет.

Я вас же процитирую:

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

Ключевые слова здесь: «а с возможностью обновления ошибки будут исправлены вообще без вашего участия».

Вы ничего про фиксированную версию не говорили в тот момент. Вы говорили про то, что подразумевается возможность обновления. То есть, вы советовали унаследоваться и переопределить метод, а потом спокойно дожидаться, пока придёт обновление. Я просто показываю минусы такого подхода.
Нет. Я предлагал не копировать код в репозиторий, а просто зафиксировать версию, потому что в результате будет то же самое, но без ручной работы. «Возможность» обновления на то и возможность, захотели — сделали. Раз в полгода решили выделить время, обновили и проверили, получили исправления ошибок и безопасности, убрали свои исправления. Постоянно следить за новым сторонним кодом не нужно. И с форком то же самое, только без наследования.
Ясно. Я, в основном всё-таки про отнаследование и переопределение метода говорил и про то, что одним переопределением метода это может не ограничиться, и поддержка этого кода в соответствие с кодом оригинальной библиотеки может требовать больше времени и внимания, чем кажется на первый взгляд.
UFO just landed and posted this here

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


Ну и при подходе с композером никто не заставляет ждать цепочку "issue — fix — tag", всегда можете форкнуть и использовать форк до исправления ошибки.

Я в моём подходе бремя тестирования беру на себя. Некоторое время назад меня впечатлила книга Г.Майерса «Надёжность программного обеспечения», особенно идея, что тесты нужны для поиска ошибок, а не для того, чтобы просто покрывать ими код или использовать их в качестве двигателя функционала (когда используется TDD). Так что, мне удобнее брать минимально возможное количества кода со стороны, чтобы потом приходилось меньше писать тестов, которые его пытаются сломать.

Ну и при подходе с композером никто не заставляет ждать цепочку «issue — fix — tag», всегда можете форкнуть и использовать форк до исправления ошибки.

Минус в том, что форк придётся тоже поддерживать в актуальном состоянии. Нужно будет принимать из оригинала обновления. Иногда эти обновления будут затрагивать лично ваши изменения, не исправляя при этом ошибок, ради которых форк создавался, и придётся делать сложные мёржи.
UFO just landed and posted this here
Критикал — ни разу в этом году. Из просто ошибок, которыми могу поделиться: этот и этот (плюс пулл-реквест, который приняли) репорты в Yii Framework.

Из того, всеми деталями чего не могу поделиться: фиксы в cordova-plugin-file-transfer, cordova-plugin-statusbar, react-dom.js (планирую попробовать отправить репорт и фикс, когда появится время, про ReactDOM.render, который возвращает не то, что должен, если вызывается внутри коллбека setState — нужно писать синтетический код для демонстрации — тот, который в проекте используется, показывать не могу), react-select.js (вообще выкинул после пары правок — проще и быстрее оказалось свою обёртку для <select> написать, чем натыкаться на постоянные грабли), sprintf.js (понимает %e, но не понимает %E + ещё несколько моментов, когда она работает не так, как в PHP).

Просто по поводу криткал-багов у меня такой подход, что проще бывает вообще не использовать библиотеку, чем заниматься правками критикал багов в ней. Поэтому я их не фиксю в других библиотеках и не могу похвалиться подобными правками. Мне хватило одного раза, когда я несколько лет назад работал с ics-parser на его начальных этапах жизни — правда, то, что я фиксил тогда, наружу никуда не выходило. Пишу свою библиотеку на эту тему, но она два года никуда не двигается, и я, наверное, не буду её писать всё-таки.
Сегодня ушло 20 секунд на то, чтобы Composer прочекал все зависимости, завтра ушло ещё 20 секунд.

А зачем вы вообще запускаете composer update, если вас устраивает версия, которая указана в composer.lock?

У меня иногда бывали моменты, когда в начале проекта корневые неймспейсы часто появляются/удаляются, пока не устаканится какая-то определённая структура. Они, в общем-то, в composer.json прописываются, и чтобы autoload.php и прочие сопутствующие загрузчики перегенерировались, нужно запускать composer update

Не нужно. Достаточно запустить composer dump-autoload.

Мне кажется что если уж сильно хочется использовать какую-то сильно изменённую под свои нужды библиотеку, то лучше оформлять её в отдельный репозиторий (хоть на своём сервере, хоть на гитхаб, без разницы) и ставить тем-же композером. Сильно поддержку упрощает, особенно если она используется в нескольких проектах, достаточно исправить ошибку в одном месте.
Столкнулся давеча с таким подходом в процессе перевода приложения с 5 на 7. Не понравилось.

Я видимо что-то не понимаю.


Очевидно же, что в библиотеке require желательно оставить пустым, а в require-dev пишем, что хотим, так как эти пакеты используются только для разработки и тестирования и к клиенту они не попадут.


В проекте же, в require, можно вообще любые зависимости держать. За них отвечает команда разработчиков.


Проблема совмещения не совместимых пакетов мне вообще не понятна.
Зачем пытаться совместить несовместимое?
Если пакеты не совместимы, то просто не используйте их или выберете совместимую версию. В крайнем случае, всегда можно выбрать конкретный коммит в пакете. Да, выбрать конкретную версию зависимости у зависимости тоже можно.


А если хочется использовать фичу из последней версии пакета, которая не совместима с другими пакетами, то просто не используйте эту фичу. Подождите пока исправят совместить или сделайте PR.


Не стоит гнаться за последними релизами.


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


Решение vendor in vendor мне видится странным и наверняка добавит проблем.

Не всегда всё так просто. Например, всё отлично работает на PHP5.6, приходит задача перевести всё на PHP7.1 по требованию аудитора/регулятора (альтернатива — закрыть бизнес) и выясняется, что две зависимости, которые отлично работали вместе под 5.6, несовместимы теми версиями с 7.1, а новые совместимые с 7.1 версии не работают вместе. Ну и PR могут не принимать месяцами или закрыть его вовсе с wantfix

UFO just landed and posted this here

Вариант в целом, более того лично так делал. Но не всегда легко взять и пофиксить, даже если в новой версии проблемы нет. очень сильно вникать в код библиотеки может понадобиться, особенно если это не лефтпад какой-нибудь, а ОРМ, например, или поддержка какого-то зубодробительного формата типа xls

UFO just landed and posted this here

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

Я сейчас перевожу один проект на PHP 7. Одн из пакетов оказался не совместим с PHP 7.
Так как проект заброшен, я просто взял и заменил его на другой. Переписал пару адаптеров и все.
Ну ещё немножко смазал маслицем чтоб лучше зашёл

Очевидно же, что в библиотеке require желательно оставить пустым, а в require-dev пишем, что хотим, так как эти пакеты используются только для разработки и тестирования и к клиенту они не попадут.


я правильно понимаю, что вы заставляете пользователя искать транзитивные зависимости вручную? ну типа у вас есть какой-нибудь api клиент с использованием guzzle, а guzzle нужной версии вы заставляете людей ставить?

А если ваша библиотека написана с использованием nullable, а у человека 7.0, он тоже об этом только опытным путем узнает?
UFO just landed and posted this here
в таком случае
1. у вас в src нигде не будет зависимости от guzzle, только от psr
2. в require у вас будет какой нибудь psr/http-message все равно, т.к он требуется для вашей библиотеки
C газлом возможно неудачный пример, надо было брать какой-нибудь соап клиент, на которых нормальных стандартов не понаписали, а наследоваться от \SoapClient все не хотят
UFO just landed and posted this here

Обычно я так и делаю. Один пакет чистый без зависимостей, а второй bundle для Symfony с конфигами и всеми делами.
Хочешь юзать в Symfony ставь бандл. Если у тебя не Symfony, юзай чистый пакет.

саджест все равно в таких случаях некорректен. саджест — это предложение, а в вашем случае без установки такого «предложения» пакет не будет работать. в таких случаях делают по-другому:

реквестят (мета)-пакет «some-vendor/my-super-interface-implementation»
этот пакет не зарегистрирован в packagist, но любой другой пакет (например из вашего списка саджестов) может прописать у себя provides some-vendor/my-super-interface-implementation

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

Вот например так работают с тем же PSR-3 логгером

packagist.org/providers/psr/log-implementation
Использую composer совместно со своим gitlab сервером, все legacy-библиотеки перевёл в отдельные репозитории, на остальные сделал форки, опять же в своём gitlab сервере.
А какой смысл в тотальных форках пакетов? Если вы боитесь, что репы пропадут, то проще использовать кэширующий прокси для пакетов

Прокси дело такое, сегодня он есть, а завтра его кто-то снёс, не поняв, что его основное назначение не ускорение доступа и т. п., а постоянное хранение. Или сам прокси оставят, а вот кэш почистят, когда место на диске кончится. Кэш же, не хранилище, прогреется со временем.

А форки также случайно не почистят? подумав что это ненужная фигня — вот же оригинальный обновляемый проект в публичном доступе.

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

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

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

Ну вот это примерно тот подход, который практиковали и мы. Обычно форк предварялся игрой «найди более поддерживаемый форк»

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

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

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

В самом крайнем случае код довольно легко извлечь с любого из стендов и уже в момент потери положить в репку, которую подключить в satis\toran\etc.

Мне все это кажется менее геморным, чем постоянно поддерживать несколько сотен форков, все таки пакеты не каждый день пропадают

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

Sign up to leave a comment.