At commit 683eab2, servo/servo has 1,921 critical-band functions out of 50,646 total — and five are in the fire quadrant right now, meaning they are both structurally complex and actively changing. The top-ranked function carries an activity-weighted risk score of 21.06, flagged as a god function and hub function with a fan-out of 55 distinct callees; any engineer merging into files it touches this week is working against a live regression risk, not a deferred cleanup item. I would start triage with clean_cargo_cache in python/servo/bootstrap_commands.py — a cyclomatic complexity of 108 with nine levels of nesting is the single most structurally dangerous function in this list, and it was modified one day ago.
The table below ranks functions by activity-weighted risk — a score that multiplies structural complexity by recent commit frequency. A function that is both hard to understand (high cyclomatic complexity) and actively changing is a higher priority than one that is complex but untouched. CC = cyclomatic complexity (independent execution paths); ND = max nesting depth; FO = fan-out (distinct callees).
Top 5 Hotspots
| Function | File | Risk | CC | ND | FO |
|---|---|---|---|---|---|
run | tests/wpt/webgpu/tests/webgpu/webgpu/web_platform/reftests/canvas_complex.html.js | 21.1 | 35 | 4 | 55 |
serialize | tests/wpt/tests/resources/channel.sub.js | 20.0 | 60 | 7 | 13 |
<anonymous> | tests/wpt/webgl/tests/js/glsl-constructor-tests-generator.js | 19.9 | 50 | 7 | 53 |
clean_cargo_cache | python/servo/bootstrap_commands.py | 19.7 | 108 | 9 | 53 |
checkCallResults | tests/wpt/webgpu/tests/webgpu/webgpu/shader/execution/expression/call/builtin/texture_utils.js | 19.6 | 36 | 7 | 64 |
Large Repo Analysis
servo is a large repository. To stay within memory constraints, this analysis used hybrid touch mode: structural complexity — CC, ND, FO — is measured precisely for every function. Git activity is tracked at the function level (via git log -L) only for files with 20 or more commits in the last 30 days; other files use a file-level approximation. Rankings therefore surface functions that are both structurally complex and in the most actively-changing parts of the codebase. Dormant code with high structural complexity will rank lower than it would under a full per-function analysis — to surface it, run hotspots analyze . --per-function-touches on a machine with sufficient memory.
Codemod / Tooling Files in Results
Four of the five top hotspots — run in canvas_complex.html.js, serialize in channel.sub.js, the anonymous function in glsl-constructor-tests-generator.js, and checkCallResults in texture_utils.js — live under tests/wpt/, which is servo’s local mirror of the W3C Web Platform Tests suite and the WebGPU conformance test suite (CTS). These files are upstream test assets vendored into the repository, not servo-authored code; their structural complexity reflects the test suite’s design, not servo’s own architecture. To exclude them from future hotspots runs and focus on servo’s own Rust and Python source, add the following to .hotspotsrc.json: { "exclude": ["tests/wpt/webgpu/", "tests/wpt/webgl/", "tests/wpt/tests/"] }. That will surface the servo-native hotspots that represent actual refactoring opportunity in the engine itself.
What the numbers show across the codebase
50,646 functions analyzed
The quadrant picture is striking: every function in servo/servo falls into either fire or watch — there is no debt quadrant and no ok quadrant. That means every structurally complex function is also seeing recent activity, and every dormant function is structurally simple. The 6,985 fire-quadrant functions are the ones that matter most for near-term regression risk.
Function body is too long to review in a single pass; likely contains multiple distinct responsibilities.Complex Branching×5Complex Branching
High cyclomatic complexity — many independent execution paths, each a potential bug surface and required test case.God Function×5God Function
Calls an unusually large number of distinct functions (high fan-out), making it the structural centre of gravity for a subsystem.Exit Heavy×4Exit Heavy
Multiple return or throw paths dispersed through the body — each exit needs separate test coverage.Deeply Nested×4Deeply Nested
Control structures nested 4+ levels deep, making it hard to reason about the full execution state at inner branches.Hub Function×3Hub Function
Many other functions call this one — a change here ripples widely through callers.
The pattern distribution across the top five tells a consistent story: every hotspot is a long function, every one shows complex branching, and most are flagged as god functions. These are not isolated quirks — they are the signature of functions that have accumulated responsibility over time and are now difficult to reason about, test, or change safely.
Top 5 hotspots
run — canvas_complex.html.js
This is the top-ranked function in the repository with an activity-weighted risk score of 21.06. Its name and path tell a clear story: it is the entry point for a WebGPU canvas reference test, responsible for orchestrating a runRefTest call that exercises multiple GPU texture formats. The source excerpt confirms what the metrics already suggest — a large outer switch dispatching on format strings (bgra8unorm, rgba8unorm, rgba16float, and their sRGB variants), followed by a nested inner function copyBufferToTexture that contains a second large switch with per-format typed array initialization.
A fan-out of 55 is the primary concern here. The hub_function pattern confirms it: this function is a coordination point that calls into GPU buffer creation, texture format utilities, alignment helpers, float conversion routines, and test assertion machinery. A change to any of those callees can ripple back through run in ways that are hard to anticipate. The exit_heavy pattern, combined with a cyclomatic complexity of 35, means there are 35 independent execution paths to cover — but since this is itself a test file, testing the test becomes the challenge. The double switch structure is the concrete extract-method opportunity: the inner copyBufferToTexture function is already partially extracted but still carries its own branching weight. Pulling each format’s buffer-fill logic into dedicated helpers would cut both the CC and the fan-out of the outer run substantially.
serialize — channel.sub.js
With a cyclomatic complexity of 60 and a maximum nesting depth of 7, serialize in the WPT channel resources file is the most structurally dangerous of the JavaScript hotspots on a pure branching basis. The source excerpt shows an iterative serializer — it avoids recursion by maintaining an explicit queue of objects to process, a deliberate design choice for handling cyclic references. That design is sound, but the implementation has grown to handle every JavaScript type: undefined, null, string, boolean, number (with four special numeric cases — NaN, -0, +Infinity, -Infinity), bigint, function, remoteobject, sendchannel, regexp, date, and error, each as a case in a large switch statement nested inside a while loop.
The deeply_nested and exit_heavy patterns are direct consequences of that structure: 7 levels of nesting arise from the while loop, the seen-object guard, the outer switch, and per-type sub-branches. With 60 paths, any change to how a single type is serialized risks breaking the general loop logic. My recommendation is to extract each type’s serialization into a standalone handler function keyed by type name, then replace the switch with a dispatch table. That refactoring would reduce the CC of the main loop to roughly the number of distinct type categories and make each type handler independently testable.
<anonymous> — glsl-constructor-tests-generator.js
An anonymous IIFE at the module level of a GLSL test generator with a cyclomatic complexity of 50, a nesting depth of 7, and a fan-out of 53. The source excerpt confirms this is a module factory: it defines shader template strings for vertex and fragment shaders, encodes GLSL type dimensions (s, v2, v3, v4, m2, m3, m4), and generates test cases for every combination of constructor argument type and target type.
The god_function pattern is apt — this single anonymous block handles template definition, type encoding, argument generation, and test case assembly all in one place. The fan-out of 53 means it reaches into WebGL test utilities, GLSL type helpers, and test harness machinery broadly. Because the function is anonymous, stack traces referencing it will be opaque, making debugging failures in generated tests harder than necessary. The concrete first step is to give the IIFE a name — GLSLConstructorTestsGenerator is already used as the variable receiving its return value — and then extract the type-dimension encoding and shader template assembly into named module-level functions. That alone would reduce the complexity of the top-level factory and surface the logical seams for further splitting.
clean_cargo_cache — bootstrap_commands.py
This is the function I would prioritize for a refactoring session today. A cyclomatic complexity of 108 means 108 independent execution paths through a single Python method. Nesting reaches 9 levels deep — a strong signal that the function’s control flow has been extended incrementally rather than designed. The source excerpt confirms it: clean_cargo_cache parses Cargo.lock via TOML, distinguishes between registry crates and git-sourced crates, walks the filesystem under ~/.cargo, reconciles what is present against what is locked, and deletes stale entries — all inline, with branch conditions layered inside loops inside conditionals inside more loops.
The deeply_nested pattern at ND 9 is a direct consequence of that layering: by the time you are reasoning about a branch nine levels in, you are simultaneously tracking the state of the CARGO_HOME environment variable, whether a crates vs git key exists in the package dict, whether the directory structure matches the expected layout, and whether force was passed. The fan-out of 53 means this method touches filesystem utilities, path manipulation, TOML parsing, and OS environment APIs broadly, so errors here can cascade silently. My recommendation is a staged extract-method refactoring: pull _collect_locked_packages (TOML parsing and crate classification), _scan_cache_entries (filesystem enumeration), and _remove_stale_entries (deletion logic) into separate methods, each testable in isolation. That would cut the CC of the orchestrating method to the single-digit range and make the force and show_size flag logic far easier to follow.
checkCallResults — texture_utils.js
checkCallResults holds the highest fan-out of any function in the top five at 64 distinct callees, earning it both the god_function and hub_function patterns. Its path places it deep in the WebGPU shader execution test suite, specifically in the builtin texture utility layer. The source excerpt reveals an async function that compares GPU texture sampling results against a software reference implementation: it initializes mip-level weights, computes per-format fractional difference tolerances, iterates over every test call, reads back GPU texels, and formats detailed diagnostic messages when results diverge.
The nesting depth of 7 comes from the combination of the outer call loop, per-call result comparison, format-conditional tolerance logic, and the textureSampleBias precision-handling branches — the source excerpt even includes inline commentary explaining why bias values above ~12 produce unexpected GPU results. That commentary is valuable domain knowledge, but buried inside a 64-callee function it is hard to find and easy to break. With 36 execution paths and a fan-out touching mip-weight initialization, software texture reads, GPU readback, and error formatting, a change to any one of those subsystems has a plausible path to silently breaking the comparison logic. I would extract the per-call comparison logic — the tolerance calculation, the software vs GPU texel comparison, and the diagnostic message formatting — into a compareTextureCallResult helper. That would keep the outer loop simple and let the tolerance and precision logic be unit-tested independently of the GPU readback machinery.
Patterns Found
Antipatterns detected across the top functions in this snapshot:
| Pattern | Occurrences |
|---|---|
long_function | 6 |
complex_branching | 5 |
god_function | 5 |
exit_heavy | 4 |
deeply_nested | 4 |
hub_function | 3 |
These labels belong to two tiers — Tier 1 (structural): complex_branching, deeply_nested, exit_heavy, long_function, god_function. Tier 2 (relational/temporal): hub_function, cyclic_hub, middle_man, neighbor_risk, stale_complex, churn_magnet, shotgun_target, volatile_god.
See more analyses with these patterns: complex_branching, deeply_nested, exit_heavy, god_function, long_function.
Reproduce This Analysis
git clone https://github.com/servo/servo
cd servo
git checkout 683eab21c0b640649d511f11f599a2efdd9f102f
hotspots analyze . --mode snapshot --explain-patterns --force --hybrid-touches 20
To run the same analysis on your own codebase, run hotspots analyze . --mode snapshot in any local git repo — no configuration required.
I use Hotspots to highlight structural and activity risk — not “bad code.” I treat these findings as a prioritization aid, not a bug predictor. Editorial policy →