8 лет назад я исправил опечатку в чужом репозитории, а сейчас регулярно делаю коммиты в проекты, которые использую, и даже вошел в core team библиотеки с 27000 звёзд на GitHub
В этой статье покажу, что участие в Open Source проще, чем кажется. Расскажу, как регулярная работа с чужим кодом помогает быстрее разбираться в незнакомых кодовых базах, писать тесты и лучше документировать решения. А также объясню, почему публичная активность на GitHub выгодно отличает вас от других разработчиков, особенно в эпоху повсеместного использования ИИ.
Почему стоит участвовать в Open Source проектах
Понимание инструментов изнутри
Мне нравится возможность открыть исходники библиотеки и разобраться, как она устроена. Это меняет отношение к библиотекам - перестаёшь их использовать как чёрный ящик, чувствуешь контроль в разработке. А когда натыкаешься на баг, то можешь сам найти причину, а не ждать исправления месяцами. Например на одном из проектов нужно было интегрировать интерфейс ИИ чата. Такой интерфейс требует много времени на самостоятельную разработку: чат, скролл, поддержка таблиц и заголовков в ответах от ИИ, возможность прикреплять файлы. Воспользовался готовой библиотекой, которая предоставляет нужный интерфейс. Интегрировать удалось быстро, но почему-то ИИ переставал отвечать спустя минуту после общения в чате. После чтения исходников оказалось что библиотека запоминает переданные мной HTTP заголовки и использует их для всех последующих запросов в мой бекенд. Код использования библиотеки:
const runtime = useCustomEdgeRuntime({
headers: { Authorization: `Bearer ${token}` },
});
Старые заголовки спустя время становились не актуальными и авторизация ломалась. Проблему удалось исправить лишь внутри библиотеки:
-const headers = new Headers(this.options.headers);
+const headersValue = typeof this.options.headers === 'function'
+ ? await this.options.headers()
+ : this.options.headers;
Таким образом, пользователи библиотеки могут передать свою функцию для HTTP заголовков, которая вычисляется каждый раз при отправке:
const runtime = useCustomEdgeRuntime({
headers: async () => {
const token = await getToken();
return { Authorization: `Bearer ${token}` },
}
});
Получился простой пулл реквест, который автор библиотеки одобрил.
Освоение новых подходов
В каждом популярном открытом проекте всегда есть чему научиться. Работа с такими проектами даёт то, чего не получишь ни в одном курсе - возможность увидеть реальный код, не всегда превосходный, часто сложный, но работающий для миллионов пользователей. Например именно так я узнал про табличные тесты - способ покрывать большое количество тестовых сценариев малым количеством строк:
const tests = [
{ input: "2 + 3 * 4", expected: 14 },
{ input: "(2 + 3) * 4", expected: 20 },
{ input: "10 / 0", expected: "error" },
{ input: "2 + ", expected: "error" },
{ input: "2.5 + 1.3", expected: 3.8 },
{ input: "sqrt(16)", expected: 4 },
{ input: "pow(2, 3)", expected: 8 },
];
tests.forEach(({ input, expected }) => {
const result = evaluate(input);
expect(result).toBe(expected);
});
Такой подход применяется в статическом анализаторе типов PHPStan, который имеет 8 млн загрузок в месяц. Именно табличное тестирование потребовалось применить в этом пулл реквесте, чтобы за один раз покрыть большое количество сценариев.
Карьерные преимущества
Сейчас когда ИИ генерирует резюме, код портфолио и массово откликается на вакансии в автоматическом режиме, разработчикам выделяться стало сложно. Я считаю что публичная активность в популярных проектах помогает выделиться. Например, мне писали CTO американских стартапов с предложением о трудоустройстве с релокацией. Эти компании активно используют MobX, увидели меня в верхнем списке контрибьюторов и написали на email. А на текущую валютную удалёнку устроился благодаря узким знаниям API Телеграм и Open Source библиотеки Gram.js - работодатель искал именно эту экспертизу. Получается есть ситуации когда работодатели сами находят кандидатов, а не наоборот, и ваши коммиты будут работать на вас.
Отправляем пулл реквест
С чего начать
Документация или исправление опечаток - идеальный старт. Для таких случаев GitHub даже предоставляет удобный интерфейс в одну кнопку: у каждого файла в репозитории есть иконка редактирования. Если на неё нажать и отредактировать файл - GitHub за вас сделает форк репозитория, запушит коммит и предложит создать pull request. Не нужно самому делать форк и открывать редактор. Примеры просты пулл реквестов:
Изменения кода
Изменения в коде требуют более основательного подхода - нужно форкнуть библиотеку, настроить и запустить проект локально.
Бывает, что в проекте нет Docker и не описано, как его запустить локально. В таком случае могут помочь файлы CI (Continuous Integration), если они есть. На GitHub они находятся в папке .github/workflows
. Обычно там запускается проект и выполняются тесты с линтингом кода. Если код запустился в CI, то эти же инструкции помогут запустить его локально.
Далее опишу стратегию успешного пулл реквеста, чтобы увеличить шансы на принятие ваших изменений:
Золотое правило: сначала создаём issue, потом пишем код. Не стоит предлагать изменения без предварительного обсуждения. Автор библиотеки может посчитать ваше изменение ненужным, и тогда вы просто потратите время впустую.
При создании issue можно сначала поблагодарить автора за библиотеку. Скорее всего, он работает над ней бесплатно в свободное время. Если есть возможность - предоставляем способ воспроизведения в виде ссылки на код или репозиторий. Чем легче автору запустить и проверить, тем быстрее он ответит.
Если проблема обсуждена и автор библиотеки дал зелёный свет, рекомендую воспроизвести проблему в тестах. Такая возможность есть не всегда, но вас скорее всего и так попросят добавить тесты, как меня попросили тут и тут.
Если пулл реквест готов, тесты есть, а автор библиотеки не отвечает неделями, можно написать follow-up сообщение. Пример на английском: "Hi, just wanted to follow up on this PR, would love to get it merged. Let me know if there's anything else you need from me. Thanks!". На моей практике это ускоряет принятие пулл реквестов
Не ломаем обратную совместимость, если есть возможность. Библиотеки, как правило, стараются следовать семантическому версионированию, чтобы пользователи могли предсказуемо обновляться. Если мы добавляем новый параметр в функцию, то старый код должен продолжать работать.
Например, я пользовался библиотекой wavesurfer.js (9000 звёзд), которая анализирует аудио и рисует столбцы на HTML Canvas. Столбцы не имели закруглений, а на работе по дизайну требовались закругления. Код, отвечающий за рисование столбца в библиотеке, выглядел так:
fillRectToContext(ctx, x, y, width, height) {
if (!ctx) {
return;
}
ctx.fillRect(x, y, width, height);
}
Для закругления пришлось переработать так:
fillRectToContext(ctx, x, y, width, height, radius) {
if (!ctx) {
return;
}
if (radius) {
this.drawRoundedRect(ctx, x, y, width, height, radius);
} else {
ctx.fillRect(x, y, width, height);
}
}
Появился новый параметр radius
, а старый код, не знающий об этом параметре, продолжит работать. Пулл реквест целиком.
От участника к мейнтейнеру
Я много лет пользуюсь библиотекой MobX для управления состоянием на фронтенде. Библиотека имеет порядка ~2 млн загрузок в неделю и 27000 звёзд на GitHub. В какой-то момент в коде библиотеки я обнаружил, что описание TypeScript типов для одной из функций выглядит очень громоздко:
export interface IActionFactory {
<A1, R, T extends (a1: A1) => R>(fn: T): T & IAction
<A1, A2, R, T extends (a1: A1, a2: A2) => R>(fn: T): T & IAction
<A1, A2, A3, R, T extends (a1: A1, a2: A2, a3: A3) => R>(fn: T): T & IAction
<A1, A2, A3, A4, R, T extends (a1: A1, a2: A2, a3: A3, a4: A4) => R>(fn: T): T & IAction
<A1, A2, A3, A4, A5, R, T extends (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) => R>(fn: T): T &
IAction
<A1, A2, A3, A4, A5, A6, R, T extends (a1: A1, a2: A2, a3: A3, a4: A4, a6: A6) => R>(fn: T): T &
IAction
<A1, R, T extends (a1: A1) => R>(name: string, fn: T): T & IAction
<A1, A2, R, T extends (a1: A1, a2: A2) => R>(name: string, fn: T): T & IAction
<A1, A2, A3, R, T extends (a1: A1, a2: A2, a3: A3) => R>(name: string, fn: T): T & IAction
<A1, A2, A3, A4, R, T extends (a1: A1, a2: A2, a3: A3, a4: A4) => R>(name: string, fn: T): T &
IAction
<A1, A2, A3, A4, A5, R, T extends (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) => R>(
name: string,
fn: T
): T & IAction
<A1, A2, A3, A4, A5, A6, R, T extends (a1: A1, a2: A2, a3: A3, a4: A4, a6: A6) => R>(
name: string,
fn: T
): T & IAction
}
Мне показалось, что это можно оптимизировать, в результате получилось:
export interface IActionFactory {
<T extends Function | null | undefined>(fn: T): T & IAction
<T extends Function | null | undefined>(name: string, fn: T): T & IAction
}
Мейнтейнер библиотеки удивился что так можно, но отнёсся к изменению с осторожностью. Тогда я написал исчерпывающие тесты, и пулл реквест приняли.
С тех пор я делал другие пулл реквесты, отвечал на вопросы пользователей и участвовал в обсуждениях. Это не осталось незамеченным - автор библиотеки предложил мне стать мейнтейнером. Теперь я могу принимать или отклонять пулл реквесты, закрывать issue и участвовать в выпуске новых версий библиотеки.
Главным уроком для меня стало то, что не нужно просить разрешения быть полезным. Также оказалось, что не обязательно быть суперэкспертом, достаточно быть активным и последовательным.
Главные выводы
Участие в open source может оказаться одним из лучших решений в вашей карьере. Это увлекательно - в каждом проекте можно чему-то научиться, а начать можно буквально с исправления опечатки. Понимание инструментов изнутри прокачивает технические навыки быстрее любых туториалов. А публичная активность - это отличная инвестиция в карьеру.
Комментарии (17)
Kuzmin_Vyacheslav
22.05.2025 19:19Прямо в сердечко, про то, как опенсорс и бесплатные баги реально меняют игру.
kompilainenn2
22.05.2025 19:19Очень многие разработчики этого не понимают. Я часто слышу, что вклад в опенсорц это просто бесплатный труд или ещё мулька "этот ваш опенсорц очень сложный и вообще легаси"
olku
22.05.2025 19:19Это те же разработчики, которые сами учились на опенсорс, или пользуются копайлотоми, обученными на опенсорс? Стартовавшее как движение маргиналов программистов, обзываемое "раковой опухолью", оно изменило индустрию программного обеспечения в корне.
kompilainenn2
22.05.2025 19:19Не готов сказать, на них не написано, не будешь же у каждого спрашивать возраст
apcs660
22.05.2025 19:19когда полепишь дерьма по заданию
горкомаумных менеджеров выше которые выбирают стек, что и как делатьгадая на картах таропо непонятным причинам, иногда хочется что то сделать для души.Так что опенсорс это акт творения, проявление божественной сути.
Иногда с поддержкой бизнеса, симбиоз так сказать.
trauus
22.05.2025 19:19Всегда хотел попробовать участие в опенсорс, однако после 8-часового рабочего дня уже нет ни сил, ни времени. Как вам это удается?
kubk Автор
22.05.2025 19:19Хороший вопрос. Если исправление касается инструмента, который используется на работе и без этого фикса нельзя выполнить рабочую задачу - делаю в рабочее время. В готовой библиотеке уже написан почти весь код, остаётся поправить небольшую деталь. Работодателю это выгоднее, чем если бы разработчик писал решение с нуля. Многие разработчики просто делают локальный патч для себя без пулл реквеста, так быстрее. Но если решение уже найдено - оформить пулл реквест не занимает много времени.
Если же это проект для души, то тут уже вопрос приоритетов - как например с чтением книг, занятиями спортом или пет проектами. Главное чтобы было интересно, тогда время найдётся. Для меня Open Source отличается от работы - нет дедлайнов и больше свободы. Можно найти интересную задачу или отложить её на потом.
apcs660
22.05.2025 19:19основная проблема - нехватка времени.
А если работаешь дома, жена и дети быстро находят тебе применение.
Пулл реквест приходится делать в личное время, ведь пока автор проекта ответит и тд, время уйдет а на работе исправление надо как правило, "вот прям щас и закрыть и тикет". Некоторые авторы по полгода думают, а некоторые уже забили на свой проект и на багфиксинг (как у меня недавно с JCL получилось). Посмотрел на P4J - делают практически то же самое что я слепил в 2015, думаю надо посмотреть их код и перейти на него. Заодно проверить на баг который для JCL правил.
gun_dose
22.05.2025 19:19В опенсорс вполне себе можно вкладываться и в рабочее время. Стандартная ситуация: используешь в рабочем проекте какую-то библиотеку, а там оказывается ошибка, которая влияет на работу твоего проекта. И тут уже выбора нет, приходится проблему решать, делать пулл реквест, ставить библиотеку в проект из форка. А потом ещё несколько недель или даже месяцев следить за этим пулл реквестом, чтобы когда его примут, поставить библиотеку обратно из оригинального репозитория.
Я вообще думал, что это у всех так.
grosm4n
22.05.2025 19:19На днях изучал проекты CNCF, от некоторых репо в проектах под их крылом становится дурно. Все в одну кучу, многие issues по своей сути просто дублируются. Да, поддержка, да разработка, но нужно же как-то соблюдать организованность. Это хорошо когда заметил баг и можешь создать issue, но кажется многие пользователи не знают как пользоваться этим разделом, потому что на одну ошибку может быть создано по 3 обсуждения. Автору спасибо что поделился опытом, еще больше подтолкнул заниматься вкладом в опенсорс.
Kenya-West
22.05.2025 19:19Issue можно конвертировать в Discussion (и наоборот). Чего не хватает CNCF, так это условного "community manager'а", который будет хозяйственной деятельностью на публичных репозиториях заниматься - автоматизацию настраивать, гайдлайны писать, редиректить фидбек кому надо и issue категоризировать.
Хорошо оформленный GitHub в ручной работе много не требует. Вон, разработчики VS Code как-то справились с 185К issues, и ничего, выжили.
apcs660
22.05.2025 19:19Спасибо за правильные мысли, сейчас начал пет проект по мотивам идей положенных на полку.
В одной опен сорс библиотеке исправил достаточно неприятный баг в класс лоадере.
Не поленюсь и запушу им фикс с описанием бага. До этого частенько ленился.
Делал тикеты до этого в опен сорс изредка, но один раз запомнился тикет в solr в real-time document get() - закомментили код у себя, дя повышения производительности, указал им на это что функционал сломали, один молодой участник проекта предложил правильное с моей точки решение, с обратной совместимостью, но лид его отверг. Неудивительно что с таким подходом этот проект идет на дно.
Еще вопрос такой - эккаунт в том же apache jira был заведен с корпоративным почтовым адресом , доступ к которому после ухода утрачен. Почту указал новую, а имя то осталось старое, корпоративная почта.
Так что для личного портфолио делайте фиксы и тикеты в опенсорсе с личными данными (почтой). Работодателю как правило все равно, бонусы и премии за это не дают.
pnmv
когда я заглядываю на гитхаб, мне хочется сесть и исправить всё, до чего дотянулся взор.
это не означает, что "там, у них" всё плохо, просто настроения у меня такие.