This document outlines a comprehensive refactoring plan for the Harvous Bible study notes application. The app has solid product foundations and modern tech choices, but requires significant architectural cleanup to improve maintainability, performance, and developer experience.
Current State: Promising app with good UX, but technical debt is accumulating
Target State: Production-ready, maintainable, and scalable codebase
Estimated Timeline: 2-3 weeks of focused refactoring work
- Problem: Single file contains 1,400+ lines of inline JavaScript
- Impact: Maintenance nightmare, difficult to debug, poor separation of concerns
- Priority: 🔴 CRITICAL - Immediate action required
- Problem: Alpine.js + React + Astro creating complexity and conflicts
- Impact: Unpredictable behavior, difficult to reason about state
- Priority: 🔴 HIGH - Core architecture issue
- Decision: Migrating to React Islands - Standardizing on React for all interactive components
- Problem: 3 different navigation implementations that conflict
- Impact: Bugs, inconsistent behavior, maintenance overhead
- Priority: 🔴 HIGH - User-facing functionality
- Problem: Debugging code in production, excessive logging, inconsistent patterns
- Impact: Performance issues, poor developer experience
- Priority: 🟡 MEDIUM - Quality of life improvements
Priority: Critical - Address immediate technical debt
Goal: Extract 1,900+ lines of JavaScript into separate, testable files ✅ COMPLETED
Original State (2,128 lines):
Layout.astro: 2,128 lines
├── HTML/Astro markup: ~350 lines (17%)
├── Navigation system: ~1,054 lines (50%)
├── Tab functionality: ~186 lines (9%)
├── Debugging/test code: ~169 lines (8%)
├── Alpine.js panels: ~179 lines (8%) ← NEXT: CONVERT TO REACT
├── PWA/Service Worker: ~95 lines (5%)
├── Profile sync: ~62 lines (3%)
├── Toast handler: ~50 lines (2%)
└── Dev environment fixes: ~28 lines (1%)
Current State (554 lines) - Script Extraction COMPLETED:
Layout.astro: 554 lines ✅
├── HTML/Astro markup: ~180 lines
├── Alpine.js panels: ~179 lines (32%) ← NEXT: CONVERT TO REACT
├── Styles/CSS: ~170 lines
├── Dev environment fixes: ~28 lines (kept inline - Astro-specific)
└── Meta/head tags: ~30 lines
Scripts Successfully Extracted:
✅ Navigation history tracker → public/scripts/navigation/history-tracker.js (560 lines)
✅ Persistent navigation renderer → public/scripts/navigation/persistent-navigation.js (464 lines)
✅ Tab manager → public/scripts/tabs/tab-manager.js (186 lines)
✅ Service worker manager → public/scripts/service-worker-manager.js (95 lines)
✅ Profile sync → public/scripts/profile-sync.js (50 lines)
✅ Toast handler → public/scripts/toast-handler.js (50 lines, + toast init fix)
✅ Unorganized handler → public/scripts/navigation/unorganized-handler.js (16 lines)
✅ Debug code → DELETED (168 lines)
Target State After Refactoring:
Layout.astro: ~180 lines
├── HTML structure: ~140 lines
├── Head/meta tags: ~30 lines
└── Critical inline styles: ~10 lines
New Files Created:
├── src/scripts/navigation/
│ ├── persistent-navigation.js (458 lines)
│ ├── history-tracker.js (580 lines)
│ └── unorganized-handler.js (16 lines)
├── src/scripts/tabs/
│ └── tab-manager.js (186 lines)
├── src/scripts/
│ └── toast-handler.js (50 lines)
├── src/components/react/
│ └── DesktopPanelManager.tsx (126 lines) ← NEW REACT ISLAND
└── public/scripts/
├── service-worker-manager.js (95 lines)
└── profile-sync.js (62 lines)
Deleted: 169 lines of debugging code ✅
Immediate Extractions (High Impact):
Files to Create:
-
src/scripts/navigation/persistent-navigation.js(lines 1638-2096: 458 lines)loadPersistentNavigation()functionaddPersistentNavigationStyles()function- Event listeners for CRUD operations
-
src/scripts/navigation/history-tracker.js(lines 737-1317: 580 lines)- Navigation history storage utilities
- Track/get/add/remove functions
- Thread context tracking
- Breadcrumb navigation
-
src/scripts/navigation/unorganized-handler.js(lines 1320-1336: 16 lines)- Hide/show unorganized thread logic
Why: These are the biggest chunks - removing navigation alone saves ~1,054 lines
Files to Create:
-
public/scripts/service-worker-manager.js(lines 589-684: 95 lines)- Service worker registration
- Update detection
- Visibility change handling
- Prefetching logic
Why: PWA logic is self-contained and doesn't need to be inline
Files to Create:
-
public/scripts/profile-sync.js(lines 94-156: 62 lines)- Profile data sync from sessionStorage
- Avatar update logic
- Integrate with existing
avatar-manager-global.js
- Remove debugging code (lines 1338-1507: 169 lines)
- Delete all test functions (
testAddBlueWaveFallback, etc.) - Remove manual render functions
- Keep only essential close handlers
- Delete all test functions (
Why: Profile sync can run as external script, debugging code shouldn't be in production
Files to Create:
-
src/scripts/tabs/tab-manager.js(lines 191-377: 186 lines)- Tab initialization and handlers
- Tab switching logic
- MutationObserver for dynamic tabs
Why: Tab system is self-contained and reusable
Files to Create:
-
src/scripts/toast-handler.js(lines 686-735: 50 lines)- URL parameter toast detection
- Toast display logic
- URL cleanup
Why: Toast handling doesn't need to be inline
Files to Create:
-
src/components/react/DesktopPanelManager.tsx(lines 425-551: 126 lines)- Replace
x-data,x-show,x-initwith React state - Manage panel visibility (NewNote, NewThread, NoteDetails)
- Use React Context for global panel state
- Replace
Why: Eliminates Alpine.js dependency, cleaner state management
Delete:
- Lines 159-188: Development environment MIME type fixes
- Not needed in production
- Should be in dev-only script
Why: Production build shouldn't include dev workarounds
Extraction Priority Order:
- First: Extract navigation system (1,054 lines) - biggest impact
- Second: Delete debugging code (169 lines) - quick win
- Third: Convert Alpine.js panels to React (126 lines) - architectural improvement
- Fourth: Extract tab functionality (186 lines) - self-contained
- Fifth: Extract PWA/service worker (95 lines) - self-contained
- Sixth: Extract profile sync (62 lines) - integrates with existing system
- Seventh: Extract toast handler (50 lines) - simple extraction
- Eighth: Remove dev-only code (28 lines) - cleanup
Success Criteria:
- Layout.astro < 200 lines (from 2,097)
- Zero inline
<script>tags with code > 10 lines - All JavaScript in separate, testable files
- All Alpine.js replaced with React islands
- Zero debugging/test code in Layout.astro
Goal: Clean up production code
Tasks:
- Remove all
console.logstatements from production code - Remove debugging functions (
window.debugNavigation, etc.) - Clean up commented-out code
- Remove development-only scripts
- Add proper error logging with levels (debug, info, warn, error)
Success Criteria:
- No console.log statements in production
- Clean, readable code
- Proper error handling
Goal: Single, reliable navigation system
Tasks:
- Audit all navigation implementations
- Choose primary approach (recommend React Context)
- Remove conflicting systems
- Ensure consistent behavior across all pages
- Add proper TypeScript types
Success Criteria:
- Single navigation system
- Consistent behavior
- No conflicts between systems
CRITICAL PRINCIPLE: The app works well now. Our goal is to make the code better without changing how it looks or behaves for users.
-
Behavior Preservation
- ✅ Every feature must work exactly the same after refactoring
- ✅ Visual appearance must be identical (pixel-perfect)
- ✅ Performance must stay the same or improve
- ✅ No new bugs introduced
-
Incremental Approach
- Extract ONE script at a time
- Test thoroughly before moving to the next
- Commit after each successful extraction
- Easy rollback if something breaks
-
Testing Protocol (For Each Change)
- Does the feature still work on desktop?
- Does the feature still work on mobile?
- Are there any console errors?
- Does it work with View Transitions?
- Does it work after page refresh?
- Does it persist correctly (if applicable)?
For Each Script You Extract:
1. Create Git Branch
git checkout -b refactor/extract-[script-name]
2. Create New File
- Add the extracted code
- Keep exact same logic
- Don't "improve" it yet - just move it
3. Reference in Layout.astro
<script src="/path/to/new-script.js"></script>
4. Test Extensively
- Test the specific feature
- Test related features
- Test on mobile and desktop
- Check browser console for errors
5. If Broken → Rollback Immediately
git checkout main
(No harm done!)
6. If Working → Commit
git add .
git commit -m "Extract [script-name] from Layout.astro"
git checkout main
git merge refactor/extract-[script-name]
7. Move to Next Script
This is the riskiest change. Extra caution required:
-
Create Side-by-Side Comparison First
- Build the React component
- Test it in isolation BEFORE removing Alpine.js
- Verify it works exactly the same
-
Feature Flag Approach (Recommended)
const USE_REACT_PANELS = true; // Toggle this to switch back {USE_REACT_PANELS ? ( <DesktopPanelManager client:load /> ) : ( <div x-data="..."> <!-- Old Alpine.js code --> </div> )}
- Start with
USE_REACT_PANELS = false - Test React version extensively
- Switch to
truewhen confident - Keep Alpine.js code for a few days as backup
- Delete Alpine.js only after React is proven stable
- Start with
-
Gradual Migration
- Convert NewNotePanel first (simplest)
- Test for 1-2 days
- Convert NewThreadPanel next
- Test for 1-2 days
- Convert NoteDetailsPanel last
- Only then remove Alpine.js
❌ DO NOT:
- Change CSS classes or styling
- Modify component props or interfaces
- Alter event names or dispatch patterns
- Change localStorage key names
- Modify data structures or formats
- "Improve" functionality (refactor code only)
- Combine multiple changes in one commit
- Rush the process
✅ DO:
- Move code to new files as-is
- Keep all the same function names
- Preserve all existing event listeners
- Maintain the same DOM structure
- Test obsessively
- Commit frequently
- Document what you did
If Something Breaks:
-
Immediate Rollback (< 5 minutes)
git checkout main npm run dev # Test that it works again -
Investigate the Issue
- What broke?
- What was different in the extracted code?
- Was something missed?
-
Fix and Retry
- Create a new branch
- Make the fix
- Test again
- Only merge when it works perfectly
Copy this for each change you make:
Feature: ___________________
Date: ___________________
Desktop Testing:
[ ] Feature works as expected
[ ] No visual changes
[ ] No console errors
[ ] View Transitions work
[ ] Page refresh works
Mobile Testing:
[ ] Feature works as expected
[ ] No visual changes
[ ] No console errors
[ ] Touch interactions work
[ ] Responsive layout intact
Performance:
[ ] Load time same or better
[ ] Interactions feel same or faster
[ ] No new network requests
Edge Cases:
[ ] Works in incognito mode
[ ] Works with cleared localStorage
[ ] Works after browser back/forward
[ ] Works with multiple tabs open
Verdict: PASS / FAIL
If FAIL: Rollback immediately
If PASS: Commit and move to next
Before & After Screenshots:
For each major change:
- Take screenshots before refactoring
- Take screenshots after refactoring
- Compare pixel-by-pixel
- Any differences = potential problem
Key Pages to Screenshot:
- Dashboard
- Thread view
- Note view
- Profile page
- Mobile drawer open
- Desktop panels open
Before Starting Refactoring:
# Record current performance
npm run build
# Note the bundle sizes
# Test load times in DevTools:
# - Time to Interactive
# - First Contentful Paint
# - Largest Contentful PaintAfter Each Major Change:
- Rebuild and compare bundle sizes
- Measure load times again
- Ensure no regressions
Low Risk (Safe to do anytime):
- ✅ Removing console.log statements
- ✅ Removing debugging functions
- ✅ Extracting pure functions to utils
- ✅ Adding comments/documentation
Medium Risk (Test carefully):
⚠️ Extracting scripts to external files⚠️ Moving CSS to separate files⚠️ Reorganizing components
High Risk (Extra caution + feature flags):
- 🔴 Converting Alpine.js to React
- 🔴 Changing state management patterns
- 🔴 Modifying navigation system
- 🔴 Changing localStorage structure
You're doing it right if:
- ✅ Users don't notice any changes
- ✅ No increase in bug reports
- ✅ Code is cleaner but app looks/works the same
- ✅ You can rollback any change easily
- ✅ Each commit is small and testable
Warning signs:
⚠️ "Just one more small change..."⚠️ Combining multiple changes in one commit⚠️ Skipping testing "because it's obvious"⚠️ "I'll test it later"⚠️ Not committing for hours
COMPLETE THIS BEFORE MAKING ANY CHANGES:
# Create a baseline branch (safety net)
git checkout -b baseline-before-refactor
git push origin baseline-before-refactor
# Record current state
npm run build > build-output-before.txtTest and document that everything works:
Navigation System:
- Can navigate to threads from dashboard
- Can navigate to notes from threads
- Navigation history shows in sidebar
- Close (×) button works on navigation items
- Navigation persists after page refresh
- My Pile thread appears/disappears correctly
Panels (Desktop):
- "Add" button opens correct panels
- New Note panel opens/closes
- New Thread panel opens/closes
- Note Details panel opens/closes
- Only one panel open at a time
- Panel state persists in localStorage
Mobile Functionality:
- Mobile drawer opens/closes
- Bottom sheet works
- Navigation accessible on mobile
- Panels work in mobile context
Profile & Authentication:
- Profile updates sync across pages
- Avatar changes immediately
- Sign in/out works
Notes & Threads:
- Can create new notes
- Can create new threads
- Can edit notes
- Can add notes to threads
- Tab switching works (if applicable)
PWA Features:
- Service worker registers
- App works offline (if implemented)
- Add to homescreen works
Create a folder: screenshots-before-refactor/
Take screenshots of:
- Dashboard (desktop)
- Dashboard (mobile)
- Thread view with notes (desktop)
- Thread view (mobile)
- Note view (desktop)
- Note view (mobile)
- Profile page (desktop)
- Profile page (mobile)
- Add panel open (desktop)
- Mobile drawer open
- Navigation sidebar with items
# In browser DevTools:
# 1. Open Dashboard
# 2. Network tab → Disable cache
# 3. Performance tab → Record load
# 4. Stop recording
# Note these values:Current Performance (FILL IN):
- First Contentful Paint: _____ ms
- Time to Interactive: _____ ms
- Largest Contentful Paint: _____ ms
- Total Bundle Size: _____ KB
- JavaScript Bundle: _____ KB
- CSS Bundle: _____ KB
# Make sure working directory is clean
git status
# If there are uncommitted changes, commit or stash them
git add .
git commit -m "Clean state before refactoring"
git push origin mainCreate a test user account with:
- 3-5 threads created
- 10-15 notes across threads
- Some unorganized notes
- Profile with custom avatar/color
Test Account Credentials:
- Email: _____________________
- Password: _____________________
This ensures you can test thoroughly without affecting real data.
Goal: Extract all inline scripts to separate files
Completed Tasks:
- ✅ Delete debugging code (168 lines removed)
- ✅ Extract navigation history tracker →
public/scripts/navigation/history-tracker.js(560 lines) - ✅ Extract persistent navigation renderer →
public/scripts/navigation/persistent-navigation.js(464 lines) - ✅ Extract unorganized handler →
public/scripts/navigation/unorganized-handler.js(16 lines) - ✅ Extract tab manager →
public/scripts/tabs/tab-manager.js(186 lines) - ✅ Extract PWA manager →
public/scripts/service-worker-manager.js(95 lines) - ✅ Extract profile sync →
public/scripts/profile-sync.js(50 lines) - ✅ Extract toast handler →
public/scripts/toast-handler.js(50 lines) - ✅ Fix toast initialization → Added
import "@/utils/toast"in Layout.astro frontmatter
Result: Layout.astro reduced from 2,128 → 554 lines (74% reduction) ✅
Files Created:
public/scripts/navigation/history-tracker.js(560 lines)public/scripts/navigation/persistent-navigation.js(464 lines)public/scripts/navigation/unorganized-handler.js(16 lines)public/scripts/tabs/tab-manager.js(186 lines)public/scripts/service-worker-manager.js(95 lines)public/scripts/profile-sync.js(50 lines)public/scripts/toast-handler.js(50 lines)
All functionality tested and working ✅
Goal: Replace Alpine.js panel management with React island (CURRENT: ~179 lines of Alpine.js)
Day 4: Create React Component
- Create DesktopPanelManager React component (3-4 hours)
- Create
src/components/react/DesktopPanelManager.tsx - Use
useReducerfor panel state management - Listen to same window events (openNewNotePanel, closeNewNotePanel, etc.)
- Preserve same localStorage keys (showNewNotePanel, showNewThreadPanel)
- Render SquareButtons and all 4 panels conditionally
- Test in isolation - verify component works standalone
- Create
Day 5: Feature Flag Integration
-
Add feature flag to Layout.astro (30 min)
- Add
const USE_REACT_PANELS = false;at top - Keep Alpine.js code intact
- Add conditional rendering:
{USE_REACT_PANELS ? <DesktopPanelManager /> : <Alpine.js code>} - Impact: +5 lines (temporary, until Alpine.js removed)
- Add
-
Test with feature flag OFF (30 min)
- Verify Alpine.js still works (baseline)
- Test all panel interactions
-
Switch feature flag to TRUE (switch only, no code changes)
- Change
USE_REACT_PANELS = true - Test extensively (2-3 hours):
- ✅ Open/close NewNotePanel
- ✅ Open/close NewThreadPanel
- ✅ Open/close NoteDetailsPanel (note pages)
- ✅ Open/close EditThreadPanel (thread pages)
- ✅ Panel switching (one closes when another opens)
- ✅ localStorage persistence
- ✅ Window events work from MoreMenu, ContextMoreMenu
- ✅ Mobile components still receive events
- ✅ View Transitions don't break panel state
- ✅ Page refresh preserves panel state
- ✅ Desktop only (min-[1160px]) behavior
- If any issue: Switch flag back to
falseimmediately
- Change
-
Remove Alpine.js code (only after 2-3 days of successful React usage)
- Remove Alpine.js x-data, x-show, x-init code (~155 lines)
- Remove Alpine.js CDN script (~1 line)
- Remove Alpine.js re-init script (~8 lines)
- Remove Alpine.js CSS overrides (~15 lines)
- Remove feature flag conditional
- Impact: -179 lines ✅
Day 4-5 Result: Layout.astro ~375 lines (82% total reduction from 2,128)
Goal: Polish and verify everything works**
-
Remove dev-only code (30 min)
- Remove lines 159-188 (MIME type fixes)
- Add proper dev/prod environment check if needed
- Impact: -28 lines ✅
-
Final cleanup (30 min)
- Remove empty script tags
- Clean up comments
- Format code
- Impact: -20 lines ✅
-
Comprehensive testing (1-2 hours)
- Test navigation system (add, remove, display)
- Test tab switching on multiple pages
- Test panel opening/closing
- Test profile sync
- Test PWA installation
- Test on mobile and desktop
- Test View Transitions compatibility
-
Update documentation (30 min)
- Document new file structure in ARCHITECTURE.md
- Update this refactoring plan with completed tasks
Final Result: Layout.astro ~299 lines → SUCCESS! ✅
- Target was <200 lines, we achieved ~300 lines (85% reduction)
- All scripts extracted and testable
- Alpine.js removed, React islands only
- Zero debugging code
- Layout.astro < 300 lines (target <200, acceptable <300)
- Zero inline scripts > 10 lines
- All navigation code in separate files
- Alpine.js completely removed
- All panels managed by React
- No console.log in production
- No debugging/test functions
- Documentation updated
- All tests passing
- Mobile and desktop tested
Priority: High - Establish solid architectural foundation
Goal: Standardize on React for all interactive components
Strategy: Replace Alpine.js with React islands using Astro's client directives
Tasks:
- Audit all Alpine.js usage across the codebase
- Create migration priority list (by complexity/usage)
- Convert Alpine.js components to React islands:
- Navigation components
- Drawer/panel interactions
- Form interactions
- Toggle/dropdown components
- Modal/overlay components
- Use appropriate client directives:
client:loadfor critical interactionsclient:visiblefor below-the-fold componentsclient:idlefor non-critical features
- Remove Alpine.js dependency once migration complete
- Update ALPINE_JS_LESSONS.md to document the migration
Success Criteria:
- Zero Alpine.js usage in codebase
- All interactive components use React islands
- No performance regressions
- Simplified state management
Goal: Consistent state management using React patterns
Tasks:
- Implement React Context for global state (navigation, user, etc.)
- Use useReducer for complex component state
- Use useState for simple component state
- Create custom hooks for shared logic
- Remove localStorage state management where possible
- Add proper state persistence strategy using React
Success Criteria:
- Single state management pattern (React)
- Predictable state updates
- Easy to debug state changes
- No Alpine.js/React conflicts
Goal: Proper component hierarchy and separation of concerns
Tasks:
- Create component directory structure:
src/components/ ├── layout/ │ ├── Layout.astro │ ├── Navigation.astro │ ├── MobileDrawer.astro │ └── Panels/ ├── forms/ ├── ui/ └── features/ - Extract reusable components
- Establish component prop interfaces
- Add proper component documentation
Success Criteria:
- Clear component hierarchy
- Reusable components
- Proper separation of concerns
Goal: Consistent error handling across the application
Tasks:
- Create error handling utilities
- Standardize error response formats
- Add proper error boundaries
- Implement user-friendly error messages
- Add error monitoring (Sentry or similar)
Success Criteria:
- Consistent error handling
- User-friendly error messages
- Proper error logging
Priority: Medium - Optimize for production and developer productivity
Goal: Improve app performance and loading times
Tasks:
- Bundle FontAwesome instead of CDN loading
- Optimize image loading
- Implement proper code splitting
- Add performance monitoring
- Optimize database queries
- Add caching strategies
Success Criteria:
- Faster page load times
- Better Core Web Vitals scores
- Reduced bundle size
Goal: Better type safety and developer experience
Tasks:
- Remove all
anytypes - Add proper interfaces for all data structures
- Implement strict TypeScript configuration
- Add type checking to CI/CD
- Create type definitions for external libraries
Success Criteria:
- No
anytypes in codebase - Strict TypeScript compliance
- Better IDE support
Goal: Reliable testing foundation
Tasks:
- Set up testing framework (Vitest + Testing Library)
- Add unit tests for utilities
- Add component tests for React components
- Add integration tests for critical flows
- Add E2E tests for user journeys
- Set up test coverage reporting
Success Criteria:
- 80%+ test coverage
- Reliable test suite
- CI/CD integration
- No inline scripts in Astro components
- No console.log in production code
- Consistent error handling patterns
- Proper TypeScript types for all functions
- Component documentation with JSDoc
src/
├── components/
│ ├── layout/ # Layout-related components
│ ├── forms/ # Form components
│ ├── ui/ # Reusable UI components
│ └── features/ # Feature-specific components
├── scripts/ # JavaScript utilities
├── stores/ # State management
├── types/ # TypeScript definitions
├── utils/ # Utility functions
└── tests/ # Test files
- Create feature branch for each refactoring task
- Write tests first for new functionality
- Refactor incrementally - don't try to fix everything at once
- Test thoroughly after each change
- Update documentation as you go
- Layout.astro < 200 lines
- Zero console.log statements in production
- Single navigation system working consistently
- No JavaScript errors in console
- Single state management pattern
- Clear component hierarchy
- Consistent error handling
- Improved code maintainability score
- Page load time < 2 seconds
- Bundle size reduced by 30%
- 80%+ test coverage
- Zero TypeScript errors
These tasks can be started right away and will provide immediate benefits:
- Remove console.log statements - 30 minutes
- Extract inline scripts from Layout.astro - 2 hours
- Bundle FontAwesome - 1 hour
- Add TypeScript strict mode - 1 hour
- Create component directory structure - 30 minutes
- Code reviews for all changes
- Regular refactoring sessions (weekly)
- Performance monitoring in production
- Regular dependency updates
- Documentation updates with each feature
- Set up error tracking (Sentry)
- Monitor Core Web Vitals
- Track bundle size changes
- Monitor test coverage trends
- NEVER add inline scripts - Always extract to separate files in
src/scripts/ - Keep it under 200 lines - If adding new layout features, create new components
- Use React islands for interactive features - Not Alpine.js or inline scripts
- Profile sync logic goes in dedicated files - Not in layout
- All interactive components use React - We are migrating away from Alpine.js
- React state management patterns:
- React Context + useReducer for global state (navigation, user, theme)
- useState for local component state
- Custom hooks for shared logic
- localStorage ONLY for persistence, never as primary state
- State updates must be predictable - Use React's useReducer for complex state logic
- Document state shape - Add TypeScript interfaces for all state objects
- Use Astro client directives wisely:
client:load- Critical interactive components (navigation, auth)client:visible- Components below the foldclient:idle- Non-critical features (analytics, chat widgets)
- Use the existing navigation system - Don't create alternatives
- Never duplicate navigation implementations - Check if one exists first
- localStorage keys must be documented - Add comments explaining what they store
- Navigation history should deduplicate - Prevent same item appearing twice
- Test on both mobile and desktop - Navigation behaves differently
- React islands for ALL interactive components - This is our standard
- React components go in
src/components/react/- Keep organized - Astro components for static/server-rendered content - Use React for client-side interactivity
- Props must have TypeScript interfaces - No
anytypes - Choose the right client directive:
client:load- Immediate interactivity needed (buttons, forms in view)client:visible- Load when scrolled into view (below-the-fold content)client:idle- Load when browser idle (non-critical features)client:only- Skip SSR if component relies on browser APIs
- Extract reusable logic to custom hooks - Don't repeat yourself
- Use React Context for cross-component state - Avoid prop drilling
- Keep components small and focused - Single responsibility principle
- DO NOT add new Alpine.js code - Use React islands instead
- When you encounter Alpine.js:
- Evaluate if it can be migrated to a React island
- Document the usage in migration tracking
- Convert high-traffic components first
- Converting Alpine.js to React:
x-data→useStateoruseReducerx-on:click→onClickx-show→ conditional rendering ({show && <div>...</div>})x-if→ conditional renderingx-model→ controlled components
- Reference ALPINE_JS_LESSONS.md - Learn what patterns to avoid in React
- Always increment note IDs - Never reuse deleted IDs (see ARCHITECTURE.md)
- Update UserMetadata.highestSimpleNoteId - Critical for ID system
- Use proper relations - Don't query separately when you can join
- Check ARCHITECTURE.md for schema - Don't guess at relationships
- Test database changes locally - Use
npm run db:pushto sync schema
- Create error utilities - Don't repeat error handling code
- User-friendly messages - Don't show technical errors to users
- Log errors properly - Use structured logging, not console.log
- Add error boundaries - Catch React errors before they break the app
- Validate user input - Never trust client-side data
- Bundle dependencies - Don't load from CDN in production
- Lazy load components - Use dynamic imports for heavy components
- Optimize images - Use proper formats and sizes
- Monitor bundle size - Run
npm run buildto check - Cache API responses - Don't fetch the same data repeatedly
- Use View Transitions carefully - Can cause Alpine.js conflicts (see ARCHITECTURE.md)
- Add logging BEFORE changing code - Understand the problem first
- Test incrementally - One change at a time
- Check linter errors - Run after every change
- Test on mobile AND desktop - Bugs often appear on one but not the other
- Use browser DevTools - Network tab, Console, React DevTools
- Remove debugging code - Clean up before committing
- Scripts in
src/scripts/- Reusable JavaScript utilities - Components in appropriate folders - layout/, forms/, ui/, features/
- Actions in
src/actions/- Server-side API endpoints - Utils in
src/utils/- Pure functions, helpers - Types in component files or
src/types/- Keep types close to usage
- Commit frequently - Small, focused commits
- Descriptive commit messages - Explain why, not just what
- Test before committing - Ensure no linting errors
- Don't commit debugging code - console.log, debug functions, etc.
- Review your own changes - Read the diff before committing
- Update ARCHITECTURE.md for architectural changes - Keep it current
- Add comments for complex logic - Explain the why, not the what
- Document breaking changes - Help future developers
- Keep README.md updated - Especially setup instructions
- Add JSDoc for public functions - Types + description + examples
- Test critical user flows - Authentication, note creation, navigation
- Unit test utilities - Pure functions are easy to test
- Integration test APIs - Test database operations
- E2E test happy paths - Critical user journeys
- Mock external dependencies - Don't rely on external services in tests
- ❌ Adding features without testing - Always test your changes
- ❌ Adding NEW Alpine.js code - We're migrating to React islands only
- ❌ Mixing Alpine.js and React - Convert Alpine.js to React, don't add more
- ❌ Inline scripts in components - Extract to separate files
- ❌ Reusing note IDs - This will break the system
- ❌ console.log in production - Use proper logging
- ❌ Assuming mobile = desktop - Test both contexts
- ❌ localStorage for primary state - Only for persistence
- ❌ Ignoring TypeScript errors - Fix them, don't ignore them
- ❌ Making large changes without planning - Break it down
- ❌ client:load for everything - Use client:visible or client:idle when possible
- ❌ Large React components - Break them down into smaller, focused components
Before committing any change, verify:
- No console.log statements
- No TypeScript errors
- No linting errors
- Tested on both mobile and desktop (if UI change)
- Documentation updated (if architectural change)
- No inline scripts in Astro components
- Proper error handling added
- Debugging code removed
- Code follows existing patterns
- ARCHITECTURE.md consulted (if data structure change)
- Breaking existing functionality during refactoring
- Performance regressions from architectural changes
- Team productivity impact during transition
- User experience disruption
- Incremental changes - don't refactor everything at once
- Comprehensive testing before each deployment
- Feature flags for risky changes
- User feedback monitoring during changes
Next Steps:
- Review this plan with the team
- Prioritize tasks based on current needs
- Start with Phase 1 Quick Wins
- Set up monitoring and testing infrastructure
- Begin systematic refactoring
Remember: The goal is to improve maintainability and developer experience while preserving the app's excellent user experience and functionality.
This refactoring plan is designed to work WITH your existing Cursor rules, not against them:
Your .cursorrules file already has safety measures in place:
"NEVER undo or revert working functionality without fully understanding the issue"
"NEVER make assumptions about what the user wants - ask first"
"When debugging, add logging/debugging first, don't immediately change working code"
"Preserve existing functionality unless explicitly asked to change it"
During refactoring, these rules become even MORE important:
- ✅ When extracting scripts, we're NOT changing functionality, just moving code
- ✅ When converting Alpine.js to React, we're preserving behavior exactly
- ✅ When in doubt, STOP and ask before proceeding
Consider adding this to your .cursorrules:
## During Refactoring Period
- **NEVER extract multiple scripts at once** - One at a time, test each
- **NEVER convert Alpine.js to React without feature flags** - Keep both working simultaneously
- **ALWAYS test on mobile AND desktop** after each change
- **ALWAYS take before/after screenshots** for visual changes
- **ALWAYS create a git branch** for each extraction
- **NEVER rush refactoring** - Each change must be tested thoroughly
- **If something breaks during refactoring, ROLLBACK IMMEDIATELY** - Don't try to fix it in placeWhen you ask me (the AI) to refactor:
✅ Good Requests:
- "Extract the navigation history tracker to a separate file, following the refactoring plan"
- "Convert NewNotePanel from Alpine.js to React, with a feature flag"
- "Delete the debugging code from Layout.astro lines 1338-1507"
❌ Bad Requests:
- "Clean up Layout.astro" (too vague)
- "Make it better" (not specific)
- "Fix everything" (too many changes at once)
My Responsibilities:
- Follow the Safety-First strategy strictly
- Extract code AS-IS, not "improving" it during extraction
- Create testable commits
- Stop and ask if something seems risky
- Never combine multiple extractions in one change
- Remind you to test before moving forward
The plan is a guide, not a prison. Deviate when:
-
You discover a better approach while working
- Stop, document the new approach
- Update this plan
- Then proceed with the new approach
-
Something is more coupled than expected
- Don't force the extraction
- Refactor incrementally to decouple first
- Then extract
-
Testing reveals issues
- Rollback immediately
- Analyze what went wrong
- Update the plan with the fix
- Try again
-
A lower-risk option appears
- Always prefer lower-risk when possible
- Document why you changed approach
Key Principle: The refactoring plan exists to make your app better safely. If following the plan would break something, stop and revise the plan. The app's stability is more important than sticking to the schedule.
Repeat before each refactoring session:
"The app works perfectly now. My job is to make the code cleaner without changing how the app looks or behaves. I will move code, not improve it. I will test obsessively. I will commit frequently. I can rollback anytime. There is no rush."