Democracy.cc's video clipper lets users create clips from commission meeting videos. I built separate versions for desktop (canvas timeline + keyboard shortcuts) and mobile (touch handles). Different UI, but the logic underneath was copy-pasted -- 1,200+ lines of duplicate validation, API calls, and player controls.

The Duplication

Both modals had this exact 50-line validation block:

// Appeared identically in desktop (line 1561) and mobile (line 1667)
if (!title || title.trim() === '') {
    alert('Please enter a title for your clip.');
    return;
}
if (startTime >= endTime) {
    alert('End time must be after start time.');
    return;
}
let maxDuration = isPro ? 7200 : (isVerified ? 180 : 0);
if (duration > maxDuration) {
    alert(`Clip duration exceeds ${isPro ? '2 hours' : '3 minutes'}...`);
    return;
}

Any bug fix or feature change had to be applied twice. When I changed the PRO clip limit from 1 hour to 2 hours, I updated desktop, forgot mobile, and shipped inconsistent behavior.

The Fix

I extracted four shared utilities into video-clipper-shared.js:

// Both modals now use one line
const validation = ClipValidator.validateAll({
    title, startTime, endTime, isPro, isVerified, canCreateClips
});
if (!validation.valid) {
    alert(validation.error);
    return;
}

Same pattern for API calls -- both modals had ~40 lines of fetch boilerplate with identical error handling and status polling:

// Before: 40 lines of fetch + polling per modal
// After: clean shared client
const result = await VideoClipAPI.createClip(clipData);
VideoClipAPI.pollClipStatus(result.clip_id, onProgress, onComplete, onError);

Results

Feature Before (desktop + mobile) After (shared)
Validation 100 lines x 2 40 lines
API calls 50 lines x 2 35 lines
Video controls 80 lines x 2 60 lines

The UI still differs by platform -- canvas vs touch, keyboard vs gesture. But the business logic is a single source of truth. Bug fixes apply to both automatically.