К настоящему моменту Linux существует уже почти три десятка лет. В ранние дни этой ОС Линус Торвальдс сам управлялся с кодом, написанным другими программистами, делающими вклад в развитие Linux. Тогда не было никаких систем контроля версий, всё делалось вручную. В современных условиях те же задачи решаются с использованием git.
Правда, всё это время кое-что оставалось неизменным. А именно, код отправляют в список рассылки (или в несколько списков), а там его проверяют и обсуждают до тех пор, пока он не будет сочтён готовым для включения в ядро Linux.
Но, несмотря на то, что этот процесс работы с кодом успешно использовался многие годы, он постоянно подвергался критике. Например, эта недавняя статья, которую написала Сара Новотны из Microsoft, наделала в интернете много шума. В той статье сказано, что методы совместной работы над кодом, применяемые при разработке ядра Linux, устарели. Там говорится о том, что если сообщество разработчиков Linux хочет привлекать в свои ряды молодых специалистов, эти методы хорошо бы заменить на что-то более современное. В спорах вокруг этих идей схлестнулись их защитники и противники.
Я полагаю, что моё положение позволяет мне высказать некоторые идеи относительно разработки ядра Linux. Вот уже почти десять лет я пишу код для Linux и для других проектов, работа над которыми организована похожим образом. Когда я работал в Red Hat, я сделал вклад в код инфраструктуры x86-ядра, в код гипервизора KVM и эмулятора QEMU, в код Xen Hypervisor. Я участвовал и в развитии других проектов. Я не особенно много занимался Linux примерно 7 лет, но лишь из-за того, что посвящал своё время работе над C++-фреймворком Seastar и над базой данных ScyllaDB. Оба эти проекта разрабатывались с применением методологии, очень похожей на ту, что используется при разработке Linux. Теперь я работаю ведущим инженером в Datadog, в компании, где процессы разработки ПО представляют собой практически полную противоположность тем, что используются в работе над Linux. Это гораздо ближе к тому, как организована разработка в других веб-компаниях.
Итак, на чьей я стороне? Позвольте мне сразу же однозначно заявить о том, что мне не нравится процесс разработки Linux. Я совершенно уверен в том, что это — не только барьер для новых разработчиков, но и барьер для обеспечения высокой продуктивности работы над кодом (и дело тут вовсе не в использовании электронной почты). Это — источник негативных ощущений, которые испытывают разработчики. И я не собираюсь следовать этой модели при работе над любым проектом, в котором у меня есть исключительное право принимать решения о том, как будет организована работа над ним.
Но, в то же время, возникает такое ощущение, что многие критики процесса работы над Linux полагают, что тот факт, что его защитники так яростно за него сражаются, является лишь следствием того, что сообщество Linux полно стариков, мёртвой хваткой вцепившихся в традиции и ни под каким предлогом не желающих меняться. А это не так (хотя я уверен в том, что такие люди в сообществе Linux есть). Процесс разработки ядра Linux даёт тем, кто его использует, некоторые уникальные и важные преимущества. Если применить те же принципы в любом другом проекте, такой проект от этого лишь выиграет.
Любые другие инструменты, помимо электронной почты, диктуют тем, кто их применяет, достаточно жёсткие схемы работы, лишающие Linux подобного выигрыша. А списки рассылки — это лишь заметный механизм, привлекающий внимание спорщиков. Нам нужны инструментальные средства, которые могут снизить входные барьеры для разработчиков Linux. Такие средства, которые способны исправить недостатки процесса разработки. Такие, которые позволят различным организациям осознать сильные стороны организации разработки Linux. Подобные инструменты способны по-настоящему благотворно повлиять на всю индустрию разработки программного обеспечения.
Существует много подобных механизмов, приносящих немалую пользу. Я, чтобы не растягивать наш разговор, сосредоточусь на одном из них, который я считаю самым важным. Я сделаю всё возможное для того чтобы раскрыть его сущность и рассказать о том, почему он, несмотря на его сильные стороны, вызывает у разработчиков так много негативных эмоций. Расскажу я и о том, почему он, с одной стороны, способен принести пользу другим проектам, а с другой — почему он невероятно важен для Linux.
Сообщения коммитов и патчи
В мире разработки ядра Linux есть правило: код, предназначенный для включения в ядро, должен быть разбит на отдельные патчи. Каждый из них должен решать одну и только одну задачу. К каждому патчу должно быть подготовлено содержательное сообщение коммита. Нередко бывает так, что подобные сообщения оказываются длиннее того кода, который они описывают.
Это — яркий пример того, чего, в целом, не хватает в других проектах. Большинство сообщений коммитов, которые мне встречались в современных проектах на GitHub, выглядят примерно как «изменения по состоянию на 25 августа», или немного (но лишь немного) лучше, вроде «реализация функции X». Если кому-нибудь понадобится в будущем взглянуть на подобный код, ему будет нелегко разобраться в том, почему в код были внесены именно такие изменения,. Некоторые ошибки, исправляемые подобными коммитами, могут быть едва заметными. Если не знать о том, как именно такие ошибки были исправлены, они легко могут вернуться в проект. Читая короткое, бессодержательное сообщение коммита, я могу и не узнать о том, в каких обстоятельствах была выявлена ошибка.
Вот небольшой пример. Взгляните на коммит в ядро Linux, сделанный моим хорошим другом Иоганном Вейнером. Мне не составляет труда представить себе то, как в другом проекте сообщение к подобному коммиту выглядело бы как «устранение предупреждений». А когда я читаю сообщение обсуждаемого здесь коммита, я узнаю о том, почему можно, без вреда для проекта, избавиться от этих предупреждений, о том, в каких обстоятельствах это, и правда, ни к чему плохому не приведёт, и о том, каких правил надо придерживаться, если когда-нибудь будет решено изменить этот код.
Я уверен, что во многих организациях есть люди, которые работают именно так. Но при работе над ядром Linux делать это обязаны все, кто в этом деле участвует. Поэтому я совершенно уверен в том, что, читая сообщение коммита, я пойму всё, что нужно понять о соответствующих изменениях кода. Если мы говорим об ошибке, то я узнаю о том, в каких системах она проявилась, в каких условиях она произошла, почему она никак не повлияла на другие системы, и на что надо обратить внимание для того чтобы эта ошибка не вернулась бы в проект.
Подобная схема работы весьма желательна в любой организации. Это облегчает другим людям (и самому разработчику, когда он через некоторое время обратится к своему коду) понимание причин внесения изменений в код, понимание того, почему код работает именно так, как работает. Это облегчает знакомство с проектом для новых программистов. Это решает проблему возвращения старых ошибок, уменьшает опасность того, что некий код, вроде бы, не связанный с рассматриваемым, может в нём что-то поломать.
В других проектах это «весьма желательно». А вот в Linux это совершенно необходимо по двум причинам:
- В работе над Linux участвует огромное количество людей. У них разный опыт, они работают в разных компаниях. Внося вклад в код Linux, они преследуют разные цели, они по-разному строят планы на будущее. Масштабные проекты, которые реализуются в рамках некоей организации, могут использовать другие средства для распространения информации о них, другие надёжные механизмы реализации ответственности. В сфере же опенсорсных проектов лишь очень немногие (если таковые вообще существуют) так же велики и долгосрочны, как Linux. В очень немногие проекты внесло вклад столько же людей, сколько внесло вклад в Linux.
- Применение современных патчей к более старым версиям проекта (бэкпорты). Учитывая размер и важность Linux, эта система находится в состоянии постоянного создания её форков. Даже сейчас, в 2020 году, различные дистрибутивы Linux могут добавлять собственные исправления поверх той версии системы, которую они назвали LTS-версией. Сейчас это происходит реже, чем прежде, но лишь из-за того, что в самой Linux стали использовать концепцию LTS-версий, исправления из которых могут использовать и различные дистрибутивы Linux. Раньше, в начале 2000-х годов, всё было не так. Тогда, например, немалая доля усилий Red Hat была направлена на решение подобных задач.
Бэкпорты — это, для современных онлайн-компаний, которым не нужно поддерживать несколько параллельных линеек продукта, обычно не проблема. Они что-то создают, передают пользователям, и на этом всё заканчивается. Но когда в игру вступают бэкпорты, дело сильно усложняется. Разработчику (вероятно — не автору программы), может понадобиться принимать решение о том, как слегка адаптировать код к более старой кодовой базе, немного отличающейся от современной. И решением, которое позволяет минимизировать риск, часто может быть (и часто бывает) такое, которое заключается в создании патча лишь для реализации определённой части большого набора изменений. Представьте себе коммит на 2000 строк, в котором содержится 5 строк кода, исправляющего некую ошибку. Ещё представьте себе то, что эта ошибка возникла после рефакторинга API. Что бы вы выбрали: подготовку бэкпорта на основе огромного набора изменений или на основе хорошо документированных, хорошо описанных, разбитых на мелкие части патчей? Я, как человек, который сделал бесчисленное множество бэкпортов, уже знаю, как ответил бы на такой вопрос.
Ну, с бэкпортами или без них, проектам приходится платить высокую цену за те преимущества, которые даёт такая организация работы, когда огромное внимание уделяется тщательному документированию изменений. Теперь программисту нужно заботиться не только о коде, но и о том, как его реорганизовывать и приводить в соответствие правилам работы над проектом.
Некоторые из подобных реорганизаций кода просты. Скажем, речь идёт об использовании команды git add -p и о выборе того, что именно попадёт в некий пакет изменений. Всё становится немного сложнее в том случае, если программист сталкивается с циклическими зависимостями между отдельными фрагментами кода. Представьте себе функцию, которая возвращает объект, имеющий тип, который будет добавлен в проект после добавления в него этой функции. Для того чтобы справиться с этой ситуацией, придётся использовать код, который, в итоге, в готовый проект не попадёт, а будет лишь играть роль временного решения.
Всё это добавляет программистам головной боли, но нельзя говорить о том, что подобные задачи совершенно нерешаемы. Предположим, вы с хирургической точностью разделили всё то, чем занимаетесь, на фрагменты, которыми легко и удобно пользоваться. Настоящие проблемы начинаются после того, как другие программисты приступают к рассмотрению вашего кода. Код-ревью в любой организации — это очень важно. Специалисты читают чужой код и предлагают (или требуют) внести в него изменения.
Предположим, программисту предложили добавить новый параметр некоему методу, присутствующему в первом патче из какого-то набора исправлений. И ещё предположим, что этот метод используется во всех последующих патчах.
Это значит, что программисту придётся возвращаться к первому патчу и добавлять в метод новый параметр. После этого следующие патчи применить уже будет нельзя. Поэтому программисту не только придётся ломать голову над тем, почему это так, но и нужно будет вручную исправлять все ошибки. Если все отдельные патчи раньше были протестированы, то теперь результаты этих тестов оказываются неактуальными и патчи придётся тестировать снова.
Реорганизация работы — это небольшая проблема. А вот переделывание того, что уже сделано, это — проблема куда более серьёзная.
Вот что мне хотелось бы донести до сообщества Linux-разработчиков и до тех, кто имеет отношение к этому сообществу: всё это, безусловно, вполне реализуемо. Но если это — не входной барьер для молодых специалистов, тогда я даже не знаю о том, что можно назвать «входным барьером». Необходимость тратить своё время, силы, нервы и ресурсы компьютеров на реорганизацию, переписывание, переработку того, что уже сделано, это явно не то, к чему стремятся программисты. Мне в этой связи попадалась одна идея, которая периодически появляется в таком виде: «…но у хорошего программиста проблем с этим не будет». Ещё её озвучивают так: «но это учит программистов определённому стилю
Вот что мне хотелось бы сказать тем, кто далёк от сообщества Linux: у процесса разработки, применяемом в работе над Linux, имеются совершенно реальные сильные стороны. Некий инструмент не в состоянии полностью справиться с задачей, которую представляет собой работа над Linux. GitHub, например, отлично показывает себя в работе над проектами, в которых новый код всегда добавляется после существующего. Можно, конечно, воспользоваться командой git push --force, принудительно включая некую ветку в состав репозитория, но тогда комментарии, прикреплённые к коммиту, окажутся, по сути, «повисшими в воздухе», и обсуждение этого коммита окажется бессмысленным.
Современные средства разработки многое упрощают. Они позволяют вызывать выполнение каких-то действий при возникновении определённых условий, они поддерживают процессы непрерывной интеграции и развёртывания проектов, уведомляют программистов об изменениях в коде и решают массу других задач. Но они совершенно точно усложняют процесс разбиения результатов чьего-то труда на небольшие фрагменты, с которыми легко и удобно работать. Использование обычных текстовых электронных писем многое усложняет, но такая организация работы, надо отметить, не мешает применению процессов разработки, ведущих к определённой цели.
Даже если бы можно было объективно и точно оценить то, сколь много выиграла и проиграла бы (ничего бы она не проиграла) экосистема Linux, отказавшись от существующего процесса разработки, это вряд ли что-то изменило бы. Дело в том, что существующая ситуация отлично иллюстрирует человеческую природу, когда люди стремятся сохранить то, что до этого очень хорошо показало себя на практике.
Есть ли выход из сложившейся ситуации?
Я искренне считаю, что если бы в нашем распоряжении были бы инструменты, которые давали бы различным организациям те же выгоды, которые получает сообщество Linux от применяемой в нём методологии разработки, то это принесло бы всем очень большую пользу. А если бы такие инструменты и правда существовали, то, возможно, даже сообщество Linux могло бы заменить ими обычные текстовые электронные письма.
У меня нет ответа на вопрос о том, как могли бы выглядеть подобные инструменты. Но я позволю себе рискнуть и немного о них поразмышлять:
- Git — это система контроля версий кода. Такие системы, что совершенно естественно, стремятся к тому, чтобы присоединять новый код к существующему, а не переписывать историю изменений проектов. Однако это накладывается и на процесс разработки в GitHub, где разработка и код-ревью построены вокруг git-коммитов, и на «текстово-почтовых» разработчиков Linux, которые занимаются разработкой в собственных локальных деревьях git, постоянно переписывая историю. Возможно, стоит разделить процессы разработки и код-ревью, создав такие инструменты, которые не так сильно ограничивают людей, как существующие, что позволит упростить процессы редактирования кода. А в Git можно хранить результаты всех согласований и правок. Тут можно привести пример взаимоотношений CSS и HTML, а именно — то, как CSS позволил HTML-разработчикам отделить представление от логики. Помните, как работали с HTML до появления CSS? Я, например, в силу своего возраста, это помню.
- Если расширить вышеозвученную идею, то, возможно, патчи, в которых изменения построчно сравниваются с существующим кодом, лишь всё усложняют. Может ли существовать система, в которой можно описать изменения, вносимые в код, на более высоком уровне? Такая система могла бы позволить строго определённым образом применить эти изменения. Например, можно было бы написать так: «Переместить функцию create_foo() так, чтобы она была бы перед функцией create_bar()», или так: «Добавить к функции create_bar() целочисленный параметр y, который должен быть её последним параметром». Даже если следующие изменения будут модифицировать код так, что это нарушит возможность построчного сравнения изменений кода с его исходным вариантом, это не помешало бы подобной системе в применении разрешённых изменений к слегка модифицированной версии кода. Может, я рассуждаю слишком наивно, и подобную систему создать невозможно, но, глядя на невообразимые успехи GPT-3, я вполне могу поверить в то, что нечто подобное вполне может быть создано в обозримом будущем.
- Или, если приблизить рассуждения к реальности, возможно, есть некое промежуточное решение, где процесс изменения кода после код-ревью может быть организован так, чтобы это всегда выглядело бы как добавление нового кода к существующему. Только после того, как код всех устраивает, и исключительно в этом случае, изменения вносятся в историю. При этом может использоваться достаточно простой инструмент, который способен помочь тем, кто поддерживает проект, удостовериться в том, что изменения, внесённые в проект, соответствуют тому варианту кода, который, после всех согласований и изменений, был в итоге признан годным для включения в проект.
Занимались ли вы разработкой чего-либо, сравнимого по масштабам с Linux?
Автор: ru_vds