Skip to content

ADR-004: Incremental Frontend Modernization (No Big Rewrite)

Status

Proposed

Date

2026-03-19

Context

The Nextpoint frontend is a dual-pipeline Rails application:

  • 914 ERB templates (~97% of pages) — server-rendered, working, low-risk
  • 156 jQuery/AG Grid files (Sprockets) — document grid, coding panel, chronology, markups
  • 105 React JSX files (Webpacker 5) — NGE-specific: document viewer, batch details, imports

The codebase has several modernization pressures:

  1. Webpacker 5 / Webpack 4 are EOL — no security patches, blocks Node upgrades
  2. No TypeScript — 105 JSX files with only prop-types for type safety; the two largest components (DocumentToolbar.jsx at 74KB, NgeDocumentViewPdf.jsx at 52KB) have complex state and NGE/Legacy branching with no compile-time safety
  3. No frontend tests — zero Jest/RTL; regressions caught manually
  4. Two AG Grid versions — Enterprise vendored in Sprockets AND AG Grid React via npm
  5. Global state via window.NP — jQuery and React share state through window globals

The team is concerned about a large frontend modernization effort. A "rewrite to React SPA" would be high-risk, high-effort, and zero user value. Most eDiscovery platforms (Relativity, Everlaw, Logikcull) are moving to modern frontends, but the key insight is that server-rendered HTML is fine for 90% of Nextpoint's pages.

Decision

Adopt an incremental modernization strategy with zero dedicated rewrite sprints.

Phase 0: Foundation (1-2 sprints, one engineer)

Replace the build toolchain without changing any application code:

  1. Replace Webpacker 5 with jsbundling-rails (esbuild)
  2. Webpacker/Webpack are dead; Rails 7 officially recommends jsbundling-rails
  3. esbuild builds in <1 second vs Webpack's 30+ seconds
  4. This is a build-tool swap — no React component code changes required
  5. Keep Sprockets for legacy JS (it's fine for jQuery/AG Grid vendored files)

  6. Add TypeScript configuration

  7. tsconfig.json with allowJs: true and strict: false initially
  8. Zero files need to change on day 1
  9. New files written as .tsx, old files stay .jsx
  10. Rename .jsx.tsx when you touch a file for other reasons

Phase 1: Safety Net (ongoing, no extra sprints)

Apply the Boy Scout Rule — leave code better than you found it:

  • When fixing a bug in a React component → add a test for that specific bug
  • When adding a feature to a React component → add TypeScript to that file first
  • When touching a CSS file → no changes needed (plain CSS works)
  • No dedicated "add tests" or "add TypeScript" sprints — this happens naturally

Phase 2: New Features in React (ongoing, natural)

  • Any new interactive feature → write it as a React component
  • Mount React inside ERB via react_component helper (already works)
  • ERB pages stay ERB — they're fine for server-rendered content
  • jQuery pages stay jQuery — don't touch working code

Phase 3: Convert on Contact (opportunistic, no deadline)

When a jQuery page needs significant rework for feature reasons, convert it to React:

  1. Coding panel — heavy state, frequent feature requests, most developer friction
  2. Import wizard — already partially React, completing the conversion is natural
  3. Export configuration — forms + validation, benefits from React state management
  4. Document grid — LAST priority (huge, working, AG Grid handles it well)

What We Explicitly Do NOT Do

Avoid Reason
Rewrite all jQuery to React Massive effort, zero user value, high regression risk
Add Redux/Zustand/MobX Overkill — React Context suffices for our component tree
Adopt Next.js/Remix/SPA architecture Would require building an API layer that doesn't exist
Create a shared component library upfront Build as you go, extract when patterns emerge (≥3 uses)
Set a deadline for "frontend modernization" This is a permanent engineering practice, not a project
Rewrite ERB to React Server-rendered HTML is fast, simple, and correct for 90% of pages

Guidelines for When to Use What

Use ERB when:
  - Page is mostly static content (settings, admin, case management)
  - Page has simple forms (standard Rails form helpers work)
  - Page is read-only or has simple CRUD

Use React when:
  - Rich client-side interactivity (drag/drop, real-time updates)
  - Complex form state (multi-step wizards, conditional fields)
  - WebSocket-driven updates
  - Heavy data visualization
  - Components shared across multiple pages

Keep jQuery when:
  - It works and nobody is actively changing it
  - The page isn't getting new features
  - Conversion effort exceeds maintenance cost

Consequences

Positive

  • Zero-risk foundation — Phase 0 is a build-tool swap, not a code rewrite
  • No dedicated sprints — modernization happens alongside normal feature work
  • TypeScript catches bugs incrementally — each converted file is immediately safer
  • Build time drops dramatically — esbuild <1s vs Webpack 30s+ improves DX
  • Team isn't blocked — anyone can write .jsx or .tsx, no forced migration
  • ERB pages untouched — 914 templates keep working without any changes

Negative

  • Mixed codebase persists — JSX and TSX coexist for months/years
  • Two pipelines remain — Sprockets (jQuery) + esbuild (React) until jQuery pages convert
  • No "before/after" milestone — harder to show management progress on a chart
  • Inconsistent patterns — some pages are React, some jQuery, some ERB-only

Risks

  • Phase 0 regression — Webpacker → esbuild swap could break edge cases in build. Mitigation: comprehensive manual QA of all React-mounted pages before deploy.
  • Team doesn't follow Boy Scout Rule — without enforcement, new code may skip TypeScript/tests. Mitigation: PR review checklist, CI lint for .tsx on new files.
  • AG Grid version drift — two versions can cause subtle behavior differences. Mitigation: Phase 3 includes grid consolidation when the document grid is touched.
Ask the Architecture ×

Ask questions about Nextpoint architecture, patterns, rules, or any module. Powered by Claude Opus 4.6.