Процессы рецензирования документов: отправка / утверждение / отклонение с неизменяемыми версиями
Вики решили задачу совместного написания документов. Они не решили задачу управления ими. Как только вашей документации нужно удовлетворить аудитора или выдержать опору на неё во время инцидента, модель вики ломается. Вот как устроена изнутри платформа знаний, построенная вокруг рецензирования: блокировки по ETag, обязательная причина отклонения, семантические различия и история только на добавление, которую никто не перепишет незаметно.

TL;DR: Wiki-style platforms optimized for collaborative editing made one assumption that does not hold in regulated or incident-response contexts: that the latest version is the authoritative one. When a document carries operational weight (runbook, policy, threat-model, change procedure), "anyone can edit, latest wins" stops being a workflow and becomes a liability. This post walks through the mechanics that turn a knowledge base into an audit-grade artifact: explicit submit / approve / reject states, ETag-based optimistic locking, mandatory rejection reasons, semantic diffs, and an immutable version history.
The default model for collaborative documentation is the wiki. Everyone can edit, the most recent edit wins, and the history is a linear chain of revisions you can inspect if you bother to look. For its original purpose (getting a team to write things down without a Word-document-via-email bottleneck), the model is excellent.
The model breaks the moment the documentation starts carrying operational weight. A runbook the on-call engineer follows at 3 AM. A response procedure the SOC executes during an incident. A security policy the auditor reviews next month. A threat model the architecture team relies on for the next release. In those contexts, the question is not "what does the document say now," but "what did the document say when this decision was made, who approved the change, and on what basis."
Wiki tools were not designed to answer those questions, and most attempts to retrofit them produce ceremony without substance.
This post walks through the model behind Krasper's own review-first knowledge hub, built review-first rather than edit-first. The mechanics are unglamorous: ETag locks, explicit states, mandatory rejection reasons, semantic diffs, an append-only history. The result is a knowledge base that holds up under audit, behaves predictably under concurrent editing, and makes "what was approved when" a one-query answer rather than a forensic investigation.
Contents
- Why wikis are the wrong substrate for governed documentation
- The three-state document model
- ETag optimistic locking: concurrent editing without the last-write-wins trap
- Mandatory reject-with-reason: the form is the policy
- Diff viewer: semantic comparison over textual noise
- Append-only versioning: no in-place edits, period
- The audit record that falls out of the workflow
- What to ask a knowledge platform before betting on it
1. Почему вики — неподходящая основа для управляемой документации
Модель вики стоит на трёх допущениях, и управляемая документация ломает все три.
Первое: актуальная версия и есть авторитетная. В вики последняя правка становится текущей истиной, и это нормально для команды, ведущей заметки со встреч, и абсолютно неверно для политики, изменения в которой должны пройти рецензирование до вступления в силу. Само редактирование не должно менять то, что организация считает авторитетным.
Второе: история — любопытная деталь, а не обязательство. Вики хранят историю ревизий, но относятся к ней как к средству отладки: ничто не мешает её удалить, подписанного аудиторского следа нет, и нет гарантии, что показанное в истории — это то, что исторически отображалось.
Третье: конфликты правок редки, и «побеждает последний» достаточно. На малопосещаемой странице — да. В рабочей инструкции, которую во время инцидента одновременно правят два инженера, тихая перезапись — ровно тот механизм, из-за которого теряются критичные исправления.
Некоторые продукты этой категории, включая Confluence, добавили функции утверждения поверх вики-основы. Для лёгких согласований это работает, но исходные допущения остаются. Шаг рецензирования надстроен, а не заложен; история изменяема; модель правок по-прежнему «побеждает последний».
Платформа, построенная вокруг рецензирования, переворачивает каждое из этих допущений. Авторитетна не последняя правка, а последняя утверждённая версия. История ведётся только на добавление и криптографически привязана. Одновременные правки обнаруживаются и показываются как конфликты, а не разрешаются молча. Дальше — о том, как это выглядит на практике.
2. The three-state document model
Every document in a review-first system lives in one of three states at any moment:
- Draft: being edited by one or more authors, not visible to general readers, not part of the canonical record.
- Submitted: frozen for review, visible to designated reviewers, awaiting an approve or reject decision.
- Approved: the current canonical version, visible to all readers, closed to edits until a new version is approved.
┌───────────────────────────────┐
│ │
author edits │ │
──────────▶ [Draft] │
│ │
│ submit │
▼ │
[Submitted] │
│ │
┌─────────┴─────────┐ │
│ │ │
approve reject │
│ │ │
▼ ▼ │
[Approved] back to Draft │
│ with reason ────────────────┘
│
new draft for next version starts from here
Переходы контролируются. Перевести документ в «Отправлен» могут только авторы. Вывести его из этого состояния могут только назначенные рецензенты, но не автор. Система обеспечивает это на уровне API, а не интерфейса, поэтому никакой обход на стороне клиента не изменит состояние без того, чтобы бэкенд записал действующее лицо и решение.
Утверждение — не конец жизни документа. Это конец данной версии. Немедленно начинается новый черновик следующей версии, ответвлённый от утверждённого состояния, и цикл повторяется. История версий растёт линейно через утверждённые версии; черновики и отклонённые отправки фиксируются, но в каноническую цепочку не входят.
3. Оптимистичная блокировка по ETag: одновременное редактирование без ловушки «побеждает последний»
В операционных условиях одновременное редактирование одного документа двумя инженерами встречается достаточно часто, чтобы проектировать под это напрямую. Платформа, построенная вокруг рецензирования, решает это оптимистичной блокировкой на основе ETag.
Каждое чтение документа возвращает ETag — сильный, привязанный к версии отпечаток состояния документа на момент чтения. Каждая запись должна содержать ETag, с которым работал редактор. Если документ тем временем изменился, ETag больше не совпадает с текущим состоянием, и запись отклоняется как конфликт.
┌─────────────────────────────────────────────────┐ │ Editor A Server Editor B │ │ │ │ │ │ │ │ GET doc │ │ │ │ │◀───────── etag:v7 ─────────────│ │ │ │ │ │ GET doc │ │ │ │◀────────── etag:v7 ──────│ │ │ │ │ │ │ │ PUT etag:v7 │ │ │ │ │──────▶ (accepted, now v8) │ │ │ │ │ PUT etag:v7 │ │ │ │ │◀──── 409 Conflict ───────│ │ │ │ │ │ │ │ │ B re-reads, sees v8, │ │ │ │ resolves, retries │ └─────────────────────────────────────────────────┘
The conflict response does not just reject the write: it returns the current document state plus a structured diff between the editor's stale version and the current one. Editor B sees what Editor A changed, can resolve the conflict explicitly, and retries with the updated ETag.
This is more friction than last-write-wins, and deliberately so: a little resistance at the right moment is what prevents data loss. In practice it costs one extra round-trip when a conflict actually happens, and in exchange no edit ever silently disappears.
ETag locking applies to drafts. Submitted documents are frozen, no edits during review. Approved documents are closed to edits entirely; the next version is a new draft branched from the approved state.
4. Обязательная причина отклонения: форма и есть политика
Процесс рецензирования полезен ровно настолько, насколько устроено отклонение. Если «отклонить» — это одна кнопка без обязательного ввода, рецензенты либо перестают отклонять (потому что автор не получает сигнала), либо отклоняют без объяснений (что приучает авторов игнорировать отклонения). Ни то, ни другое не улучшает документы.
Решение структурное: отклонение требует причины. API отказывается записать отклонение без непустого обоснования. Интерфейс выводит поле причины как основное действие в форме отклонения, а не как второстепенное.
┌──────────────────────────────────────────────┐ │ Reject submission │ │ │ │ Document: incident-response-runbook v8 │ │ │ │ Category: ▼ [Required] │ │ ○ Incorrect technical detail │ │ ○ Missing required section │ │ ○ Conflicts with existing policy │ │ ○ Not aligned with current release │ │ ○ Other (requires explanation) │ │ │ │ Specific feedback: [Required] │ │ ┌─────────────────────────────────────────┐ │ │ │ Step 4 references the old isolation API. │ │ │ │ Update to use the unified action API per │ │ │ │ ARCH-2026-014. │ │ │ └─────────────────────────────────────────┘ │ │ │ │ References (optional): [link to spec/ticket] │ │ │ │ [Cancel] [Reject submission]│ └──────────────────────────────────────────────┘
Два решения в этой форме важны сверх очевидного.
Во-первых, категории структурированы, но расширяемы. Отчёт «почему в этой команде в этом квартале отклоняют документы» становится одним агрегирующим запросом. Проявляются закономерности: если 60% отклонений за квартал — «отсутствует обязательный раздел», дорабатывать нужно шаблон, а не авторов.
Во-вторых, само отклонение становится частью истории документа. Авторы следующего черновика видят прошлые отклонения и их причины прямо в контексте. Институциональная память накапливается вместе с документом, а не растворяется в чате.
5. Просмотр различий: семантика вместо текстового шума
Сравнение двух версий документа — это то, на что рецензент реально тратит внимание. Текстовые различия (построчные, как по умолчанию в любой системе контроля версий) для прозы шумны. Переформатирование абзаца даёт сотни изменённых строк без единого смыслового изменения.
Просмотр различий для управляемых документов должен работать на уровне блоков: добавлен ли этот заголовок, переписан ли этот абзац, изменён ли порядок в списке, правилась ли таблица. Рецензент может развернуть текстовое сравнение там, где нужно вчитаться, но представление по умолчанию показывает изменения с той детализацией, которая важна для решения об утверждении.
┌────────────────────────────────────────────────────────┐ │ Diff: runbook v7 → v8 (submitted) │ │ │ │ ▼ Section 3.2 "Initial Triage" - modified │ │ Paragraph 2 rewritten [expand] │ │ │ │ ▼ Section 4 "Containment" - modified │ │ Step 4: old "call /isolate endpoint" │ │ new "submit isolate action" │ │ │ │ ▶ Section 5 "Notification" - unchanged │ │ ▶ Section 6 "Audit" - unchanged │ │ │ │ + Section 7 "Post-incident review" - added │ │ │ │ [Approve] [Reject with reason] │ └────────────────────────────────────────────────────────┘
Представление показывает и правки метаданных: смену владельца, изменение тегов, смену классификации. Они нередко весомее изменений содержания (документ «Public», тихо переклассифицированный в «Internal», может сломать смежную автоматизацию) и никогда не должны быть невидимы рецензенту.
6. Append-only versioning: no in-place edits, period
Every approved version is stored as a write-once record. The storage layer is append-only at the API; there is no "edit a previous version" operation, no admin override, no "replace this version with a corrected one" affordance.
If a previously-approved version turns out to be wrong, the fix is to author a new version, take it through review, and approve it. The wrong version remains in the history as a record of what was approved at that time. This is uncomfortable for some organizations and load-bearing for the rest of the value of the system: if previous versions can be silently rewritten, the history is not evidence.
This is enforced two ways. At the data layer, each approved version is content-addressed by a hash of its content plus metadata. At the storage layer, the records are write-once. A modification attempt produces a new record with a new hash; the prior record is unchanged.
The version chain itself is hashed forward: each version's record includes the hash of the previous approved version. The chain can be verified end-to-end at any time. Tampering with any individual version breaks the chain, and the break is detectable.
7. The audit record that falls out of the workflow
The combination of explicit states, mandatory rejection reasons, ETag-locked edits, and append-only versioning produces an audit record as a side effect rather than as a separate concern.
For any document, at any past point in time, the system can answer:
- What was the approved version of this document on date X?
- Who submitted version N and at what time?
- Who reviewed it, when, and with what decision?
- If rejected, what was the categorized reason and the specific feedback?
- What metadata fields changed in this version compared to the prior one?
- Does the version chain verify cleanly?
These are the questions an auditor asks. They are also the questions an incident responder asks ("what did the runbook say when this incident started?") and the questions a release manager asks ("what policy was in force when this change was approved?").
The same record satisfies all three. There is no separate "compliance export" that has to be reconciled against the live system; the workflow is the audit trail.
8. What to ask a knowledge platform before betting on it
If you are evaluating a knowledge-management platform for governed documentation, the questions worth asking are these.
Is the approved version a distinct state from the latest edit, or are they the same thing? If they are the same thing, the platform is a wiki with a comment field, not a review platform.
If two reviewers approve different submitted versions of the same document at the same time, what happens? The honest answer involves ETag locking on submissions too, not just on edits.
Can a rejection be recorded without a reason? If yes, the rejection record is decorative.
Can a previously approved version be modified or deleted? If yes, the history is not audit-grade.
Show me the chain verification. If there is no chain verification, there is no cryptographic anchor; tampering is undetectable.
How a platform handles these questions tells you whether it has done the governance work or just done the wiki work and called it governance.
Closing
Governed documentation has to be designed for from the start, not bolted onto a wiki after the fact. That means explicit states, locked edits, mandatory rejection rationales, semantic diffs, append-only versions, and a verifiable chain.
The mechanics above are not exotic. They are unfussy applications of well-understood patterns: optimistic concurrency, append-only storage, hash-chained verification, structured forms as policy enforcement. The investment is in insisting on them across the platform, not in inventing anything new.
The next post in this series turns to code-analysis review: SAST results that arrive on every commit and have to be reviewed, suppressed, or fixed under the same governance discipline as the documents above.
Further reading
- RFC 7232, HTTP Conditional Requests (the canonical ETag spec)
- ISO/IEC 27001:2022, Annex A.5 (Documented information)
- Martin Kleppmann, Designing Data-Intensive Applications, chapter on concurrency control
корпоративную инфраструктуру?
Запишитесь на технический брифинг. Без продаж, только архитекторы и ваша команда.