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

в 13:32, , рубрики: c++, Блог компании Mail.Ru Group, никто не читает теги, Программирование, С++, Совершенный код, управление разработкой
Черновик FAQ: Почему стандарты С++ выходят каждые три года? - 1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Если мы будем контролировать это Мы перестанем контролировать это Мы можем работать над «большими» многолетними фичами? Когда мы добавляем фичи в рабочий черновик IS? Что мы делаем, если обнаруживаем проблемы с добавленной фичей?
«Что»: фичи, которые мы отгружаем «Когда»: срок релиза. Да, в документах с предложениями и рабочем черновике IS. Обычно раньше, чтобы провести больше интеграционных тестов → снижается средняя стабильность рабочего черновика. Задерживаем выпуск стандарта.
«Когда»: срок релиза «Что»: фичи, которые мы отгружаем Да, в документах с предложениями и «ветках фич» в TS. Обычно позже, когда фича лучше проработана → повышается средняя стабильность рабочего черновика. Убираем фичу, позже можем добавить её снова, если она готова к моменту отправления следующего «поезда» IS.

Поясняю:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Поэтому предложение равносильно «давайте превратим С++20 в С++22 или С++23», и простой ответ на него: «да, у нас будет и С++23, но в дополнение к С++20, а не вместо него». Задержка С++20 означает пропуск С++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. Если так произойдёт, мы сообщим об этом в предложении по внедрению и объясним причины.

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

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

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

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

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

Поясняю:

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

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

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

Да.

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

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

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

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

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

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

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

Да.

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

С 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).

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

Автор: AloneCoder

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js