Paste a git repository URL, or an absolute path to a checkout already on this machine. URLs are cloned read-only; local folders are analyzed in place (no clone, git history included). The code is never executed. Private repositories work with a read-only access token below.
These thresholds decide when a function is flagged. The numbers below come from measuring 42,021 functions across 15 major Python projects (listed at right), lined up from simplest to most complex. A percentile pN is the complexity value at position N% in that line-up, so a higher percentile means a more complex function, not a better one: p99 is the messy 1% at the far end, not a mark of quality. Two of the columns are inverse views: median/p90/p95/p99 ask ‘what value sits at this rank?’, while default’s rank asks ‘what rank does CUPID’s default sit at?’
Reading a row (cyclomatic): a typical function (median) scores 2; 90% score 6 or less; 95% score 9 or less. CUPID’s default of 10 is higher than 96% of real functions; that is its rank, the 96th percentile, so it flags only the most complex ~4%. Only the extreme p99 (20) sits above the default, i.e. the rare outliers worth catching. Raise a number to flag less, lower it to flag more, or uncheck a signal to skip it.
| Check | Default | Source / standard | default's rank | median | p90 | p95 | p99 |
|---|---|---|---|---|---|---|---|
| cyclomatic complexity | 10 | McCabe 1976: ≤10 = moderate risk | 96th | 2 | 6 | 9 | 20 |
| cognitive complexity | 15 | Campbell / SonarSource 2018: default 15 | 96.2th | 0 | 7 | 13 | 34 |
| nesting depth | 4 | structured-programming convention: ≤4 | 99.2th | 0 | 2 | 3 | 4 |
| function length (lines) | 60 | review convention: fits on a screen | 96.9th | 7 | 32 | 47 | 101 |
| parameter count | 5 | Fowler, Refactoring: data clump | 97.7th | 1 | 3 | 4 | 8 |
| magic numbers | 8 | maintainability heuristic: ≤8 literals | 99.6th | 0 | 0 | 1 | 5 |
Are these thresholds strict or lenient? Strictness is about where the line is drawn in the crowd: a strict bar is a low number that flags ordinary code; a lenient bar is a high number that catches only the worst. Every default here sits above the 90th–95th percentile of real code (cyclomatic 10 > p95 of 9; cognitive 15 > p95 of 13), so the line is drawn at the far-right edge of the distribution: deliberately lenient, catching only outliers. That a high percentile exceeds the default (cyclomatic p99 = 20 > 10) does not make it strict; it just means a rare 1% is messier than the threshold; precisely the code worth flagging.
So the literature thresholds are not stricter than industry practice; they sit above almost all of it. Each default is a risk ceiling from the literature (McCabe’s cyclomatic 10 = ‘moderate risk’, SonarSource’s cognitive 15, review conventions for the rest), not a target. Well-regarded code naturally lands far below it (median cyclomatic 2, median nesting 0), so a default fires on only the most complex few percent; for nesting, the top ~0.8%. They are not ‘too low’; lowering one toward the corpus median would start flagging ordinary, healthy code.
Cyclomatic complexity (Thomas McCabe, A Complexity Measure, IEEE TSE 1976) counts the independent execution paths through a function: start at 1 and add 1 for every branch point (if, for, while, and/or, case). It approximates the minimum number of tests needed to cover the function, so a high value means more branches, harder testing, and more places for bugs to hide. McCabe proposed 10 as the 'moderate risk' boundary above which a function should be split; still in wide use, and CUPID's default.
Cognitive complexity (G. Ann Campbell, SonarSource white paper, 2018) measures how hard a function is for a person to read, not how hard it is to test. Unlike cyclomatic it penalises nesting (each level deep costs more) and flow-breaking structure (break, continue, nested conditionals), so ten flat ifs score far lower than three deeply nested ones. SonarQube ships it as rule S3776 with a default limit of 15, which CUPID adopts.
Nesting depth is how many control structures are stacked inside one another (an if inside a for inside a while = depth 3). Deep nesting is the classic target of the guard-clause / early-return style and traces back to structured-programming practice (Dijkstra, Go To Statement Considered Harmful, 1968). There is no single threshold paper; 4 is a long-standing review convention, and the fix is to flatten with early returns rather than indent further.
Function length is simply the line count. The guidance 'a function should do one thing and fit on a screen' is a review convention popularised by Martin Fowler's Refactoring (Extract Function) and Robert C. Martin's Clean Code, not a number from a paper. CUPID's 60 is a screen-sized heuristic, and it only raises severity when the function is also cognitively complex; a long but flat data table is not punished.
Parameter count flags the 'long parameter list' / 'data clump' code smell from Fowler's Refactoring: when many arguments travel together they usually want to be grouped into an object. It is a design heuristic, not a measured threshold; 5 is the common ceiling CUPID uses.
Magic numbers are un-named numeric literals embedded in code (e.g. `if retries > 7`), which hide intent and drift out of sync. Naming them as constants is a maintainability convention found in most style guides; there is no canonical paper or number, so CUPID flags a function only once it carries more than 8 such literals: a prescription, not a firehose.
python/cpythondabeaz/curiodabeaz/plysimonw/datasettesimonw/sqlite-utilssimonw/llmTextualize/richTextualize/textualencode/httpxencode/django-rest-frameworkpython-attrs/attrshynek/structlogpsf/blackpytest-dev/pytestdjango/djangomax(default, profile)). The profile's job is to attach percentile
evidence to every finding, not to move thresholds.build_degraded() finds the long-term debt that crossed
the threshold and was never fixed.The effect: CUPID can tell a customer that datasette fixed this exact class of problem in a specific commit, the same way, and the fix held. The recommendation upgrades from “we think” to “history shows”.