Skip to content
Go back

Forking and Modernizing PointWith.me: 52% Smaller, Dramatically Faster

Updated: (Originally published 18 Oct, 2025 )

TL;DR: Forked Phil Palmieri’s planning poker app, modernized it with Vite and Tailwind CSS, achieving 52% bundle reduction (1,402 KB → 679 KB), 96% CSS reduction, dramatically faster builds, and improved UX. Live at pointwith.baker.is.

In November 2023, I discovered PointWith.me — a real-time planning poker application for distributed teams built by Phil Palmieri in August 2019. It was exactly what I needed: real-time Fibonacci voting, Firebase-powered collaboration, and a clean interface for sprint planning.

But it was showing its age.

The app was built with the tools of 2019: Create React App, Semantic UI, moment.js, and all the patterns of that era. I forked the project, started modernizing it, and submitted pull requests back to Phil’s original repo.

Life finds a way though, and Phil never responded. The project was effectively mothballed.

Rather than let a good tool languish, I decided to build out my own improved version. In October 2025, I gave it a comprehensive technical refresh — and the results exceeded my expectations.

The Starting Point

Phil’s Original Stack (August 2019):

The app worked well for its purpose — real-time Fibonacci voting, session management, vote statistics — but by 2023, the development experience had fallen behind current standards.

The Fork and First Modernization (November 2023)

When I initially forked the project, I focused on getting it up to modern React standards:

November 2023 Updates:

These changes brought the project into 2023 standards, but there was still more work to do. The build tooling (Create React App), UI framework (Semantic UI), and dependencies (moment.js) were all ripe for replacement.

The October 2025 Comprehensive Refresh

Phase 1: Build Tool Migration (October 17, 2025)

Create React App → Vite 7

The first major change was replacing Create React App with Vite. CRA has been effectively abandoned, and Vite represents the modern standard for React development.

Changes:

Results:

Migration gotchas:

Phase 2: Package Manager Migration (October 17, 2025)

npm → pnpm

Switched to pnpm for faster installs and better disk space efficiency.

Configuration:

# .npmrc
auto-install-peers=true
shamefully-hoist=false
strict-peer-dependencies=false

Benefits:

Phase 3: Date Library Modernization (October 18, 2025)

moment.js → date-fns

Moment.js has been deprecated for years. Time to move on.

Changes:

Bundle Impact:

Migration pattern:

// Before
import moment from "moment";
const formatted = moment(date).format("MMM DD, YYYY");

// After
import { format } from "date-fns";
const formatted = format(date, "MMM dd, yyyy");

The format string syntax is slightly different, but date-fns is actively maintained, fully tree-shakeable, and has no deprecated warnings.

Phase 4: UI Framework Migration (October 18, 2025)

Semantic UI → Tailwind CSS

This was the biggest change and yielded the most dramatic results.

The Problem with Semantic UI:

Migration Strategy:

Migrated all 14 components to Tailwind utility classes:

Icon Strategy:

Results (The Big Win):

CSS Reduction:

JavaScript Reduction:

Total Bundle:

Tailwind Configuration:

// Preserved brand colors from Semantic UI
colors: {
  primary: '#2185d0',    // Semantic UI blue
  secondary: '#1b1c1d',  // Semantic UI dark
  // ... other theme colors
}

Migration Example:

// Before: Semantic UI
<Button primary fluid onClick={handleVote}>
  Vote
</Button>

// After: Tailwind CSS
<button
  className="w-full bg-primary hover:bg-primary/90 text-white font-bold py-2 px-4 rounded transition-colors"
  onClick={handleVote}
>
  Vote
</button>

Phase 5: User Experience Enhancements (October 18, 2025)

Toast Notifications

Integrated react-hot-toast for immediate user feedback, replacing silent failures and console.logs.

Added toasts for:

Features:

import toast from "react-hot-toast";

// Success feedback
toast.success("Vote cast successfully!");

// Error handling
toast.error("Failed to create issue");

// Loading states
const toastId = toast.loading("Saving...");
await saveData();
toast.success("Saved!", { id: toastId });

Prime Number Voting System

Enhanced the final score calculation with intelligent prime number rounding.

Implementation:

Utilities created:

// Prime number checking
function isPrime(num) {
    if (num <= 1) return false;
    if (num <= 3) return true;
    if (num % 2 === 0 || num % 3 === 0) return false;
    for (let i = 5; i * i <= num; i += 6) {
        if (num % i === 0 || num % (i + 2) === 0) return false;
    }
    return true;
}

// Find nearest prime with threshold
function nearestPrime(num, threshold = 0.2) {
    // Implementation details...
}

Anonymous Login Improvements

Enhanced anonymous login with proper display name support:

Phase 6: Code Quality and Testing (October 2025)

Test Suite Migration:

Test Coverage:

Code Refactoring:

Final Technology Stack (October 2025)

Core:

UI & Styling:

Backend & Data:

Development:

Deployment:

Performance Comparison

MetricBeforeAfterImprovement
Total Bundle1,402 KB679 KB-52%
Gzipped316 KB172 KB-46%
CSS556 KB21 KB-96%
JavaScript846 KB658 KB-22%
Dev Server Start~15s~2s-87%
Build Time~45s~12s-73%

Lessons Learned

1. Vite is a Game-Changer for Developer Experience

The improvement in development speed is hard to overstate. Near-instant server startup and HMR make the development loop dramatically faster. The migration from CRA was straightforward and worth every minute invested.

2. Semantic UI’s Icon Font Problem is Real

1.5 MB of unused icon fonts is absurd for a modern web app. Tree-shakeable SVG icons (like lucide-react) are the clear winner here. The 96% CSS reduction speaks for itself.

3. Tailwind CSS Isn’t Just About Utility Classes

The real win with Tailwind is that you only ship CSS for classes you actually use. No more shipping an entire component library’s worth of styles when you only use a few components.

4. Don’t Sleep on date-fns

Moment.js has been deprecated since 2020. If you’re still using it, migrate now. date-fns is smaller, faster, tree-shakeable, and actively maintained.

5. Toast Notifications Dramatically Improve UX

Replacing silent operations with toast feedback makes the app feel significantly more responsive and professional. Users immediately know when actions succeed or fail.

6. Open Source Means Freedom to Fork and Improve

When Phil didn’t respond to PRs, the open source license gave me the freedom to build my own version. This is exactly how open source is supposed to work — useful code doesn’t die just because the original author moves on. Credit to Phil for releasing it under an open license in the first place.

7. Old Projects Benefit Massively from Modern Tooling

This 6-year-old codebase now has:

The time investment (a few days) paid off immediately. Forking and modernizing beat building from scratch.

Phase 7: Feature Enhancements and Security Hardening (Late October 2025)

After the core modernization was complete, I continued improving the application with new features and critical security fixes.

Spectator Role

Added a complete spectator mode allowing users to observe poker planning sessions without participating in voting.

Features:

Implementation Highlights:

// Role persisted per table
localStorage.setItem(`pokerRole_${tableId}`, role);

// Participants enhanced with role field
{
  uid: string,
  displayName: string,
  joinedAt: ISO8601,
  isHost: boolean,
  role: 'voter' | 'spectator'  // NEW
}

UX Design:

This feature enables team members, managers, or clients to observe sprint planning sessions without influencing the voting process.

Security Hardening

Conducted comprehensive security audit and fixed 3 CRITICAL vulnerabilities in Firebase database rules.

Critical Issues Found:

  1. Unauthorized Data Modification

    • ANY authenticated user could delete or modify ANY table
    • Malicious users could destroy the entire database
    • Fixed: Restricted writes to table owners only
  2. World-Readable Votes

    • Anyone (even unauthenticated users) could read all votes globally
    • Privacy violation exposing all voting data
    • Fixed: Require authentication for vote reading
  3. No Data Validation

    • Users could write malformed data structures
    • No string length limits (DoS vulnerability)
    • No type checking on fields
    • Fixed: Comprehensive validation rules

New Security Rules:

{
    "pokerTables": {
        "$ownerId": {
            ".read": "auth != null",
            ".write": "$ownerId === auth.uid", // Owner-only writes
            "tableName": {
                ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length <= 100"
            }
        }
    },
    "votes": {
        "$tableIssueId": {
            ".read": "auth != null", // Authenticated-only reads
            "$userId": {
                ".write": "$userId === auth.uid" // Self-only writes
            }
        }
    }
}

Data Validation Added:

Security Posture:

UX Polish: Issue Creation

Enhanced the issue creation form with a submit button for better accessibility and discoverability.

Changes:

Before:

[Input Field____________________]
(Enter key only)

After:

[Input Field____________] [+ Add Issue]
(Enter key OR button click)

Simple change with significant usability impact - users no longer have to discover that Enter submits the form.

What’s Next

The modernization is complete, but there are always improvements to consider:

Potential Enhancements:

Maybe Someday:

Try It Out

Live App: pointwith.baker.is Original Project: github.com/philpalmieri/pointwith.me (Phil Palmieri) Updated Project: github.com/keif/pointwith.me (My updates)

This project demonstrates two important lessons:

  1. Open source works: When maintainers move on, the code doesn’t have to die. Forks keep useful tools alive.
  2. Modernization beats rewrites: Strategic migrations of build tools, UI frameworks, and dependencies yield dramatic improvements without throwing away battle-tested code.

If you have a legacy React project running on Create React App, Semantic UI, or moment.js, this modernization path is worth considering. The ecosystem has evolved significantly, and the gains are substantial.

And if you find an abandoned open source project you love, don’t be afraid to fork it and make it yours. That’s what open source is for.

The best time to modernize was when these tools came out. The second best time is now.


Share this post on:

Previous Post
Migrating from Render to Fly.io: 4x More RAM, Lower Cost
Next Post
In Defense of Emojis in Developer Logs