GitHub new-repos research · Calibration study · 2 November 2026

Estimating a GitHub repo's lines of code without downloading it

We cloned 250 popular repos created in the last 12 months, counted their code, and tested which cheap signal from GitHub's API predicts lines of code best. Repo size doesn't work well. GitHub's per-language byte counts do.

Key takeaways

  • Use GitHub's language bytes, not repo size. Dividing the code bytes GitHub reports per language by 41 bytes per line lands within ±13% of the real count for the typical repo, and within 2× for 97% of repos.
  • Repo size is off by about 2× for a typical repo, even with a separate ratio for every language and size bucket (median error 2.1×, only 47% within 2×).
  • Bigger repos have far fewer lines per KB: 16.6 lines/KB for 100 KB–1 MB repos versus 1.7 for repos over 250 MB, a 9× drop. Large repos are mostly assets, data and model files, not code.
  • Language matters less than expected. Code density ranges from 36 bytes per line (Go) to 46 (JavaScript), but the variation within a language and size cell is often 5–10×.
  • Plan for the research: keep size: as a loose search pre-filter, then make one language-bytes call per repo to estimate lines of code.
250
repos cloned and counted
±13%
median error, language bytes
2.1×
median error, best size-based estimate
97%
within 2× using language bytes

Why this matters

The upcoming research looks for new GitHub repos and filters or describes them by how much code they contain. GitHub search has no lines-of-code filter. Counting lines exactly means downloading each repo, which is fine for a shortlist but too slow for thousands of candidates. So we needed a cheap signal from the API that predicts lines of code, and to know how far off it can be.

There were two candidates:

Method

Sample

We picked 5 languages × 5 size buckets and took the 10 most-starred repos in each of the 25 cells. Every repo was created on or after 2025-11-02 and none are forks or archived. We used new repos because the research targets new repos, and old repos carry years of git history that inflates size. The language is GitHub's primary language for the repo.

Each number in the table below is a count of repos: how many public GitHub repos fall in that language and size range (and pass the filters above). That's the pool each cell's 10 repos were picked from. For example, there were 2.28 million Python repos of 100 KB–1 MB, and we cloned the 10 most-starred.

Repos available100 KB–1 MB1–10 MB10–50 MB50–250 MB> 250 MBAll sizes
Python2.28M1.51M728K350K85K4.96M
TypeScript3.20M1.75M746K296K55K6.05M
JavaScript2.24M1.71M787K286K54K5.08M
Go121K90K46K14K2K273K
Rust140K98K35K16K4K294K
All 5 languages7.98M5.16M2.34M962K202K16.65M

Counting

Testing the estimators

Each estimator was scored leave-one-out: each repo is predicted using a ratio fitted on the other repos, so no repo helps predict itself. Error is the factor the estimate is off by, in either direction. "Off by 2×" means the estimate is half or double the real count.

Size vs lines of code

Both axes are logarithmic. If size predicted lines of code well, the points would form a tight line. Instead, at any given size the line count varies by one to two orders of magnitude. The dashed line is the best fit across all repos: LOC ≈ 58 × size_kb0.73. The exponent is below 1, so doubling a repo's size adds far less than double the code.

Repos in this languageAll other reposPower-law fit (all repos)

Lines per KB, by cell

Lines of code per KB of repo size, median, with the middle 50% (25th–75th percentile) below each value. Darker cells mean more code per KB.

Language100 KB–1 MB1–10 MB10–50 MB50–250 MB> 250 MB
Python3.31.7–7.45.14.2–6.66.52.7–9.12.91.8–3.31.20.0–2.6
TypeScript26.318.8–35.95.93.2–17.95.85.2–11.64.42.9–9.12.91.4–5.4
JavaScript16.73.4–23.94.03.4–5.32.01.1–4.12.20.6–4.00.60.3–0.8
Go27.016.8–43.17.84.1–11.57.65.6–14.45.03.9–7.01.70.5–2.5
Rust15.23.1–33.513.37.6–19.97.04.7–11.25.54.8–7.62.72.0–4.5
All languages16.64.1–28.45.53.6–14.06.22.8–11.84.22.5–6.21.70.7–3.0

Two patterns stand out. Lines per KB falls steadily with size in every language. And the middle-50% ranges are wide: in many cells the 75th percentile is 3–10× the 25th, which is why a single ratio per cell can't give accurate estimates. Python's small repos are the exception to the trend: many of the top-starred ones are agent skills or small tools where most of the repo is prompts and docs, with only a few hundred to a few thousand lines of code.

Why size is a weak proxy

GitHub's size measures the whole repository as stored, including history and every file type. Code makes up a shrinking share of it as repos grow:

Source code as a share of repo size
Median per size bucket: bytes of counted code ÷ GitHub repo size
Size bucketCode share of sizeCheckout ÷ sizeMedian LOC
100 KB–1 MB60%1.514,410
1–10 MB25%1.1031,478
10–50 MB25%0.88193,057
50–250 MB17%0.59474,650
> 250 MB7%0.30905,673

In the smallest bucket code is 60% of the size. Above 250 MB it is 7%, with the rest made up of images, videos, model weights, datasets, built bundles and history. The latest checkout is also often a different size from GitHub's figure, because size is the compressed repository including history. A new repo with a few large assets and a mature code-heavy repo can therefore be the same size.

Estimator comparison

Share of repos estimated within 2× of the real count
Leave-one-out, 250 repos. Higher is better.
Based on repo sizeBased on GitHub language bytes
EstimatorMedian error75th pct90th pctWithin ±25%Within 2×
Size × one global ratio2.4×5.1×11.4×17%42%
Size × ratio per language2.4×4.7×10.2×17%40%
Size × ratio per size bucket2.1×4.0×10.2×19%48%
Size × ratio per language + bucket2.1×3.7×7.8×23%47%
Size, power-law fit2.2×4.2×8.9×10%43%
GitHub language bytes ÷ 41 B/line1.1×1.2×1.4×80%97%
GitHub language bytes ÷ per-language B/line1.1×1.2×1.4×83%96%

Splitting the size ratio by size bucket helps (median error 2.4× → 2.1×). Splitting by language barely helps on top of that. The power-law fit does about as well as the per-bucket ratios with only two parameters. None of the size-based estimators gets close to the language-bytes approach.

Median error by size bucket
How far off each estimator is, in % (100% = off by 2×). Size estimate uses the per-language + bucket ratio.
Size × ratio per language + bucketLanguage bytes ÷ 41

The language-bytes estimator

estimated LOC = Σ GitHub language bytes (code languages only) ÷ 41

"Code languages only" drops Linguist languages that aren't source code in our count: Jupyter Notebook, Markdown, TeX, Roff, Makefile, Dockerfile, Nix, HCL and template languages. 41 is the median bytes per non-blank line across all 250 repos.

Estimated vs actual lines of code
Each dot is a repo. The solid line is a perfect estimate; the dashed lines are 2× over and 2× under.

Bytes per line by language

Primary languageMedian bytes / lineMiddle 50%Code in primary languageMedian error (÷ 41)
Python4542–4879%±11%
TypeScript4038–4592%±11%
JavaScript4642–5288%±18%
Go3634–3987%±17%
Rust4139–4590%±5%

The per-language divisors range from 36 to 46, but using them barely changes accuracy (see the estimator table). Repos mix languages (the primary language is typically 80–90% of the code), and the spread within a language is about as wide as the gap between languages, so one divisor of 41 is enough.

Biggest misses

RepoActual LOCEstimatedEst ÷ actualProbable cause (inferred, not verified)
datawhalechina/easy-vibe241,9385,1650.02×GitHub's language stats leave out Vue, which is most of the code here (stale stats or a .gitattributes override)
tamaratran/fast-jev-compaction13,1251,5540.12×GitHub counts fewer bytes than are in the checkout: likely files marked vendored, generated or documentation by Linguist or the repo's .gitattributes
tt-a1i/archify1,021,492211,6460.21×GitHub counts fewer bytes than are in the checkout: likely files marked vendored, generated or documentation by Linguist or the repo's .gitattributes
JimLiu/baoyu-skills203,05147,4960.23×GitHub counts fewer bytes than are in the checkout: likely files marked vendored, generated or documentation by Linguist or the repo's .gitattributes
colbymchenry/codegraph1,320,827529,9120.40×GitHub counts fewer bytes than are in the checkout: likely files marked vendored, generated or documentation by Linguist or the repo's .gitattributes
wheresryan22/anatomy52,61325,0360.48×GitHub's language stats leave out HTML, which is most of the code here (stale stats or a .gitattributes override)
agentskills/agentskills1,4857140.48×GitHub's language stats leave out JavaScript, which is most of the code here (stale stats or a .gitattributes override)
Leonxlnx/taste-skill4,94210,2732.08×Code with long lines (data tables, inlined assets) inflates bytes relative to lines

The large misses come from mismatches between what GitHub counts and what we counted: languages GitHub leaves out (stale stats or .gitattributes overrides), or bundled and minified files GitHub counts as code. These are rare, but they mean a single estimate can occasionally be far off.

How the research will use this

  1. Search with a loose size: pre-filter to remove obvious mismatches without losing candidates. The table below shows where repos of each lines-of-code band actually fell in size (10th–90th percentile):
Actual LOC bandReposSize p10 (KB)Median (KB)Size p90 (KB)
< 5k3715753017,736
5k–50k672962,71357,815
50k–500k846,92935,586231,640
> 500k6289,059302,2321,028,821

The sample was stratified by size, so read this as a guide to how wide the pre-filter should be, not as population frequencies.

  1. Estimate lines of code for each candidate with one call to the languages endpoint ÷ 41. At 5,000 authenticated requests per hour [4], a few thousand repos fit comfortably. Search itself is limited to 30 requests per minute and 1,000 results per query [5], so broad searches need to be split into narrower ones.
  2. Count exactly only where it matters: repos near a cut-off, or a final shortlist, using the same clone-and-count script.

Limitations and open questions

All 250 repos

Click a column heading to sort. "Size est." is the per-language + bucket estimate (leave-one-out); "Bytes est." is language bytes ÷ 41.

RepoLanguageSize bucketStars Size (KB)LOCLOC/KBSize est. Bytes est.Main languages counted

Sources

  1. Searching for repositories, GitHub Docs. size: qualifier in kilobytes. Accessed 2026-11-02.
  2. REST API endpoints for repositories: List repository languages, GitHub Docs. "Number of bytes of code written in that language." Accessed 2026-11-02.
  3. Linguist: Overrides, GitHub Linguist. Vendored, generated and documentation files are excluded from language stats, and can be overridden in .gitattributes. Accessed 2026-11-02.
  4. Rate limits for the REST API, GitHub Docs. 5,000 requests per hour for authenticated users. Accessed 2026-11-02.
  5. REST API endpoints for search, GitHub Docs. Repository search 30 requests per minute; up to 1,000 results per search. Accessed 2026-11-02.
  6. Primary data: this study's clone-and-count run over 250 repos, 2026-11-02.