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:
- Webpacker 5 / Webpack 4 are EOL — no security patches, blocks Node upgrades
- No TypeScript — 105 JSX files with only
prop-typesfor type safety; the two largest components (DocumentToolbar.jsxat 74KB,NgeDocumentViewPdf.jsxat 52KB) have complex state and NGE/Legacy branching with no compile-time safety - No frontend tests — zero Jest/RTL; regressions caught manually
- Two AG Grid versions — Enterprise vendored in Sprockets AND AG Grid React via npm
- 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:
- Replace Webpacker 5 with jsbundling-rails (esbuild)
- Webpacker/Webpack are dead; Rails 7 officially recommends jsbundling-rails
- esbuild builds in <1 second vs Webpack's 30+ seconds
- This is a build-tool swap — no React component code changes required
-
Keep Sprockets for legacy JS (it's fine for jQuery/AG Grid vendored files)
-
Add TypeScript configuration
tsconfig.jsonwithallowJs: trueandstrict: falseinitially- Zero files need to change on day 1
- New files written as
.tsx, old files stay.jsx - Rename
.jsx→.tsxwhen 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_componenthelper (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:
- Coding panel — heavy state, frequent feature requests, most developer friction
- Import wizard — already partially React, completing the conversion is natural
- Export configuration — forms + validation, benefits from React state management
- 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
.jsxor.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
.tsxon 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 questions about Nextpoint architecture, patterns, rules, or any module. Powered by Claude Opus 4.6.