Files
q-blog/docs/Q-Blog_1.0.0_ROADMAP_implementation.md

15 KiB
Raw Permalink Blame History

TL;DR: Below is a phase-by-phase implementation guide: what libraries well use, what kinds of tests well write, what contracts/functions well define, and how well validate each step. Still high-level—no filenames or code dropped—yet detailed enough to steer day-to-day work.


Q-Blog → 1.0.0 Implementation Plan (Technical, High-Level)

Phase 0 — Orientation & Quality Bar

Libraries / tools

  • Issue labels & PR templates (tracker-native).
  • Metrics: Web Vitals (web-vitals), synthetic a11y checks (axe-core via CI). Artifacts
  • Quality charter: SLOs (e.g., “save post success ≥ 99.9%”), accessibility bar (keyboard-only paths pass), perf budgets (LCP/INP).
  • Protected journeys list: read post, create post, edit post, manage blogs. Validation
  • Signed-off charter; journeys ratified in a short doc.

Phase 1 — Project Docs & Working Agreements

Libraries / tools

  • Diagrams as code (Mermaid in docs renderer).
  • Conventional commits (tooling optional). Artifacts
  • Architecture map (UI ↔ state ↔ API), Testing pyramid, A11y standard, Contributing rules, Changelog categories.
  • Decision Record template (context → options → decision → impact). Validation
  • New contributor dry run: “can run + test + understand contracts in <30 min.”

Phase 2 — Test Harness & Linting Baseline

Libraries

  • Unit/Component: Vitest, @testing-library/react, @testing-library/user-event, @testing-library/jest-dom.
  • A11y lint/checks: eslint-plugin-jsx-a11y, axe-core or jest-axe.
  • Mocking: MSW (Mock Service Worker) for API.
  • Types: TypeScript strict mode.
  • Lint/format: eslint (flat config), @typescript-eslint plugin, prettier. Key setup
  • Global test setup (RTL matchers, MSW server, clock/timers).
  • Coverage thresholds (e.g., 80/70/70 lines/branches/functions to start).
  • CI lanes: install/cache → typecheck → lint → unit/component → (later) e2e smoke. Tests to seed
  • Smoke renders: home, post list, editor screen.
  • Tiny interaction: typing in editor, enabling/disabling toolbar control, save button disabled until valid. Validation
  • Green typecheck + lint + tests on clean checkout; coverage reports generated.

Phase 3 — Correctness Sweep

Libraries

  • RTK ecosystem: @reduxjs/toolkit (already present), RTK Query for data-fetching (adds caching, retries, invalidation).

  • Schema/runtime validation: Zod (form/io validation) and zod-to-json-schema for docs or AJV if we align with JSON Schema like Q-Chess. (Pick one; see “Contracts” below.) Contracts / functions

  • Fetch layer with cancellation & retries:

    • fetchJson<T>(req: Request, opts): Promise<Result<T>>
    • withTimeout(controller, ms): AbortSignal
  • Async state convention: { status: 'idle'|'loading'|'success'|'error', data?, error? }.

  • Error object: { code: string; message: string; recoverable: boolean }.

  • Validation:

    • For Zod: const PostDto = z.object({ id: z.string(), ... })parse(response).
    • For AJV: compile JSON Schemas once; validate per response. Work items
  • Remove legacy UI imports (v4 → v5), delete ts-nocheck sections by adding minimal types.

  • Replace any in reducers/selectors and editor props with discriminated unions & precise payloads.

  • Normalize empty/loading/error states for all remote views. Tests

  • Contract tests: MSW returns both valid and invalid payloads → validation errors surface as user-friendly messages.

  • Reducer tests: transitions for each thunk/query state. Validation

  • No suppressed type regions; “known issues” list empty; core routes never hard-crash on bad data.


Phase 4 — Accessibility Baseline

Libraries

  • A11y testing: jest-axe or axe-core integration; testing-library queries by role/name.
  • Focus management: rely on MUIs Dialog/Popover focus traps; add focus-trap only where needed. Contracts / behaviors
  • Landmarks: header/nav/main/footer; Skip link available and visible on focus.
  • Focus policy: focus moves to first interactive element on open; returns to invoker on close.
  • Names/roles/states: toggles expose aria-pressed; inputs have programmatic labels; errors are linked with aria-describedby.
  • Live regions: polite updates for “saving…/uploaded/failed”.
  • Motion/contrast: respect prefers-reduced-motion; enforce contrast tokens. Tests
  • Keyboard paths for protected journeys (Tab/Shift+Tab order, Escape close).
  • axe checks for key pages (no critical violations).
  • Live region announces long-running operations (assertions via toHaveAccessibleDescription or role queries). Validation
  • Keyboard-only success across protected journeys; automated checks pass with zero criticals.

Phase 5 — UX & IA Touch-up

Libraries

  • Router (current choice): ensure route guards & prefetch hooks supported.

  • Toasts/banners: lightweight (e.g., notistack or MUI Snackbar pattern). Contracts / functions

  • Design tokens: spacing, radius, typography, color roles (text, surface, brand) centralized.

  • Common UI patterns:

    • Empty state contract: { icon?, title, body, primaryAction?, secondaryAction? }
    • Error surface: banner + inline guidance; all errors have recovery action.
  • Editor ergonomics:

    • Toolbar state machine: computeToolbarState({ selection, schema }) → { boldEnabled, … }
    • Draft lifecycle: autosaveDraft(postId, content) → { savedAt } with debounce and “last saved” indicator. Tests
  • Token snapshots for theme roles; editor toolbar enable/disable based on selection.

  • Error/empty states render consistently across pages (table-driven tests). Validation

  • Navigation clarity (first-click discoverability); zero dead ends; consistent affordances.


Phase 6 — Multiple Blogs per Name

Libraries

  • State & data: continue RTK Query (cache per blog key).

  • Validation: same as Phase 3 (Zod/AJV). Domain model

  • Name (account) 1..N Blog; Post 1..1 Blog (immutable relationship post-creation).

  • Identifiers:

    • Blog handle: lowercase, slug rules; unique per Name; stored canonical form.
    • URL composition: /{name}/{blogHandle}/… (conceptual, router-agnostic). Functions / contracts
  • listBlogs(name): Promise<Blog[]>

  • createBlog(input: { handle; title; visibility }): Promise<Blog>

  • switchBlog(handle): void (updates app-scoped “current blog”)

  • listPosts(blogId, filters), createPost(blogId, input)

  • Migration: backfillDefaultBlogs(): MigrationResult (idempotent: skip if blog exists) Tests

  • Migration test with legacy dataset → posts appear under default blog.

  • Router/selector scoping: when switching blogs, lists & counts update without leaking data.

  • Handle validation: rejects collisions & invalid slugs; normalizes input. Validation

  • Users can create/switch blogs; all lists/summaries are scoped; legacy users retained seamlessly.


Phase 7 — Shared Blogs (Collaboration)

Libraries

  • Role/permission helpers: in-house constants; optional casl if we want DSL-like permissions (not required). Role model

  • Owner, Editor, Author, Viewer (implicit).

  • Operation matrix:

    • Owner: all
    • Editor: edit any post, manage drafts
    • Author: CRUD own posts Functions / contracts
  • Membership:

    • inviteMember(blogId, name, role): InviteToken
    • acceptInvite(token): Membership
    • removeMember(blogId, name): void
    • listMembers(blogId): Membership[]
  • Authorization guard:

    • can(op: 'create'|'edit'|'publish'|'manageMembers', ctx: { user, blog, post? }): boolean
  • Attribution:

    • Post metadata: { createdBy, updatedBy, updatedAt, revision }
  • Concurrency:

    • savePost(postId, input, { ifMatchRevision }): { revision } → 409 on stale; client shows resolve UI. Tests
  • Permission matrix table tests (unit): each role × operation result.

  • API guard tests via MSW: simulate forbidden responses, assert UI disables and surfaces denial.

  • Concurrency tests: stale write attempt returns conflict → user offered resolve path. Validation

  • Owner can manage access; editors/authors can work within role; no unauthorized writes pass.


Milestone Updates

  • v0.1.x — Multiblog foundations and quality baselines delivered.
  • v0.2.0 — Wiki Mode canonical selection shipped (owner/whitelist/blacklist; canonical dedupe across Names in feed, favorites, subscriptions, and post view). Settings cache added for efficient owner/settings resolution.

Phase 8 — Performance & Resilience

Libraries

  • react-virtuoso or equivalent (already used) for long lists.
  • Data layer retries/backoff: RTK Querys retry plugin or custom wrapper.
  • RUM metrics: web-vitals + lightweight sender. Functions / policies
  • Optimistic updates where safe (draft save, title edits) with rollback on failure.
  • Retry policy: GETs exponential backoff; POST/PUT guarded by idempotency keys where applicable.
  • Abortable fetch: timeouts and user-initiated cancel for uploads.
  • Prefetching: on-hover/visible prefetch of likely next routes and data keys. Tests
  • Latency injection with MSW to verify spinners/skeletons and cancel/abort behaviors.
  • Optimistic update rollback test: forced server failure restores previous UI state. Validation
  • Subjective feel: interactions seem instant; objective: Web Vitals within budget; error paths recover without data loss.

Phase 9 — Internationalization & Theming Consistency

Libraries

  • react-i18next (already used in sister projects).
  • Intl APIs for date/number/plural; fallback polyfills if needed. Functions / contracts
  • t('namespace:key', { vars }) usage with explicit, descriptive keys.
  • Currency/number/date formatters as wrapper utilities to standardize formatting.
  • Theme tokens as a single source of truth; contrast checks (scripted) against WCAG AA. Tests
  • Snapshot tests for key screens in both light/dark with token diffs.
  • Formatting tests for pluralization and number/date locales. Validation
  • Strings externalized; theme passes automated contrast check; RTL-safety spot check on core screens.

Phase 10 — Observability, Security & Release Readiness

Libraries

  • Error tracking: Sentry (or equivalent) with source maps.

  • Sanitization: DOMPurify for any HTML serialization/render of editor output.

  • CSP guidance (server-side; document the policy). Functions / policies

  • Error boundaries: route-level and editor-level with friendly fallback and “copy details”.

  • Client logging: logClientError({ code, message, context }) throttle; opt-in breadcrumbs (sanitized, no PII).

  • Security checks:

    • Validate all inputs client-side with Zod/AJV before sending.
    • Sanitize rich text on ingest and display; allowlist for marks/nodes.
    • Permission checks every write path; never trust client-side hide/disable alone.
  • Migrations:

    • Versioned, idempotent; dry-run flag; record migration status.
  • Release discipline:

    • Version bump + changelog; migration notes; upgrade guide; go/no-go checklist (green gates + budgets met). Tests
  • Error boundary renders fallback on thrown child component; telemetry is called with redacted payload.

  • XSS tests: paste malicious payload → sanitized output (no script execution) both save and render paths.

  • Migration dry-run/effects tests with sample datasets. Validation

  • Crashes captured with actionable context; no stored XSS vectors; release checklist green.


Cross-Cutting Standards

Contracts-first

  • Pick Zod (form/input + client response validation, type inference) or JSON Schema + AJV (cross-project parity with Q-Chess).

    • If Zod: generate JSON Schema for docs via zod-to-json-schema.
    • If AJV: generate TypeScript types via json-schema-to-ts.
  • Maintain a single roles/status/constants module imported across UI, data, and tests.

Data access layer

  • Prefer RTK Query for cache, memoized selectors, retries, and invalidation; treat queries/mutations as the one path to remote state.

State shaping

  • Store UI state distinct from server cache; selectors derive view models (small pure functions, unit-tested).

Security posture

  • Default-deny in permission checks; treat editor input as untrusted; output sanitize on every render path.

Testing pyramid (target distributions)

  • Unit (~60%): pure utils, selectors, reducers, permission guards.
  • Component (~30%): screens/components with RTL (+ axe).
  • Integration/E2E (~10%): happy-path create/edit/publish; blog switch; invite/accept.

CI

  • Node LTS matrix (current + previous).
  • Caching for package manager.
  • Artifacts: coverage report, axe report, Web Vitals synthetic (if applicable), build output for preview.

Example Function Signatures (illustrative, no filenames)

// Networking & validation
type Result<T> =
  | { ok: true; data: T }
  | { ok: false; error: { code: string; message: string; recoverable: boolean } };
function fetchJson<T>(
  input: RequestInfo,
  init?: RequestInit & { timeoutMs?: number },
): Promise<Result<T>>;

// Permissions
type Role = 'owner' | 'editor' | 'author';
function can(
  op: 'create' | 'edit' | 'publish' | 'manageMembers',
  ctx: { role: Role; userId: string; postOwnerId?: string },
): boolean;

// Blog management
function createBlog(input: {
  handle: string;
  title: string;
  visibility: 'public' | 'private';
}): Promise<Result<Blog>>;
function inviteMember(blogId: string, name: string, role: Role): Promise<Result<{ token: string }>>;
function savePost(
  postId: string,
  input: PostInput,
  opts: { ifMatchRevision: number },
): Promise<Result<{ revision: number }>>;

Completion Bars (what “done” looks like per category)

  • Correctness: zero suppressed type regions; known issues = empty; core flows never hard-crash.
  • Testability: green typecheck/lint/tests on clean checkout; coverage trend up and stable; fast local watch.
  • Accessibility: keyboard-only paths pass; axe shows no criticals; live region & focus rules verified.
  • Usability: consistent patterns for empty/error; editor ergonomics predictable; no dead ends.
  • Security: sanitized rich text; permission matrix enforced server- and client-side; no privilege escalation paths.
  • Performance: web vitals within budget; optimistic updates/abortable fetches; graceful failure/retry.

Assumptions & Risks: We can evolve API contracts to support multi-blog and roles; legacy data maps cleanly to default blogs; Slate output can be sanitized without losing required formatting; e2e scope remains minimal to avoid CI flake—expand only if signals require it.