Хабрахабр

[Перевод] Черновик FAQ: Почему стандарты С++ выходят каждые три года?

У WG21 есть строгий график (см. P1000) выпуска стандарта каждые три года. И никаких задержек.

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

Этот материал будет опубликован в следующей публичной версии Р1000, её мы разошлём через несколько недель начиная с текущего момента. Всё это я расписал в виде вопросов и ответов для следующего черновика Р1000, и разослал копию участникам комитета, направлявшимся в Кёльн.

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

В стандарте есть баги, следует ли вам отложить C++20?

Конечно да, и нет.

До начала 2020-го (встречи в Кёльне, Белфасте и Праге) мы должны дать обратную связь и применить любые другие решения проблем, а также исправления багов. Мы движемся в заданном направлении с выбранной скоростью: исправление багов запланировано на этот последний год, поэтому в графике в раннем С++ «19» (Kona) установлен крайний срок прекращения добавления функциональности (feature freeze) в С++ «20», чтобы у нас был год на исправление багов, включая работу с комментариями из разных стран этим летом.

Если провести ещё одну-две встречи, то можно было бы добавить <название_фичи>, которая почти готова, так что следует ли вам отложить C++20?

Конечно да, и нет.

Так мы поступили с концептами: они не были готовы к переходу из TS прямо в С++17. Подождите, когда пройдёт ещё пара встреч (после Праги), и С++23 будет открыт для бизнеса, и в первую очередь проголосуем за добавление <название_фичи> в рабочий черновик С++23. Теперь вся функциональность готова. Поэтому на первой встрече по С++20 (в Торонто) проголосовали за перенос основной функциональности концептов в черновик С++20, что дало много времени на совершенствование и доработку остальной противоречивой части TS (не-«шаблонный» синтаксис), которая была внедрена в следующем году (Сан-Диего).

Кажется, это слишком строго. Зачем выпускать релизы IS через фиксированные интервалы (три года)?

Потому что в случае с релизом С++ IS это один из двух основных вариантов управления проектом, и опыт показывает, что этот вариант лучше второго.

Какие два варианта управления проектом для релиза С++ IS?

Рад, что вы спросили.

Нельзя одновременно контролировать и то, и другое. В случае с релизом есть два основных варианта: выбрать фичи или срок релиза, и когда выбираешь одно, то теряешь контроль над определением другого. Вкратце:

Поясняю:

Если выясняется, что нужно больше времени на доработку фичи из черновика стандарта, то всему миру придётся тебя ждать. (1) «Что»: выбираем фичи и отгружаем по мере готовности, не нужно выбирать время релиза. Ты работаешь над большими фичами, которые требуют нескольких лет разработки, а потом пытаешься вообще перестать работать над новыми фичами, пока стабилизируешь релиз.

Это подход «оставляем пациента не зашитым» на неопределённый срок, который привёл к задержке интеграционного тестирования и релиза. Так было с C++98 (его ждали примерно в 1994-м, Бьёрн сказал, что релиз не выйдет к тому времени, это будет провалом) с C++11 (его назвали 0x, потому что x ждали к 2007-му). В течение нескольких лет большинство компиляторов не соответствовали стандарту, потому что никто не знал, сколько несовместимых изменений выкатит комитет в новом релизе, или когда его вообще ждать? А это, в свою очередь, привело к большой неуверенности на рынке относительно сроков выхода следующего стандарта, да и выйдет ли он вообще (да, не только участники разработки, но даже некоторые члены комитета серьёзно сомневались в 1996-м и 2009-м, появятся ли вообще соответствующие релизы). Это привело к большому разнообразию и фрагментации поддержки С++ в компиляторах, доступных для сообщества.

Не совсем, просто были неопытны и… скажем так, «оптимистичны». Почему мы так поступили, мы что, идиоты? В 1994-1996-м и в 2007-2009-м мы действительно верили, что сейчас передвинем ещё на одну-две-три встречи, и всё сделаем, и каждый раз переносили на сроки до четырёх лет. Это была дорога, вымощенная лучшими намерениями. И теперь на своём опыте убедились, что не может быть переноса на год или два.

К счастью, всё изменилось благодаря варианту (2).

Если выясняется, что нужно больше времени на доработку фичи из черновика стандарта, мы её выбрасываем и отгружаем то, что готово. (2) «Когда»: выбираем срок релиза и отгружаем те фичи, что будут готовы, не нужно выбирать набор фич. И ты постоянно работаешь над фичами, потому что их разработка полностью отделена от текущего релиза (нет большой точки соединения). Можно продолжать работать над большими фичами, на создание которых уходит времени как на несколько релизов, но делать это в сторонних «ветках», добавляя их в мастер-ветку IS по мере готовности.

Это подход «регулярно зашиваем пациента», который приводит к ожиданию более высокого качества благодаря принудительным регулярным интеграциям и отказу от добавления работы в черновик IS, пока она не достигнет определённого уровня стабильности, обычно в рамках ветки фичи. Этого подхода мы придерживаемся с 2012-го и не хотим от него отказываться. За эти годы авторы компиляторов стали всё раньше и раньше после очередного релиза выпускать соответствующие стандарту версии своих продуктов, чего раньше никогда не было. Также это создаёт предсказуемый цикл релиза, на который может опираться рынок. Это только во благо для всего рынка — разработчиков, пользователей, преподавателей. А в 2020-м мы ожидаем выхода полностью соответствующих стандарту реализаций в один год с выходом стандарта, чего тоже никогда раньше не было.

Хотя отгружаем то, что успели подготовить (а если что-то не успели, то это не отгружаем). И ещё обратите внимание, что с тех пор, как мы начали придерживаться этого подхода, мы стали делать больше (если оценивать по большим, средним и маленьким фичам) и с более высоким качеством (если оценивать по строгому уменьшению количества отчётов о багах и комментариев к черновикам каждого стандарта).

Насколько серьёзно вы относитесь к подходу (2)? Если по мнению авторитетного члена комитета какая-то большая фича будет «почти готова», то у вас возникнет соблазн немного подождать, верно?

Очень серьёзно относимся, и нет.

Когда консенсуса не достигли, Страуструпа прямо спросили, хочет ли он задержать релиз С++17 на год, чтобы включить в него концепты. У нас есть статистика: в 2016-м в Джексонвилле, когда мы окончательно определялись с фичами для С++17, Бьёрн Страуструп выступил на пленарном заседании с предложением включить концепты в С++17. Выбор стоял такой: (2) выпускаем С++17 без концептов, а затем С++20 с концептами (что мы и сделали), или (1) переименовываем С++17 в С++20, что изоморфно (2) за исключением пропуска С++17 и отказа от выпуска того, что уже было готово для С++17. Бьёрн без колебаний и увёрток ответил «нет», и добавил, что С++17 без концептов важнее, чем С++18 или С++19 с концептами, хотя Страуструп работал над ними около 15 лет.

А что насчёт компромисса между (1) и (2)? Скажем, обычно придерживаемся (2), но с «небольшой» гибкостью в сроках, чтобы получить «немного» дополнительного времени, если нужно доработать фичу?

Нет, потому что получится (1).

Фред Брукс в The Mythical Man-Month популярно объяснил про «мифический маленький перенос» и пришёл к заключению: «Не допускайте никаких маленьких переносов».

Нам пришлось бы вернуться от (2) к (1), вне зависимости от того, как сильно мы стараемся избежать этого, и при этом не получили бы никаких выгод. Представьте, что мы перенесли С++20. Не бывает таких понятий, как перенос одной или трёх встреч, потому что за это время другие продолжат (справедливо) говорить: «ну, моей фиче нужно всего ещё одну встречу, мы же всё равно перенесли, давайте ещё одну перенесём». Если бы мы решили отложить С++20, чтобы отполировать его, то мы задержали бы стандарт минимум на два года. — То есть в любом случае мы будем отгружать С++23, и разница лишь в том, что мы не переносим С++20 с большим объёмом проделанной работы, готовой к релизу, и не заставляем весь мир ждать ещё три года. И если мы переносим как минимум на два года, то это означает, что С++20 становится С++22, а вероятнее всего С++23… но мы уже собираемся отгружать С++23! Задержка не пойдёт на пользу этим фичам, большинству из них или всем вместе.

Задержка С++20 означает пропуск С++20 вместо выпуска хорошего, стабильного, готового продукта, и никакой пользы от этого не будет. Поэтому предложение равносильно «давайте превратим С++20 в С++22 или С++23», и простой ответ на него: «да, у нас будет и С++23, но в дополнение к С++20, а не вместо него».

Но фича X сломана / нужно больше времени, чем у нас осталось на исправление багов в C++20!

Не вопрос! Мы её можем просто выпилить.

Эти группы рассмотрят обращение, и если они решат, что фича сломана (и пленум с ними согласится), то фичу отложат до следующего релиза С++. В этом случае кому-то нужно будет написать в EWG или LEWG (в зависимости от ситуации) письмо с описанием ситуации, и предложить удалить фичу из рабочего черновика IS. Мы уже поступали так с концептами С++0х.

Это был бы… перебор. Но в случае с (1) мы перенесём не только эту фичу, а весь набор фич из С++20 в С++23!

Подход (2) означает «основные/второстепенные» релизы?

Нет. Сначала мы так говорили, пока не поняли, что (2) всего лишь означает, что не нужно выбирать набор фич даже с точки зрения «основного/второстепенного» релиза.

Релизы получаются: Подход (2) означает лишь «отгружаем то, что готово».

  • того же размера (то есть обычно среднего) для фич «поменьше», потому что на их разработку тратится меньше времени (скажем, меньше трёх лет на каждую), и в целом мы получаем такое же количество завершённых фич в релизе;
  • и переменного размера (раз на раз не приходится) для фич «побольше», на которые уходит больше времени (скажем, больше трёх лет на каждую), и в каждый IS-релиз входит столько этих фич, сколько успевают доделать к выпуску. Поэтому в одних релизах их больше, в других меньше.

C++14 и C++17 были относительно маленькими, потому что много усилий по стандартизации тратилось на долгоиграющие фичи, описанные в предложениях к внедрению (например, контракты) и «ветки фич» в TS (например, концепты).

C++20 — это большой релиз…

Да. В C++20 много крупных фич. Три из крупнейших начинаются на «ко» (концепты, контракты, корутины), так что мы могли бы назвать его co_cpp20. Или co_dependent.

…и не слишком ли много сделано за трёхлетний цикл для C++20?

Нет, см. выше «раз на раз не приходится».

C++20 большой не потому, что за три года мы сделали больше, а потому что много долгих разработок (включая как минимум две, над которыми в текущем виде мы работали с 2012-го в виде P-предложений и TS-«веток») дошли до стадии готовности и их решили включить в черновик IS одного и того же релиза.

Главная разница между подходом (1) для С++98 и С++11 и подходом (2) заключается в том, что в С++98 и С++11 задерживали выпуск до готовности всех этих фич, а теперь мы отгружаем большие по мере готовности, и попутно с ними релизим многое другое. Практически всегда основные фичи разрабатываются по много лет.

Мы не сделали за последние три года больше, чем за два предыдущих цикла, просто допилили больше основных фич. С++20 прошёл через такой же трёхлетний цикл, как С++14 и С++17. Если так произойдёт, мы сообщим об этом в предложении по внедрению и объясним причины. Если бы какая-то из них не была готова, то мы бы её выкинули и доделывали уже для С++23.

Но поскольку мы придерживались подхода (2), то также выпустили наработки, которые были готовы к окончанию трёхлетнего и шестилетнего циклов. С++14+17+20 составили наш третий девятилетний цикл (2011-2020) после С++98 (1989-1998) и С++11 (2002-2011).

Не лучше ли вылавливать баги, когда продукт находится в разработке, а не после того, как он выпущен?

Конечно лучше.

Но если мы говорим о причинах задержки выхода стандарта С++, то этот вопрос подразумевает два ложных предположения:

  • что до выхода стандарта фичи не выходили и не использовались (по многим уже есть опыт использования в production);
  • и что все фичи можно использовать вместе до выхода стандарта (нельзя).

Поясняю:

  1. Большинство крупных фич С++20 были реализованы в том виде, в каком они отражены в текущем черновике стандарта, как минимум в одном компиляторе, и в большинстве случаев уже использовались в production-коде (то есть уже доступны для пользователей, которые очень довольны). Например, корутины (внедрённые всего за пять месяцев до этой статьи) два года применялись в production в MSVC и год в Clang, чему оказались очень рады крупные клиенты (например Azure и Facebook).
  2. Мы не собираемся вылавливать многие проблемы взаимодействия фич, пока пользователи не начнут использовать их в production, то есть до выхода стандарта, потому что многие разработчики будут ждать его выхода, чтобы реализовать разные проекты. И если мы проявим неуверенность в отношении сроков релиза, то эти реализации тоже будут отложены. Ну, что-то всё-таки реализуют, но многое будет поставлено на паузу, пока разработчики не будут уверены, что мы готовы релизить. Спросите создателей <название_любимого_компилятора>, что произошло, когда они реализовали <название_крупной_фичи> до того, как она появилась в опубликованном стандарте. Во многих случаях реализовывать приходится неоднократно, и обламывать потребителей тоже неоднократно. Поэтому разработчики предпочитают ждать, когда комитет утвердит те или иные фичи.

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

Стандарт никогда не будет идеальным… разве вы не релизите ошибки?

Да.

Если мы видим, что фича не готова, то мы должны её удалить из релиза.

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

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

Но не следует ли вам стараться минимизировать ошибки в релизах?

Да. Мы стараемся.

Также существует риск и (возможная) цена отказа от выпуска того, что нам кажется готовым. Но мы не стараемся избежать всех рисков. И чаще всего мы оказываемся правы.

Вы уверены, что сейчас качество лучше, чем при использовании подхода (1)?

Да.

А причина именно в регулярности релизов, в размещении больших фич сначала в TS-ветки (включая полные описания их интеграции с основным стандартом) и в их последующем вливании, когда мы убеждаемся в готовности. Согласно объективным метрикам, объёму комментариев из разных стран и отчётов об ошибках, С++14 и С++17 были нашими самыми стабильными релизами, и по этим метрикам они в 3-4 раза превзошли С++98 и С++11.

Такого никогда не было ранее, когда мы долгое время держали пациента не зашитым, с длинными списками проблем и разложенными вокруг органами, которые мы собираемся скоро засунуть обратно. С 2012-го основной стандарт всегда поддерживается в состоянии почти-готов-к-отгрузке (так что даже рабочие черновики такого же высокого качества, как релизы стандартов С++98 и С++11). Если бы хотели, то могли бы выпустить CD хоть сейчас, без встречи в Кёльне, и всё равно качество было бы гораздо выше, чем когда-либо у CD C++98 или С++11 (по правде, и их опубликованных стандартов). Теперь мы знаем, что можем выдерживать график с высоким качеством работы, потому что всегда остаёмся в состоянии близкой готовности к релизу. А учитывая, что С++98 и С++11 были успешны, то понимание, что сейчас качество ещё выше, означает, что мы на верном пути.

C++98 и C++11 разрабатывались примерно по 9 лет и были очень хорошими продуктами…

Да: 1989-1998 и 2002-2011.

…а C++14 и C++17 были второстепенными релизами. C++20 является основным релизом?

Повторюсь, я считаю, что правильно сравнивать С++14+17+20 как единое целое: это наш девятилетний цикл, но поскольку мы придерживались подхода (2), мы также выпустили и те наработки, которые были готовы к завершению трёхлетнего и шестилетнего циклов.

Подход (2) позволяет достигать feature-based целей вроде P0592 для следующего C++?

Конечно! Пока в нём нет слов вроде «должен включать в себя эти фичи», потому что тогда это будет подход (1).

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

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»