Инженерный английский Стандарты и RFC: язык нормативных документов
0%

Стандарты и RFC: язык нормативных документов

Стандарты и RFC: язык нормативных документов

Типичная сцена. В pull request спорят двое. Один: «в RFC сказано SHOULD, значит рекомендация, можно не делать». Второй: «SHOULD — это “следует”, то есть надо». Через сорок минут команда всё ещё спорит — не о протоколе, а о значении одного английского слова. Спор бессмысленный: у этого слова есть письменное определение, принятое в 1997 году и действующее в тысячах документов. Оно занимает две страницы, и тот, кто его прочитал, экономит команде эти сорок минут.

Эта глава не про грамматику. Она про узкий, жёстко формализованный диалект английского, на котором написаны спецификации протоколов, стандарты языков, RFC, документы W3C и ISO, а также внутренние design docs. Хорошая новость: словарь у диалекта маленький и закрытый, конструкции повторяются, освоить его можно даже со слабым разговорным английским. Плохая: читается он не так, как обычный текст, и «интуитивный» перевод слов should, may, can, will регулярно превращается в баги в проде. Предыдущая глава — Чтение документации — разбирала язык туториалов и README; здесь другой жанр: документация объясняет и уговаривает, спецификация ограничивает. Про базовый уровень языка, без которого трек не поможет, честно сказано в карте трека.

Когда это нужно по работе, а не «для развития»: реализуете протокол или формат — HTTP, OAuth, JWT, WebSocket — и поведение в углу спецификации не такое, как вы предположили; спорите с чужой поддержкой о том, кто нарушил стандарт; аудитор ссылается на пункт, который надо прочитать; пишете API-контракт для трёх команд из разных стран; ищете в Security Considerations готовую модель угроз от авторов протокола. Всё это работа с текстом, а не с речью: нужен не беглый разговорный английский, а умение медленно и точно прочитать двадцать предложений.

Нормативный текст не объясняет, а обязывает

Сравните два предложения об одном и том же.

Документация:
  You'll usually want to set a timeout here, otherwise a slow server can hang
  your worker for minutes.

Спецификация:
  A client MUST NOT wait indefinitely for a response. A client SHOULD apply
  a timeout to each request.

Первое обращается к вам (you), объясняет причину (otherwise), допускает исключения (usually). Второе не обращается ни к кому лично: субъект — роль (a client), глагол — ключевое слово, причина не указана вовсе. Нормативный текст не убеждает, он задаёт условие: реализация либо соответствует ему, либо нет. Практическое следствие: значимо каждое слово, и особенно — подлежащее. A recipient MUST ignore... и A sender MUST NOT send... — два разных требования к двум разным сторонам, и их постоянно путают, читая по диагонали. Первый вопрос к любому предложению: кто обязан, и мы ли это?

BCP 14: одиннадцать слов, на которых держится половина интернета

Ключевые слова определены в RFC 2119 («Key words for use in RFCs to Indicate Requirement Levels», 1997) с уточнением из RFC 8174 (2017). Вместе они образуют BCP 14 — ссылку на неё вы увидите почти в любом RFC.

Шкала силы требования по BCP 14

Слово Синонимы Нормативный смысл Как это читает носитель
MUST REQUIRED, SHALL абсолютное требование «без этого реализация неверна, точка»
MUST NOT SHALL NOT абсолютный запрет «нельзя, исключений нет»
SHOULD RECOMMENDED могут быть веские причины поступить иначе, но последствия надо полностью понять и взвесить «делай так, если не можешь объяснить, почему нет»
SHOULD NOT NOT RECOMMENDED то же самое, наоборот «так почти всегда не надо, и это надо обосновать»
MAY OPTIONAL полностью на усмотрение реализации «хочешь — делай; а вот партнёр обязан работать в обоих случаях»

Три вещи здесь важнее самих определений.

SHOULD — это не «можно забить». RFC 2119 формулирует жёстко: могут существовать valid reasons in particular circumstances поступить иначе, но полные последствия должны быть understood and carefully weighed. На языке практики: отклонение от SHOULD — архитектурное решение, которое надо записать, ровно для этого существует ADR. «Мы не делаем X, потому что Y» — нормально. «Мы не делаем X, потому что там же should» — нет.

MAY — это не про вас, а про партнёра. Самая недооценённая строка таблицы. Если спецификация говорит, что сервер MAY не прислать заголовок, ваш клиент обязан корректно работать без него: MAY всегда означает парное скрытое требование к другой стороне.

Регистр решает. До 2017 года было неясно, что делать с the client should retry строчными. RFC 8174 закрыл вопрос: слово нормативно, только когда написано заглавными. Отсюда абзац, который вы видели сотни раз:

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174]
when, and only when, they appear in all capitals, as shown here.

when, and only when — юридическая формула «тогда и только тогда». are to be interpreted, а не must be interpreted: оборот is to be адресует предписание читателю документа, а не реализации. Встретив строчное should, проверьте, есть ли в документе этот абзац: если есть, строчное should — обычное английское слово из авторского пояснения.

Как читать требование

Что НЕ является ключевым словом

Здесь половина ошибок при чтении и почти все ошибки при письме.

  • can — способность или физическая возможность, не разрешение. The server can return 503 описывает, что бывает, а не что позволено. Хотели разрешить — пишите MAY.
  • will — предсказание, не требование. The token will expire in 3600 seconds — факт.
  • must строчными — по RFC 8174 не нормативно, но в текстах ISO всё иначе, см. ниже.
  • is required to, needs to, has to — разговорные обороты вне закрытого словаря, как и двусмысленное it is recommended that: это RECOMMENDED или мнение автора?

Не только IETF: ISO, W3C, ECMA

Ловушка для того, кто выучил BCP 14 и решил, что теперь всё понимает: в других организациях словарь другой.

IETF (RFC) ISO / IEC W3C
требование MUST, SHALL, REQUIRED shall строчными MUST
рекомендация SHOULD, RECOMMENDED should SHOULD
разрешение MAY, OPTIONAL may MAY
возможность can can can
слово must не нормативно запрещено к использованию обычно нормативно

В ISO/IEC Directives, Part 2 — своде правил написания стандартов ISO — сказано прямо: требование выражается словом shall, а must использовать нельзя, потому что его резервируют за внешними обязательствами, которые стандарт не устанавливает (законы, физика). Вывод: прежде чем спорить о слове, посмотрите, чей документ. Строчное shall в ISO — железное требование, хотя в RFC оно бы не значило ничего.

Третий диалект — спецификации языков программирования, где нормативность выражена терминами-категориями. Классика — стандарт C (ISO/IEC 9899, доступен черновик N1570): undefined behavior — никаких требований вообще, компилятор вправе делать что угодно; unspecified behavior — несколько допустимых вариантов, документировать выбор не обязаны; implementation-defined behavior — вариантов несколько, но свой реализация обязана задокументировать; locale-specific behavior — зависит от национальных настроек. По-английски четыре термина выглядят похоже; по смыслу это лестница от «полного произвола» до «обязанности объясниться», и на ней держатся многолетние споры об оптимизациях компилятора. В спецификации ECMAScript (tc39.es/ecma262) нормативность выражена иначе — пошаговыми алгоритмами: Let x be ..., If ..., then, Perform ..., Throw a TypeError exception. Императив Let там значит не «пусть будет», а «на этом шаге движок обязан сделать так».

Анатомия RFC: что читать первым

RFC не читают подряд. У документа жёсткая структура, заданная RFC 7322, и по ней навигируются так же, как по коду.

Анатомия RFC: шапка, разделы и выноски

Шапка — метаданные, решающие, стоит ли читать дальше:

  • Obsoletes: 2616 — документ заменяет перечисленные, их читать не нужно; на странице старого RFC будет обратное, Obsoleted by: .... Классика: HTTP/1.1 описывался в RFC 2616, потом был разбит на RFC 7230–7235, а сейчас действуют RFC 9110 (семантика), 9111 (кэширование), 9112 (HTTP/1.1). Половина статей в интернете до сих пор цитирует 2616. Про сам протокол — HTTP.
  • Updates: 3864 — дополняет, не отменяя; опаснее Obsoletes, потому что для полной картины нужны оба документа одновременно.
  • CategoryStandards Track, Informational, Experimental, Best Current Practice. Informational не обязывает ни к чему: это может быть описание чужого проприетарного протокола, и ссылаться на такой RFC как на стандарт — типичная ошибка в спорах.

Дальше по разделам: Abstract (4–8 самодостаточных предложений, читается всегда) → Introduction (контекст, можно пропустить) → Conventions / Terminology → основной текст → Security ConsiderationsIANA ConsiderationsReferences. Три из них важнее прочих. Conventions / Terminology — здесь включается BCP 14 и определяются термины: если документ вводит resource owner или user agent, дальше эти слова значат ровно написанное, а не то, что вы думаете; читать до основного текста. Security Considerations — обязательный для всех RFC раздел (RFC 3552), готовая модель угроз от авторов протокола и часто самая полезная для практика часть; ср. Моделирование угроз. References делятся на Normative — обязательные к прочтению и реализации — и Informative, то есть контекст и историю.

Два внешних инструмента экономят часы: datatracker.ietf.org показывает переписку рабочей группы, где видно, почему формулировка именно такая, а errata — исправления к опубликованным RFC. Текст RFC после публикации не меняется никогда, поэтому известные ошибки живут там: если предложение выглядит противоречиво, проверьте errata до спора с коллегой.

Практический смысл диаграммы: Internet-Draft — это не стандарт. Ссылка вида draft-ietf-something-07 — черновик, который через полгода станет другим или исчезнет. Код, опирающийся на draft, — техдолг с известной датой; так и напишите в комментарии.

Грамматика нормативного английского

Конструкций мало, и они повторяются. Пассив без агента. The request is rejected — кем? В хорошей спецификации такого нет, в средней — сплошь и рядом. Читая, достраивайте субъект; когда пишете сами — не используйте пассив в требованиях вообще. Роли вместо людей: подлежащее — a client, the server, a sender, a recipient, an implementation, a user agent, но никогда you и we. Артикль значим: A client MUST... — любой клиент, The client MUST... — тот самый, о котором шла речь выше.

Условия. Четыре разных слова, которые русскоязычные читают одинаково:

If the header field is present, the recipient MUST validate it.
   → условие; если поля нет, требование не применяется
Unless otherwise specified, the default value is 30 seconds.
   → «если явно не указано иное»; готовая формула, встречается постоянно
When a connection is closed, pending requests MUST be failed.
   → момент времени, а не гипотеза: это точно случится
Where the two definitions conflict, this document takes precedence.
   → «в тех случаях, в которых»; не про место, а про случаи

Последнее — частая ошибка чтения: where в нормативном тексте почти никогда не значит «где».

Область действия отрицания. Самая опасная конструкция:

A client MUST NOT retry the request unless the response includes a Retry-After
header field.

По умолчанию повторять запрещено; разрешение появляется только при наличии заголовка. Русскоязычный читатель часто переворачивает логику и запоминает «надо повторять, если есть Retry-After» — а это другое утверждение. Приём: разбейте на два предложения, «нельзя вообще» плюс «исключение».

Формулы, которые стоит узнавать в лицо: for the purposes of this document — дальше идёт определение, действующее только здесь; unless otherwise noted — «если не оговорено иное»; as defined in Section 4.2 of [RFC9110] — нормативная отсылка, читать обязательно; is said to be — вводит термин; takes precedence over — что важнее при конфликте правил; at its discretion — сигнал уровня MAY. И принцип робастности из RFC 793 (TCP), раздел 2.10: be conservative in what you do, be liberal in what you accept from others. Отсюда во всех протоколах формула A recipient MUST ignore unrecognized parameters — получатель обязан молча проигнорировать незнакомое. Это готовый ответ на вопрос «а что если придёт лишнее поле».

Грамматика формата: ABNF

Синтаксис в IETF-документах задаётся не прозой, а нотацией ABNF (RFC 5234). Читать её проще, чем кажется:

credentials    = auth-scheme [ 1*SP ( token68 / #auth-param ) ]
auth-scheme    = token
token68        = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" ) *"="
; [ ... ]  — необязательная часть
; 1*SP     — один или более пробелов: «1*» значит «минимум один»
; /        — альтернатива, «или»; *"=" — ноль или более знаков равенства

Три обозначения покрывают 90% случаев: * — повторение (2*4DIGIT — от двух до четырёх цифр), [ ] — опциональность, / — выбор. Спор о том, допустим ли пробел в заголовке, ABNF решает за десять секунд — быстрее, чем чтение прозы вокруг.

Из текста в протокол

Полезное упражнение — выписать требования на диаграмму и посмотреть, кто кому что должен (формулировки ниже учебные, в стиле RFC 6749, а не процитированы дословно).

Что видно на картинке и плохо видно в тексте: SHOULD на шаге 5 означает, что бывают клиенты, присылающие токен иначе, и сервер ресурсов обязан это учитывать; MAY на последнем шаге означает, что клиент обязан обработать ответ без описания ошибки — здесь ломается большинство самописных SDK; требование на шаге 7 адресовано серверу ресурсов, а не серверу авторизации, и путаница ролей в OAuth стоит дороже всего: OAuth 2.0 и OIDC, JWT и токены.

Как писать собственный нормативный текст

Внутренний RFC, design doc, описание API-контракта, раздел «Требования» в задаче — всё это нормативные тексты, даже если вы их так не называете. И читают их люди, для которых английский тоже неродной: любую двусмысленность прочитают в двух смыслах, и оба реализуют. Правило, решающее 80% проблем: одно требование — одно предложение — один субъект — одно ключевое слово.

Ниже — формулировки, которые постоянно встречаются в текстах русскоязычных инженеров. Колонка «как читает носитель» здесь главная: проблема почти всегда не в грамматике, а в том, что смысл смещается.

Что написано Как это читает носитель Как переписать
The client should send the Authorization header. «желательно; вообще-то можно и без него» The client MUST send an Authorization header field with every request.
It is necessary to validate the signature. «кому-то надо бы проверить»; субъекта нет The resource server MUST validate the token signature.
The token will be checked by the server. описание будущего, не требование The server MUST verify the token before processing the request.
Server can reject the request. «сервер в принципе на это способен» — не разрешение The server MAY reject the request with 429 Too Many Requests.
Don't use this endpoint. разговорный тон, для спеки — шум Clients MUST NOT use this endpoint. It is retained for backward compatibility only.
It is not recommended to store tokens in localStorage. личное мнение автора Storing access tokens in browser local storage is NOT RECOMMENDED.
The field is required, otherwise error 400 will be returned. смешаны требование и последствие The request MUST include an idempotency_key field. + A request without this field MUST be rejected with 400 Bad Request.
In case of error the client must retry. «error» — какая? must строчными не нормативно If the server responds with 429 or 503, the client SHOULD retry using exponential backoff.
We recommend to use pagination. грамматически неверно и не нормативно Clients SHOULD paginate requests that may return more than 100 items.

Правила, которые редакторы стандартов применяют механически:

  1. Никаких etc., and so on и and/or в требованиях. Список либо исчерпывающий, либо это не требование; вместо and/or пишите A, B, or both.
  2. Не используйте MUST без проверяемого критерия. The code MUST be readable — пожелание.
  3. Не ставьте MUST ради важности. RFC 2119 отдельно предупреждает: императивы применять экономно и только там, где это нужно для совместимости. Документ, где всё MUST, невозможно внедрить постепенно.
  4. Пронумеруйте требования. R-14: The service MUST ... — тогда на них можно ссылаться в тестах, задачах и разговоре. Про связку с приёмкой — Критерии приёмки и Документирование требований.
  5. Отделите нормативное от пояснительного пометкой Note: или This section is non-normative.

Скелет внутреннего документа, который работает в командах:

## RFC-014: Idempotency keys for the payments API

Status: Draft | Accepted | Superseded by RFC-021

### Terminology
The key words MUST, MUST NOT, SHOULD, and MAY in this document are to be
interpreted as described in BCP 14 when they appear in all capitals.
An **idempotency key** is a client-generated string of at most 64 characters.

### Requirements
R-1. A client MUST include an Idempotency-Key header field in every POST /payments request.
R-2. The server MUST store the key for at least 24 hours.
R-3. On a repeated request with the same key and the same body, the server MUST return
     the original response and MUST NOT create a second payment.
R-4. On a repeated request with the same key and a different body, the server MUST
     respond with 422 Unprocessable Content.
R-5. A client SHOULD generate keys as UUIDv4.

Note: 24 hours matches the retry window of our payment provider.

Раздел Terminology здесь не бюрократия: он превращает SHOULD из вежливого пожелания в проверяемое условие, на которое можно сослаться в ревью, не начиная спор с нуля. Ту же технику — нумерованные требования, одно предложение на требование — стоит переносить в описания задач (Задачи и баг-репорты) и изменений (Коммиты и pull request).

Как спорить, ссылаясь на спецификацию

Ссылка на стандарт — самый сильный аргумент в технической переписке и самый лёгкий способ прозвучать высокомерно. Разница в формулировке.

Плохо:  This is wrong, read the RFC.
        → снисходительно, читается как «ты некомпетентен»
Плохо:  I think GET should not change data, no?
        → это не мнение, а требование стандарта; неуверенность здесь вредит
Хорошо: Per RFC 9110 Section 9.3.1, a GET request is expected to be safe, and this
        handler writes to the DB — a proxy or a crawler could duplicate the write.
        Could we move it to POST, or is there a constraint I'm missing?

Три приёма делают ссылку рабочей, а не показной: указывайте раздел, а не только номер документа (ссылка на весь RFC читается как «иди разбирайся сам»); переводите требование в последствие — не «стандарт запрещает», а «прокси повторит запрос, и мы получим двойное списание»; оставляйте место для контраргумента — иногда команда сознательно отклонилась от SHOULD, и это записано в ADR. Тон несогласия разбирается в Комментариях на ревью.

Типичные ошибки

Читать спецификацию подряд. RFC на 200 страниц не предназначен для линейного чтения. Рабочий порядок: Abstract → Terminology → оглавление → нужный раздел → Security Considerations. Пропустив раздел терминов, вы подставите бытовые значения слов и получите неверную модель протокола.

Читать устаревший документ. Проверяйте шапку на Obsoleted by: поисковик выдаёт старые RFC чаще новых, потому что на них больше ссылок.

Путать роли. sender/recipient, client/server, authorization server/resource server. Требование, адресованное не вам, — подсказка, к чему готовиться, а не задача.

Считать SHOULD необязательным, а MAY — обязательным. Ошибки зеркальные и обе дорогие: первая ломает совместимость, вторая раздувает объём работы.

Переводить «должен» как should. Главная ловушка русскоязычного инженера: в русском «должен» — обязанность, в английском should — рекомендация; обязанность — это MUST (или shall в ISO). Другие расхождения такого рода собраны в Типичных ошибках русскоязычных инженеров.

Писать спецификацию красиво. Синонимы — враг нормативного текста. Если сущность называется access token, она называется так во всех предложениях; the token, the credential, the key — это уже три разные сущности для читателя. Подробнее — Ясность.

Мини-практикум

Для каждого предложения определите: требование, рекомендация, разрешение или не норма? И к кому оно?

1. A server MUST NOT generate a Content-Length header field in a 204 response.
2. Implementations should be careful when parsing user input.
3. The client MAY include an Accept-Language header field.
4. Where this document conflicts with [RFC7231], this document takes precedence.
5. Tokens can be revoked at any time.
6. A recipient MUST ignore any parameter it does not recognize.

Разбор:

  1. Запрет, адресован серверу. Заглавные — нормативно.
  2. Не норма: should строчными, действия нет. Комментарий автора, реализовать нельзя.
  3. Разрешение клиенту — и скрытое требование к серверу работать без этого заголовка.
  4. Не требование к реализации, а правило разрешения конфликтов между документами: смотрите на Where.
  5. Не норма: can — описание возможности. Разрешение выглядело бы как MAY be revoked, обязанность поддержать отзыв — как MUST support revocation.
  6. Требование получателю, тот самый принцип робастности: добавление нового поля в API не ломает корректных клиентов.

Чеклист

Когда читаете: проверил шапку на Obsoleted by и Category; прочитал раздел терминов до основного текста; по каждому требованию определил роль-субъект; отличил ключевые слова заглавными от «слов, похожих на требования»; заглянул в errata, если текст противоречив; прочитал Security Considerations до того, как счёл реализацию готовой.

Когда пишете: включил абзац про BCP 14; у каждого требования есть явный субъект и ровно одно ключевое слово; требования пронумерованы и проверяемы; пояснения помечены как Note:; нет etc., and/or и синонимов для одной сущности; MUST стоит только там, где нарушение действительно ломает совместимость.

Мини-итог

Нормативный английский — это диалект из нескольких десятков конструкций и одиннадцати ключевых слов, значения которых определены письменно. Его не надо чувствовать, его надо знать. Разница между MUST, SHOULD и MAY не стилистическая: это разница между «сломается интеграция», «нужно записать решение в ADR» и «партнёр обязан пережить оба варианта». Регистр букв имеет силу, а should — не перевод слова «должен». Умение читать такие тексты даёт инженеру редкую позицию в споре: не «я так думаю», а «вот раздел, вот формулировка, вот следствие». Умение их писать превращает design doc в документ, который нельзя понять двумя способами.

Источники

Что дальше

Сообщения об ошибках и логи: как читать чужие и писать свои — следующий жанр инженерного английского. Спецификация задаёт, что должно произойти; сообщение об ошибке говорит, что пошло не так. Оба текста читают в состоянии стресса, и оба должны пониматься однозначно с первого раза.

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

Статья описывает общий случай. Если у вас частный — можно разобрать его отдельно, платно. А если не хватает целого материала, предложите тему: её оплачивают вскладчину, и она выходит открытой для всех.

Доска запросов