Δ Diff Review Kit
Main review page

Current vs Proposed

A side-by-side engineering diff for replacing a synchronous publish path with a versioned, event-backed document pipeline. Scroll each section to satisfy the acceptance criteria.

1. Service boundary

Current implementation couples editor save, rendering, ACL validation, and notification in one request path.

Architecture

Current

Coupled
POST /documents/publish

editor_api
  ├─ validate_user()
  ├─ sanitize_html()
  ├─ render_preview()
  ├─ write_document()
  ├─ write_acl()
  ├─ send_email()
  └─ return link
!

RiskAny renderer or email failure blocks publish and creates ambiguous retry behavior.

Proposed

Isolated
POST /documents

document_api
  ├─ validate_user()
  ├─ write_version()
  ├─ emit DocumentVersionCreated
  └─ return pending link

workers
  ├─ render_worker
  ├─ acl_projector
  └─ notification_worker

AcceptancePublish path only persists canonical input and emits one idempotent event.

2. Versioning model

Move from mutable document rows to append-only versions with a stable public document ID.

Data model

Current

Mutable
documents
  id uuid primary key
  owner_id uuid
  slug text unique
  html text
  acl jsonb
  updated_at timestamptz
!

ProblemHard to audit what changed, which render belongs to which source, or what a reviewer saw.

Proposed

Append-only
documents
  id uuid primary key
  owner_id uuid
  slug text unique
  current_version_id uuid

document_versions
  id uuid primary key
  document_id uuid
  source_hash text
  content text
  created_at timestamptz

AcceptanceEvery publish creates a version row; previous versions remain readable by internal audit tools.

3. Idempotency and retries

The proposed flow must tolerate client retries, worker retries, queue redelivery, and partial downstream failure.

Reliability

Current

Implicit retry
client timeout
  → user clicks publish again
  → second document can be created
  → notification can send twice
  → render cache can point to stale html
!

ProblemDuplicate writes are possible because retry intent is not represented in the contract.

Proposed

Explicit key
POST /documents
Idempotency-Key: pub_01HV...

unique(owner_id, idempotency_key)

event_id = sha256(document_id + version_id)
worker writes use ON CONFLICT DO NOTHING

AcceptanceSame idempotency key returns the original document/version and does not re-enqueue side effects.

4. Access control

Separate public-read access from edit/comment permission checks and make ACL evaluation cacheable.

Security

Current

Runtime JSON check
acl = document.acl

if acl.mode == "public":
  allow_read()
if acl.mode == "domain":
  check_email_domain(user)
if acl.mode == "invite":
  query_invites(document_id, user)
!

ProblemRead path performs variable work per request and mixes link visibility with collaboration rights.

Proposed

Projected policy
document_access_projection
  document_id
  visibility: public | domain | invite
  allowed_domains text[]
  read_token_hash text
  comment_policy enum

edge cache key:
  slug + read_token_hash + visibility

AcceptanceAnonymous read checks use projected access state without loading the mutable document body.

5. Rendering pipeline

Rendering should be deterministic, tied to source hash, and safe to recompute.

Rendering

Current

Inline render
rendered = render(html)
cache.set(slug, rendered)
db.update({ html, rendered })
return rendered_url
!

ProblemRenderer latency directly affects API latency and cache invalidation is tied to slug, not content.

Proposed

Content-addressed
render_job
  version_id
  source_hash
  renderer_version

artifact_key =
  /rendered/{source_hash}/{renderer_version}.html

cache-control:
  immutable for version artifacts

AcceptanceRender artifacts are addressed by source hash and renderer version, not by slug alone.

6. Observability

Expose the request, event, job, and render artifact as one traceable chain.

Ops

Current

Log search
api logs:
  "publish failed"

worker logs:
  "render error"

no shared correlation id
no publish state machine
no user-facing job status
!

ProblemSupport cannot quickly answer whether a link is pending, failed, stale, or successfully rendered.

Proposed

Traceable
trace_id on:
  request
  document_version
  event
  render_job
  artifact

states:
  accepted → rendering → ready
  accepted → rendering → failed_retryable
  accepted → failed_terminal

AcceptanceEvery publish can be diagnosed from one trace ID and one version state field.

7. Rollout safety

Cut over reads and writes independently with feature flags, shadow renders, and reversible routing.

Release

Current

Big switch
deploy
  → all publishes use new path
  → all reads use new cache
  → rollback requires deploy revert
!

ProblemRead and write behavior changes at the same time, making faults harder to isolate.

Proposed

Flagged
flags:
  write_versioned_documents
  enqueue_render_jobs
  read_from_render_artifact
  enforce_projected_acl

stages:
  shadow → internal → 5% → 25% → 100%

AcceptanceRollback can disable proposed read path without losing newly written versions.