The Four Lines of Code That Run Hacker News

Hacker News sorts every story with one power law: (points - 1) / (hours + 2)^1.8. Here's why it works — and how to steal it for your own feeds.

Share

A 2013 teardown of the Hacker News ranking algorithm is on Hacker News's front page today with 172 points, explaining how Hacker News's front page works. Perfectly recursive. Ken Shirriff reverse-engineered the whole thing by watching scores move — and it turns out every story you've ever upvoted was sorted by one division and one exponent.

The formula fits in a tweet. The interesting part is everything hiding around it.

Why this matters

Someday you will build a ranked feed — trending items, an activity list, search results where freshness matters. HN's algorithm is the cleanest production example I know, because it also shows the part tutorials skip: how to stop bad actors from farming your front page.

How it works

The core score is a power-law decay:

score = (points - 1) / (hours + 2) ** 1.8

Every constant is a deliberate decision. The - 1 cancels the submitter's automatic self-upvote, so posting alone scores zero. The + 2 means a story's effective age never starts at zero, which stops brand-new submissions from spiking straight to the top.

The exponent — "gravity" — controls churn. Holding the same score at hour 10 takes roughly 12× the votes it took at hour 1. Nudge that number and the front page turns over faster or slower; HN picked 1.8 as the compromise.

Then come the multipliers. Shirriff found controversial stories — high comment-to-vote ratio, the flame-war signature — get dinged by roughly a factor of 0.4. Votes from brand-new accounts count less. Votes from accounts that always vote together count less. That's the anti-gaming layer: demotion instead of deletion.

Where this helps

  • Trending lists. Any "hot" endpoint. Power-law decay beats naive recency sorting because popular items earn the right to linger.
  • Search freshness. Multiply relevance score by a decay function instead of filtering by date — the same pattern Elasticsearch's function_score gives you.
  • Vote trust. Weight votes by account age and reputation before counting them. Cheaper and calmer than fighting abuse after it ranks.
  • Moderation levers. Penalty multipliers let you demote garbage without deleting it, which keeps users from discovering invisible rules the hard way.

Watch out

These numbers are 2013-era reverse engineering. HN has changed since, so treat this as the canonical mental model, not a spec.

The exponent is dangerously sensitive. The gap between 1.5 and 2.2 is the difference between a sticky front page and one that refreshes every few minutes. Derive it from your own traffic, not HN's.

Also: pure power-law decay punishes slow-burn content. A great tutorial that takes six hours to catch fire never ranks. Reddit's log-scaled votes handle this more gently — worth a look if your content isn't news.

Try it yourself

def rank(points, hours, gravity=1.8):
    return (points - 1) / (hours + 2) ** gravity

# a 100-point story two hours old
baseline = rank(100, 2)              # ~8.2

# how many votes to match it at hour 20?
votes = baseline * (20 + 2) ** 1.8 + 1
print(votes)                         # ~2130 — 21x the votes

# tune gravity for your app:
# 1.3 = sticky feed, 2.2 = minutes-level churn

TL;DR

  • What changed: The classic 2013 reverse-engineering of HN's ranking is back on the front page — the formula is (points - 1) / (hours + 2)^1.8 plus penalty multipliers.
  • Why it matters: One exponent controls your entire feed's churn rate, and HN's vote-weighting is the anti-abuse pattern most ranking systems skip.
  • Try today: Drop the rank() function into your next trending endpoint and tune gravity against real traffic.