От каждого по способностям, каждому по потребностям. — Карл Маркс, «Критика Готской программы»
После прошлой статьи про Klipper самым частым возражением было не «вы неправы». Самым частым было: «Ну и чо такого? Форкнул, сделал патч, пользуешься». Причём писали это люди, которые сами так живут, — и которых, судя по всему, совершенно не беспокоит, что их труд утонет в миллионах форков и никогда никому не пригодится.
Так что такого? Чтобы ответить, придётся сначала договориться, что вообще значит «открытый».
Публичный — не значит открытый
Откройте определение Open Source от OSI — там десять критериев. Откройте четыре свободы FSF. Все четырнадцать пунктов — о том, что гарантируется вам: читать, менять, распространять. Ни один — о том, что проект обязан принять ваш патч. Оба канона описывают артефакт и молчат про процесс.
Кто-то это понимает и говорит честно. У SQLite на странице о лицензии так и написано: Open-Source, not Open-Contribution. Код открыт, патчи не принимаются, спасибо, до свидания. Valetudo из прошлой статьи — та же честность другими словами. Контракт объявлен, вопросов нет.
Но в бытовом смысле «опенсорс» склеивает две разные вещи. Первая — право читать и форкать, и оно гарантировано лицензией. Вторая — возможность занести обратно, и лицензией она не гарантирована вообще. Первое — это публичность кода. Второе — его открытость. И Эрик Реймонд ещё в «Соборе и базаре» описал ровно эту разницу: собор с опубликованными чертежами не становится базаром.
Публичность кода не делает его открытым. Открытым код должен быть не только на чтение, но и на запись. Иначе патчи растворяются в бесконечных форках — немаркированных, несовместимых, не прошедших никакой верификации — и гибнут поодиночке при первом же ребейзе. Именно это «форкни и пользуйся» предлагает как норму.
Пока это абстракция. Посмотрим, как выглядит один «просто патч» на самом деле.
Как выглядит «просто патч»
Прямо сейчас я заношу в OpenWrt роутер Keenetic KN-1012 (здесь и далее — он же Netcraze NC-1012 после ребрендинга, одна железка независимо от шильдика).
Взялся, потому что железка того стоит: крепкая, современная, недорогая и с SFP-портом. В SFP-клетку встаёт провайдерский модуль, и роутер заменяет собой GPON-терминал от оператора. Пользователей GPON, которым это нужно, огромное количество — в РФ, Грузии, Армении и много где ещё. Драйверы почти везде понятные, я честно рассчитывал на быструю работу.
Проблема оказалась не в драйверах, а в том, как их склеить между собой. У роутера особенная топология: комбо-порт SFP/Ethernet, и переключение между ними надо делать ещё в U-Boot, формируя под выбранный режим соответствующий DTS. Это уже не «добавить device tree по образцу», это работа на трёх уровнях сразу.
Но главное даже не сложность. Главное, что за словами «поднять борду» скрывалась не одна задача, а семь. Сама борда поднимается отдельно — настолько, насколько это вообще возможно. А то, что сетевая подсистема ядра в глаза не видела железо за свитчом, — это отдельная проблема ядра, к борде она отношения не имеет. То, что на кинетиках вообще случаются коллизии MAC-адресов, — отдельная. EEE на свитче — отдельная. Опечатка в ширине SPI-шины, которой четыре года, — отдельная. Каждый из этих треков заслуживает жизни сам по себе: он чинит что-то не только для моей железки и потому идёт в апстрим самостоятельным PR или серией. Общего зонтика у них нет и быть не может. Но по-настоящему рабочий девайс получается только тогда, когда все они сходятся воедино.
Если вам интересно технопорно — добро пожаловать под спойлер. Если нет — достаточно запомнить число: семь.
Как «порт на вечер» стал патчсетом в ядро
Порт KN-1012 не начинался с нуля. К августу у меня уже был свой первый порт — Keenetic Buddy 6, #23727, четыре устройства на одном dtsi (KN-3411, KN-4410 и их Netcraze-близнецы), — и свой драйвер SPI-NAND HeYangTek, принятый в linux-mtd. На них я наработал всю механику: формат прошивки zyimage, TFTP-recovery без консоли, NMBM. То есть я не прыгал в неизвестность — я трезво оценивал, на что иду, потому что один раз уже прошёл этот путь. Buddy 6 при этом до сих пор ждёт ревью с июня — но это уже про первую статью.
Отсюда оценка на старте, девятое августа: «тот же mt7981, тот же NMBM, тот же TFTP-recovery, единственная настоящая неизвестная — какой 2.5G-PHY стоит на комбо-порту и как заведён SFP-кейдж. Станет ясно, порт это на вечер или на месяц». Стало ясно. На месяц с лишним.
Топология. Два часа над вендорским DTS — и картина ясна: GMAC0 → свитч MT7531, а пятый порт свитча — один SerDes, который через мукс на GPIO 27 уходит либо в SFP-кейдж, либо в Airoha EN8811H и дальше в медный 2.5G RJ45. Статически развести нельзя — SerDes общий. Прецеденты в дереве: TP-Link FR365 с тем же тандемом MT7981+MT7531, и Turris Omnia, в чьём мейнлайновом DTS так и написано: «until kernel supports this configuration properly, U-Boot has to enable the sfp node». Выбор среды на каждой загрузке делает загрузчик, потому что runtime-переключения у phylink/DSA нет. Отсюда chainloaded U-Boot: вендорский bootm грузит мой U-Boot вместо ядра, тот читает mod-def0 и выбирает конфиг из dual-FIT — медь или SFP.
Форк-предшественник. В тот же день нашлась ветка @Linaro — он начал порт KN-1012 ещё в феврале. DTS, светодиоды, рецепт образа — всё это взято оттуда, с атрибуцией. И два патча ядра, один на 370 строк, который подменял phylink у живого DSA-порта по поллингу mod-def0 — там, где фреймворк ничего не гарантирует. Последние коммиты ветки назывались try to fix sfp/lan4 и test lan4. Финал — SFP-only, медный phy-handle закомментирован. Плюс модель NAND в его DTS байт-в-байт совпадала с соседней платой и не совпадала с реальным чипом — копипаста. Это и есть тот самый «рандомный форк с половиной работы» из середины статьи. Разница только в том, что я знал, чего в нём не хватает, — и то потому, что автор сам объяснил в чате.
Чейнлодер. Первая прошивка — цикл перезагрузок, консоли нет. UART я паял дважды: кочергой вместо паяльника и смолой, которую я надоил с сосны вместо канифоли, — это не шутка, — с Flipper вместо терминала. Активности нет ни на одном пине, а третью попытку я делать не стал: этой кочергой лучше не запаять, а плату добить можно. Отлаживал по светодиоду: 68-байтные «маяки» на ARM64, мигающие GPIO. Выяснилось, что вендорский bootm требует ARM64 Linux Image header. Потом — что при минимизации defconfig выпал CONFIG_TIMER: get_timer() не движется, и любой цикл «крутись до таймаута» бесконечен. Один корень для трёх «разных» зависаний — чтение NAND, опрос свитча по MDIO, tftpboot.
Медь. Plain-образ загрузился, но lan4 не появился: DSA привязывает PHY на probe свитча, через две секунды после старта, а драйвер EN8811H — модуль и грузится позже. Порт валидируется с generic-PHY, а тот не умеет 2500base-T: -EINVAL, порт помечен unused. Встроить драйвер в ядро не спасает: он грузит прошивку в probe, а /lib/firmware ещё не смонтирован. Во всём дереве OpenWrt EN8811H на порту DSA-свитча не стоит нигде — все держат его на GMAC, где привязка ленивая. Никто так не делал. Шесть версий DSA-патча за одно утро, включая ту, после которой плата не вернулась и я доставал её через recovery кнопкой. Шестая — подключать PHY на ifup, когда модуль уже в памяти, — заработала. Линк не поднялся, пока я не вмешался: полярность GPIO 27, аналитически выведенная из четырёх источников, на железе оказалась обратной.
Потом медь умерла. Линк есть, трафика нет, битые кадры. Сток показал, что железо живое — значит, баг мой. Фикс airoha,pnswap-rx — перепутанная полярность RX у SerDes-пары — я попробовал и «опроверг» за двенадцать секунд. При том, что первый линк на этом PHY поднимается 55 секунд. Археология по логам сборок нашла, что единственный образ, гнавший 938 Мбит/с, был не чистым: в нём сидел тот самый pnswap. Одна строка в DTS, iperf 2.34 Гбит/с, три ребута из трёх. Вот что значит «патч лёг, а функционал не гарантирован, и проверять — нужна экспертиза».
Ядро. DSA-патч «подключать PHY на ifup» ушёл в netdev серией из трёх — с двумя попутными фиксами, найденными по дороге. Andrew Lunn ответил на следующий день: «this is not guaranteed to work… think about the case of NFS root». Тем же вечером — «moving the binding of MAC to PHY into open is wrong… I need to think on it for a while». А ещё через день прислал архитектуру целиком и попросил найти в ней дыры: до загрузки прошивки чип — не PHY, а микроконтроллер в бутлодере, и описывать его надо отдельным MDIO-устройством, которое грузит прошивку и публикует PHY на вложенной шине, а phylink получает свойство slow-to-probe и ждёт. От его «have a think» до моего «I built it» прошло сорок часов: два варианта реализации, матрица тестов, аттач PHY на 7.0358 секунды во всех бутах с разбросом 47 микросекунд — это тик поллера, а не гонка. Ещё два письма — и 29 августа ушёл RFC из девяти патчей. Мой первоначальный подход в ядро не пошёл — и правильно: он ломал NFS-root. В OpenWrt он временно живёт как локальный carry — до тех пор, пока архитектурный вариант не доедет до мейнлайна.
Два попутных фикса стали отдельной серией в net. Первый: phylink запоминает указатель на PHY до последнего шага, который может провалиться, и при провале второй disconnect отцепляет PHY повторно и отпускает уже отпущенные ссылки. Второй: PHY, прошедший цикл привязки к generic-драйверу, навсегда остаётся в поллинге, и настоящий драйвер никогда не видит прерывание из device tree. Paolo Abeni попросил Fixes-тег — археология привела к коммиту рождения phylib: обе половины бага там с первого дня. На первый патч Lunn поставил Reviewed-by.
Третья серия — EEE. Драйверы mt7530 и mtk_eth_soc заполняют lpi_capabilities, но не lpi_interfaces, а phylink считает MAC умеющим EEE только при обоих. Итог: EEE выключен на каждом порту каждого mt753x и каждого MAC mtk_eth_soc с момента конверсии драйверов, и из юзерспейса не включается — phylink идёт по другой ветке и вызывает phy_disable_eee. Daniel Golle поймал в обосновании фактическую ошибку про 2.5G, а Paolo — попросил ограничить фикс подтверждёнными ревизиями SoC. Подтверждать пришлось самому: MMD-регистр 3.1 бит 11, Tx LPI indication, читаемый 30-строчной утилитой через SIOCGMIIREG, потому что на плате нет ни devmem, ни python. Тридцать потоков по триста пакетов под взведённым LPI — 9000 из 9000.
Что в итоге не про мою плату. Первая серия — для любого чипа, которому нужна прошивка, чтобы стать PHY. Вторая — для любого PHY с драйвером-модулем на несмонтированном rootfs. Третья — для всех MediaTek-свитчей и SoC-MAC-ов разом. Плата их просто вскрыла.
В OpenWrt это две платы — шесть устройств, если считать все шильдики, — и пять отдельных проблем: Buddy 6 в #23727, KN-1012 в #24820, #24623 про генерацию MAC-адресов, #24819 с бэкпортами phylink/phylib и carry DSA-патча, #24863 с бэкпортами EEE, #24809 с опечаткой в ширине SPI-шины. Про опечатку стоит рассказать отдельно: ей четыре года и четыре месяца. Она приехала в дерево в апреле 2022-го вместе с бринг-апом mt7986 — от инженера самой MediaTek, из вендорского референсного DTS, сразу в двенадцати экземплярах. Все последующие платы честно копипастили её из референса, и все эти годы устройства молча жили на однобитной SPI-шине вместо четырёхбитной. Никто не заметил, потому что «медленно, но работает» не выглядит как поломка. Копипаста референса — самый эффективный вектор доставки опечатки в дерево, и один фикс в апстриме закрывает её на всех платах разом: на Teltonika RUTC50 чтение 64 МиБ ускорилось с 13.5 до 5.7 секунды. Рядом — патч в firmware-utils: в форке утилиты zyimage потерялась конвертация порядка байт, и тридцать устройств в дереве заточены под неправильный порядок. Плюс патч в U-Boot с поддержкой SPI-NAND FM25G01B и FM25G02B — потому что чип, который стоит в моём экземпляре, Linux знал, а U-Boot нет. Серии в netdev: RFC из девяти патчей, phylink и phylib, EEE. Было ещё несколько — старые дизайны и отвергнутые версии, их не считаем.
Это больно, интересно и полезно. И я мог бы просто остановиться на собственном форке — как мне и советуют.
Что получит тот, кто «просто форкнет»
Что тогда получил бы человек, который этот форк найдёт? Посмотрите на список выше. Это не один патч. Это семь самостоятельных треков, и вот что с ними не так: без соседей каждый из них как-то работает. Основной PR — и железка загружается. Без фикса генерации MAC — работает, пока виртуальный интерфейс не получит тот же адрес, что и физический, и трафик не начнёт ходить не туда. Без фиксов PHY — работает, пока не начнёт флапать линк на комбо-порте. Без бэкпорта DSA — работает, но не так, как должна. А опечатка в ширине SPI-шины вообще не проявляется ничем, кроме того, что обращения к флеш-памяти вчетверо медленнее, чем могли бы быть.
Теперь представьте человека, который через год находит мой форк. Скорее всего, он найдёт основной PR — он самый заметный. Соберёт прошивку. Получит железку, у которой не работает половина того, ради чего всё затевалось, — и не будет понимать почему. Честно признаюсь: в моём основном PR стоят кросс-ссылки на остальные шесть треков. Но они там потому, что мне было выгодно повысить видимость, и адресованы они ревьюерам, а не тому, кто через год ищет прошивку. Это мой выбор, а не норма. В общем случае никаких ссылок нет: борду поднимает один человек, баг в OpenWrt чинит другой, подсистему ядра правит третий, и они не знают ни друг о друге, ни о том, что их работы связаны. Обозначить семь треков в трёх проектах как единый набор негде — разве что в личном блоге или в рецепте на 4PDA. Треки встречаются только в мейнлайне. И там же выясняется, каких проблем удалось избежать — точнее, не выясняется: они просто сходятся в релизе, и никто никогда не узнаёт, что без одного из них половина устройств не работала бы. Вот этого форк не может дать в принципе.
Допустим, наш человек всё-таки нашёл рецепт и собрал набор вручную. Дальше — конфликты между патчами, которые писались в разное время под разные ветки, плюс три серии в ядре и патч в U-Boot из других репозиториев. Но конфликты — не самое дорогое. Патч, который чисто лёг на ядро через две, три, четыре версии после того, как был написан, не обязан работать: git проверяет текст, а не поведение. Подсистема под ним могла поменять семантику, и патч будет молча делать не то. Значит, после каждого ребейза — ещё пара часов тестов. И то при условии, что есть экспертиза понять, что именно тестировать: я 55-секундный линк «опроверг» за двенадцать секунд, а уж я в этом железе сидел месяц. Даже если бы я вёл всё это одной веткой без цели попасть в мейнлайн, каждый релиз стоил бы мне часов ребейза и нескольких итераций тестов. Человеку со стороны — в разы дороже.
Вот что означает «форкнул и пользуешься» на практике: пользуешься ты. Один. Пока не надоест ребейзить. Ядро не получает багфиксы в DSA. OpenWrt не получает железку. Люди с провайдерским терминалом не получают ничего. Дни моего труда, если утрировать, не делают мир лучше — они делают лучше мою домашнюю сеть, и на этом всё.
«По потребностям» без «по способностям»
Но для самого форкера это ведь всё равно рационально? Он получил свою железку за вечер, а не за месяц переписки. И он прав — для себя. Патч за вечер дешевле недели ревью, где тебя спросят за дизайн, за стиль, за тесты и за то, почему ты сделал так, а не иначе. Локально всё сходится.
Ошибка не в индивидуальном расчёте, а в горизонте. Патч в форке стоит не вечер, а вечер, умноженный на количество релизов, которые тебе придётся пережить, — потому что ребейз не бесплатен, а конфликты копятся. Патч в апстриме стоит неделю ревью один раз — а дальше его ребейзят, чинят и переносят на новые ветки чужие руки. Обычно уже на втором-третьем релизе форк дороже. А если то, что ты пишешь, уже кем-то написано и лежит в рандомном форке — ты вообще платишь дважды: за то, чтобы переизобрести, и за то, чтобы это потом тащить. Ревью в мейнлайне больно, но это одноразовая боль. И боль эта — не ритуал, а сигнал. Если ваш код действительно хорош, лишён багов и сделан по правилам подсистемы, ревью пройдёт быстро и почти не заденет. Больно становится ровно там, где код плох — и в форке вы об этом тоже узнаете, только позже и дороже: не от ревьюера в письме, а от железа, которое повисло, потеряло данные или выпустило волшебный дым. Ревью — не налог на вход, а дополнительный гард, который в форке просто не предусмотрен. Форк — это подписка.
Не верите мне — поверьте OpenWrt. Это огромный проект с сотнями контрибьюторов, и он сам по отношению к ядру — даунстрим. Так вот: OpenWrt не любит таскать патчи. В общем случае, когда ты приносишь им фикс для ядра, тебя просят хотя бы попытаться отнести его в апстрим. Это не вежливая просьба — это структура репозитория. В target/linux/generic три каталога: backport — уже принято в ядро и бэкпортировано, pending — отправлено в ядро и ждёт, hack — то, что в апстрим не занести, и его держат минимальным. Чтобы твой патч попал в pending, он должен быть отправлен наверх. При каждом бампе ядра патчи, доехавшие до стабильной ветки, выкидываются — в коммитах так и пишут: «removed, upstreamed». Проект, у которого рук больше, чем у любого из нас, посчитал и решил: таскать патчи слишком дорого, даже для него. Именно поэтому мои фиксы в phylink и EEE лежат в OpenWrt как бэкпорты и pending — а не как ещё один слой патчей, который пришлось бы ребейзить вечно.
Если это не по карману OpenWrt, то одиночке с форком — тем более.
Но есть и уровень выше личного. Вклад в апстрим — общественное благо: оплатил один, пользуются все. Форк — благо частное: оплатил один, пользуется один. Каждый рационально выбирает частное, и в сумме получается россыпь несовместимых непроверенных патчей, из которой никто ничего не может достать. Это трагедия общин наоборот: никто не выедает общее пастбище — просто никто на нём не сеет.
Вот здесь и стоит вернуться к эпиграфу. «Форкни и пользуйся» — это половина формулы. Каждому по потребностям — да, бери, лицензия разрешает. А от каждого по способностям — нет, зачем, мне и в форке хорошо. Это не коммунизм. Это его половина, и именно та, которая без первой не работает.
Только вот «общественное благо» — плохая мотивация для хакера с паяльником. Он не партийный работник.
А что с этого лично вам?
Больше, чем кажется. И я хочу это сказать как человек, который в этом году впервые пошёл с патчами в ядро Linux.
Я нарушил кучу правил. Не специально — у них просто принято иначе, чем везде, и этикет там не считывается с первого взгляда. Я писал слишком длинные ответы в тредах. Я нарушал netdev-правило двадцати четырёх часов — не слать новую версию серии раньше, чем через сутки. И так далее по списку. Никто не послал меня читать документацию. Меня подхватили, направили и потратили — реально потратили — часы своего времени на то, чтобы дизайн моих патчей стал правильным. Не «примите как есть», а «вот как это надо делать, и вот почему». Лучший пример — судьба моего DSA-патча. Он работал: плата поднималась, порт жил. Andrew Lunn, мейнтейнер PHY-подсистемы, ответил, что подход неверен в принципе — он ломает NFS-root, — и написал «мне надо подумать». Через два дня он прислал полную архитектуру решения и попросил найти в ней дыры. Не отфутболил, а спроектировал за меня то, что я спроектировать не мог, — потому что не знал подсистему так, как знает он. Через сорок часов я ответил «I built it», а ещё через четыре дня в netdev ушёл RFC из девяти патчей по его дизайну. Это не ревью. Это школа. Сеньорское ревью такого уровня в коммерческой разработке стоит очень дорого, а здесь его дают бесплатно любому, кто принёс работающий код и готов слушать. Кстати, по его же замечаниям commit messages переписывались от версии к версии: «оставь в сообщении только “почему”, механику расскажет дифф».
Второе — бесплатный мейнтейн. После мержа ваш код перестаёт быть вашей проблемой. Когда подсистему рефакторят, ваш драйвер правит тот, кто рефакторит. Когда меняется API — переносят. Драйвер NAND, который я занёс в ядро в июне, будет жить там дольше, чем я буду помнить о его существовании, — и чинить его буду не я.
Третье — репутация, и она не абстрактная. Тот самый фикс опечатки в ширине SPI-шины задевал двенадцать плат, и большинства из них у меня нет. Я попросил в OpenWrt-чате помощи с тестированием — и на следующий день со мной связался Routerich, вендор, чьи роутеры были в списке, и предложил отблагодарить железом: два роутера. Не за поддержку его устройств — я их никогда в руках не держал, — а за фикс общей опечатки, который задел и его. Репутация работает и в другую сторону — когда ты оступаешься. Недавно я нарушил правило в репозитории Gateway API. Мейнтейнер не отшил меня и не отправил читать CONTRIBUTING — он написал: «делай так же, как ты делаешь это в Cilium». Я активен в обоих проектах, и к моменту ошибки у меня уже была репутация. Ошибку простили не потому, что она мелкая, а потому что за ней стояла история полезных контрибьюций. Люди, которые приходят ко мне с PR и ишью в мои личные проекты, всё чаще пишут что-то вроде «спасибо за работу над Cozystack, слежу за тобой». Апстрим делает тебя видимым, а видимость приводит к тебе и контрибьюторов, и вендоров. Репутация в опенсорсе — валюта, и зарабатывается она только на общественном поле. В форке тебя никто не видит.
И четвёртое, самое незаметное: ваша железка переживёт ваш интерес к ней. Форк умирает, когда вам надоедает. Апстрим — нет.
Всё это работает при одном условии: на другой стороне тоже играют — проект отвечает на то, что ему приносят.
А если проект не отвечает?
Всё сказанное выше — обязанности того, кто приносит. Теперь про того, кто принимает. Потому что в эту игру играют вдвоём, и если вторая сторона не играет — первая тоже теряет смысл.
«Никто никому ничего не должен» — вечный аргумент, и для SQLite он верен: они ничего не обещали и ничего не берут. Но если проект собирает звёзды, берёт спонсорские деньги и зовёт людей контрибьютить — свою половину сделки он уже получил. Отвечать на входящее — пропозалы, ишью, PR — это вторая половина. Не альтруизм, а обязательство по сделке. Что именно брать — то, что соответствует миссии, нужно большинству или просто нравится мейнтейнеру, — его право. Молчать годами — нет.
В марте 2024-го я открыл PR в официальный helm-чарт Cloudflare — cloudflare/helm-charts#69, опция отключить дефолтную 404-ю. Два с половиной года. Ноль ответов от мейнтейнеров. И не потому, что репозиторий мёртв: в сентябре 2024-го они смержили чужой #74 — заходили, видели очередь и прошли мимо. После этого — два года без единого содержательного коммита. Зато в треде моего PR собрались люди с той же болью, и последний комментарий там — чужой человек постит ссылку на мой форк как решение. Так я стал мейнтейнером собственного чарта, который люди ставят вместо официального. Второй PR в ту же организацию — cloudflared#1582, горячая перезагрузка конфига, с января: ноль комментариев. Два PR, два игнора — это уже не случайность, это способ ведения проекта.
А вот как выглядит проект, который отвечает. У Obico нет экспертизы в Helm, а я не хотел держать чарт у себя — сценарий Cloudflare мне не нужен ни с какой стороны. В июне я открыл ишью с предложением официального чарта, через три недели принёс PR — его приняли. Потом подписанный OCI-артефакт, потом arm64-образы, потом стартап-пробы. Я напросился к ним в мейнтейнеры и в хоббийное время развиваю чарт у них, а не у себя. Ни у кого нет форка. Двадцать три дня от идеи до принятого чарта — против двух с половиной лет тишины. Разница не в размере компании. Разница в том, отвечает проект или нет.
Заметьте: я не требую, чтобы приняли. Я требую, чтобы ответили. Отказ с аргументацией — нормальный исход. Молчание — нет. Как проектам не утонуть в потоке PR и какие из них брать — отдельный разговор, и он будет. Но реагировать на инпут — часть контракта, если ты назвался открытым.
А если не хочешь — скажи, как SQLite. Это честно, и тогда все знают, что делать: форкать без иллюзий. Сам по себе форк — не зло.
Когда форк — это нормально
Форк — легитимный инструмент, и случаев, когда он уместен, больше, чем можно подумать после всего, что я написал выше. Объединяет их одно: форк — это осознанный выбор там, где донести до мейнлайна заметно дороже, чем самому нести бремя поддержки. Ключевое слово — осознанный: цена бремени посчитана заранее, а не выяснилась постфактум.
Когда ты в гараже ваяешь пет-проект, берёшь чужое и быстро накидываешь патч, чтобы закрыть свою боль. Это прикольно, и никому ничего не должен ты.
Когда ты большой и тебе нужно то, что нужно исключительно тебе. Это допустимо — ты осознанно берёшь пассив на баланс и знаешь его цену.
Когда дельта архитектурная, а не фичевая. В прошлой статье я рассказывал про blockstor — LINSTOR-совместимый control plane, переписанный в кубернетес-нативной парадигме. Это не патч, это другой проект, и через PR такое не заносят.
Когда проект тебе не рад. Сказал честно, как SQLite, — молчит, как Cloudflare, — или просто не хочет твоих патчей по политическим или каким угодно ещё причинам. Тогда форк — единственный выход, и заходить в него надо без иллюзий: это не «мой код доедет позже», это «мой код живёт здесь».
И самый очевидный случай, о котором забывают: форк как площадка. Без форка ты и PR не отправишь — так устроен GitHub. Ветка в форке, где патч живёт, пока идёт ревью и тесты, — это не форк в смысле этой статьи. Это транспорт до мейнлайна.
Ни в одном из этих случаев форк сам по себе не проблема. Проблема — когда большой открытый проект молчит, и сообщество живёт на форках не по выбору, а потому что деваться некуда. Тогда форк — не инструмент, а симптом. Именно об этом была прошлая статья.
Играть вдвоём
Итого формула простая. Сообщество приносит патчи в проекты. Проекты помогают эти патчи довести и принять. Ни одна сторона не работает без другой: без входящего потока проект — собор, без реакции на поток — собор с опубликованными чертежами.
Так что если у вас в столе завалялся патч, который закрыл вашу боль, — принесите его. Да, спросят за дизайн. Да, придётся переделать. Это и есть школа, и она бесплатная. Взамен ваш код будут мейнтейнить чужие руки, ваша железка переживёт ваш интерес, а ваш труд не растворится в форке, о котором никто не узнает.
Каждому по потребностям лицензия уже гарантирует. От каждого по способностям — это за вами.

