The history of the PopScore formula: how we got here
PopScore has been through four major versions since GitPop launched in 2024. Here is the history — what we tried, what worked, and what we had to throw out.
Quick answer. PopScore has been through four major versions: v0.1 (stars + forks only), v1.0 (added activity and mentions), v1.4 (added noise penalty, the biggest quality win), and v1.7 (the current version, refined weighting and added maturity / health tiers). Each version was a response to a specific failure mode in the previous one. This is the engineering diary.
v0.1: Stars + forks (2024 Q1)
The first version of PopScore was embarrassingly simple:
popscore = stars / 1000 + forks / 200
We launched with this in early 2024. Within two weeks, the radar was dominated by tutorial repos and awesome-lists — projects with 50k stars and 5 forks, the classic "I want to remember this exists" pattern. The score was useless for ranking actual software.
Failure mode: no signal for "real" projects vs. bookmarks.
v1.0: Add activity and mentions (2024 Q3)
The second version added two more components:
popscore = 0.5 * (stars_velocity / 100)
+ 0.2 * (forks / 100)
+ 0.2 * (activity_score)
+ 0.1 * (mentions_score)
Where activity_score was commits + PRs + issues over 7 days (normalized to 0-1) and mentions_score was HN + Reddit + Lobsters mentions over 7 days (also normalized).
This was a huge improvement. The radar started surfacing real projects with real momentum. The noise rate dropped from ~40% to ~15%. But we still had a problem: projects with deliberately inflated metrics (engagement farming, comment bots) were ranking too high.
Failure mode: no quality gate. Spammable metrics inflate the score.
v1.4: Add Noise Filter v2 (2025 Q2)
The third version added the Noise Filter — a separate 0-100 score that captures "how likely is this to be a real project?" The four dimensions are:
- D1: Name pattern (20%) — does the name look like a fork, mirror, or curated list?
- D2: Fork/archive status (15%) — is the project a fork or archived?
- D3: Asset quality (15%) — does the README have substance? Is there a license? Are there topics?
- D4: Activity (20%) — when was the last meaningful activity?
The filter produces a 0-100 noise score. The PopScore is then:
popscore = base_score * (1 - noise_score / 100)
Or more precisely, the noise score is a 10% weight in the formula:
popscore = 0.4 * (stars_velocity / 100)
+ 0.2 * (forks / 100)
+ 0.2 * (activity_score)
+ 0.1 * (mentions_score)
- 0.1 * (noise_score / 100)
The noise filter was the single biggest quality improvement. The radar noise rate dropped from ~15% to < 5%. The radar became trustworthy.
Failure mode: occasional over-penalization. Some legitimate but unusual projects (single-author projects, niche tools) were getting flagged.
v1.7: Refined weighting and added health/maturity tiers (2025 Q4 → 2026 Q2)
The current version of PopScore is the most refined. The key changes from v1.4:
- Contributor count is now a primary component. The relationship between contributor count and project survival is r = 0.82 (see The truth about GitHub forks). This is the strongest single predictor of project success.
- Health score and maturity score are separate dimensions. Rather than collapsing everything into PopScore, we now compute two additional scores:
- Health score (0-100) — measures how "alive" the project is. Contributors, recent activity, issue close rate.
- Maturity score (0-100) — measures how "ready" the project is. Tests, CI, documentation, license.
- The noise filter is more conservative. We moved from a hard skip at noise ≥ 70 to a soft flag at noise ≥ 50. This means borderline projects are tracked but hidden by default. The radar noise rate is now < 3%.
The current formula:
popscore = 0.35 * star_velocity_normalized
+ 0.20 * fork_engagement_normalized
+ 0.25 * activity_normalized
+ 0.10 * mentions_normalized
- 0.10 * noise_normalized
Where each component is normalized to 0-1 using a sigmoid. The output is then scaled to 0-100.
What we got wrong
A few things we tried that did not work:
- Sentiment analysis on mentions. We tried using NLP to score whether mentions were positive or negative. The signal was too noisy. We dropped it.
- Commit message quality. We tried scoring commits on message quality. The signal was too noisy. We dropped it.
- GitHub Issue sentiment. Same problem. Dropped.
- README word count. We tried a simple "longer README = better" heuristic. Wrong — long READMEs are sometimes AI-generated slop. The noise filter now treats very long READMEs as a yellow flag, not a green one.
- Twitter mentions. We tried pulling Twitter mentions. The signal was dominated by bot amplification. Dropped.
What we got right
A few things that worked better than we expected:
- The noise filter. It is the single most important component. Without it, the radar is unusable.
- Fork count as a quality signal. Most people dismiss forks as noise. We found the opposite — see The truth about GitHub forks.
- Reverse dependencies. The dependency graph is one of the strongest quality signals. See The dependency graph as a quality signal.
- Health score and maturity score as separate dimensions. Projects can be healthy but not mature (new but active), or mature but not healthy (mature but stalled). Collapsing them into one score loses information.
The future
We are working on v2.0 for 2026 H2. The key change: integrating the dependency graph directly into the formula, and adding a "community health" dimension (governance, code of conduct, security policy). More on this when we ship.
For the current methodology, see What is PopScore? and the methodology page. For the broader data, see State of GitPop H2 2026.