204 Code Clones and Two Self-Loops: Dissecting Bevy's 97-Crate Engine with 67 AST Tools
We benchmarked all 67 prod-code AST tools against bevyengine/bevy: 661K lines of Rust, 97 crates, 204 exact code clones, 2 dev-dependency loops, and Bug #739 discovered and fixed in fast-sync.

On this page · 11 sections
- 1. Node Topology & Fast-Sync Ingestion
- 2. Crate Topology & The Two Self-Loops
- 3. The AST Clone Harvester: 204 Exact Clones
- 4. Bug #739: The Hidden Cargo Directory Trap in Fast-Sync
- 5. Semantic & Structural AST Search
- 6. Type Hierarchy & The 28 Traits of Entity
- 7. Program Slicing: Bounding Massive ECS Structures
- 8. Guarded AST Refactorings & Integrity Loop
- 9. Speculative Synthesis, RAM Validation & Shadow Runs
- 10. Remote 32-Core Build, Lint & Bench via prod-code exec
- Conclusion & Evaluation Summary
bevyengine/bevy is celebrated as the flagship data-driven game engine in modern Rust. Its custom Entity Component System (ECS), compile-time system scheduling, and high-performance renderer have made it the darling of the Rust graphics community. But under the hood, Bevy is also a legendary stress test for compilers, IDEs, and developer laptops: 661,813 lines of Rust across 97 workspace crates and 98 Cargo manifests, heavily leaning on deep macro expansion, type reflection, and massive generic instantiations.
On a standard developer laptop, loading Bevy into rust-analyzer routinely consumes 10–25 GB of RAM, spins cooling fans to jet-engine levels, and stalls AI coding agents trying to parse cross-crate symbol graphs.
To see whether a cluster-backed remote code intelligence architecture can handle an engine of this scale with 0% local laptop CPU, we subjected bevy to our standardized 67-tool evaluation protocol of prod-code on our 32-core Linux build node (booster).
Here is what 67 AST analyzers found when dissecting Bevy.
1. Node Topology & Fast-Sync Ingestion
Syncing a 661K-line workspace across 3,044 files over a local network usually forces agents into sluggish Git clones or file-watcher thrashing. With prod-code, the checkout is projected into cluster memory via hash-watermarked differential sync:
Local Checkout: /Users/alex09x/Documents/workspace/bevy-eval
Target Host: booster (192.168.2.168:9400 / AMD EPYC 32-core, 134 GB RAM)
LAN Latency: 613.67 µs RTT
Rust Source Files: 1,917 (.rs)
Total Files: 3,044
Lines of Rust: 661,813 across 97 crates
Ingestion Telemetry
- Initial Cold Sync: 3,044 files (81 MB) transferred and hydrated in 18.2s.
- Warm Incremental Sync: 0 files updated in 0.0ms via manifest probe.
- Remote Memory Footprint: rust-analyzer held Bevy’s whole 97-crate Salsa database in 3,773 MB RSS, expanding to 26,692 MB during whole-program reachability scans.
- Local Laptop Load: Strictly 0.0% CPU, zero fan spin, zero battery drain.
2. Crate Topology & The Two Self-Loops
Using code_dependencies (Tool 4), we extracted Bevy’s full package dependency graph across 84 compiled library crates and 708 dependency edges:
⚡ prod-code Architecture & Dependency Graph Report
Scope: crates | Nodes: 84 | Dependencies: 708
🚨 CYCLES DETECTED: 2 circular dependency path(s) found:
1. bevy_math -> bevy_math
2. bevy_remote -> bevy_remote
The cycle detector flagged two direct dev-dependency self-loops: bevy_math -> bevy_math and bevy_remote -> bevy_remote. In Cargo workspaces, dev-dependencies can reference the crate itself for integration tests or benchmarks, but if tools fail to isolate dev-dependencies from regular builds, these cycles cause infinite recursion in topological compilation algorithms.
Coupling & Instability Metrics (Cₐ, Cₑ, I)
We calculated Robert C. Martin’s architectural coupling metrics:
- Afferent Coupling (Cₐ): Number of crates depending on this crate (incoming edges / stability requirement).
- Efferent Coupling (Cₑ): Number of crates this crate depends on (outgoing edges).
- Instability Index (I = Cₑ / (Cₐ + Cₑ)): 0.0 = purely stable bedrock; 1.0 = volatile leaf.
| Crate | Cₐ (Incoming) | Cₑ (Outgoing) | Instability (I) | Architectural Role |
|---|---|---|---|---|
bevy_reflect |
51 | 3 | 0.06 | Type reflection & dynamic serialization bedrock |
bevy_ecs |
49 | 5 | 0.09 | Entity-Component-System core data structures |
bevy_app |
46 | 6 | 0.12 | Application lifecycle & plugin builder |
bevy_platform |
44 | 0 | 0.00 | Pure leaf OS/runtime foundation |
bevy_math |
37 | 2 | 0.05 | Linear algebra, curves, bounding volumes |
bevy_asset |
34 | 7 | 0.17 | Asset server & loader pipelines |
bevy_utils |
31 | 1 | 0.03 | Core utilities, hashing, tracing helpers |
bevy_derive |
27 | 1 | 0.04 | Procedural derive macros |
bevy_camera |
24 | 14 | 0.37 | Camera views & projections |
bevy_color |
23 | 3 | 0.12 | Color spaces & conversions |
bevy_transform |
22 | 6 | 0.21 | Transform hierarchies & propagation |
bevy_image |
21 | 8 | 0.28 | Texture & image buffers |
bevy_log |
21 | 4 | 0.16 | Tracing & logging integration |
bevy_mesh |
17 | 9 | 0.35 | Mesh data & vertex layouts |
bevy_render |
14 | 23 | 0.62 | Render pipelines & GPU abstractions |
bevy_platform (Cₐ=44, Cₑ=0) is an absolute pure foundation crate with an instability index of 0.00. Meanwhile, bevy_reflect (Cₐ=51) serves as the single most critical structural bedrock of the engine, exceeding even bevy_ecs (Cₐ=49).
The 8-Module Circular Knot in bevy_ecs
Running Tarjan’s Strongly Connected Components (SCC) algorithm across module-level use crate:: paths inside crates/bevy_ecs/src revealed a massive circular knot:
Top-level modules in bevy_ecs: 17
Cyclic module SCC components in bevy_ecs: 1
SCC 1 (8 modules): reflect, schedule, world, system, query, entity, component, resource
All 8 primary sub-modules of Bevy’s ECS mutually depend on one another. An edit to Entity or Component immediately cascades through World, Query, System, and Schedule.
3. The AST Clone Harvester: 204 Exact Clones
Using code_find_duplicates (Tool 5), prod-code scanned all 1,917 Rust files for duplicate token blocks ($\ge 10$ lines):
- Type-1 Clones (Exact AST blocks): 204 clone groups (460 total instances).
- Type-2 Clones (Structural clones with renamed identifiers): 633 clone groups (1,619 total instances).
Where Do the Clones Live?
- Condition Evaluation Helpers: In
crates/bevy_ecs/src/schedule/condition.rs, 10 identical blocks duplicate condition evaluation logic. - Benchmark Iteration Boilerplate: 8 benchmarks under
benches/benches/bevy_ecs/iteration/duplicate identical setup boilerplate acrossiter_simple.rs,iter_simple_foreach_sparse_set.rs, etc. - Primitive Reflection Trait Implementations: In
crates/bevy_reflect/src/impls/core/primitives.rs, 14 occurrences of identical 10-line trait implementation blocks implementinto_partial_reflectacross scalar types. - Gizmo Drawing Pipelines: In
crates/bevy_gizmos/src/primitives/dim3.rs, 19 structural clones implement drawing loops across 3D primitive types.
prod-code automatically generated recommendations to fold these blocks using code_extract_function, potentially saving thousands of lines of maintenance overhead.
4. Bug #739: The Hidden Cargo Directory Trap in Fast-Sync
During Step 9 of our evaluation protocol (prod-code check --path crates/bevy_app), the remote compiler check failed with target resolution errors:
error: can't find example `computed_states` at path `/.../bevy-eval/examples/state/computed_states.rs`
error: can't find example `custom_transitions` at path `/.../bevy-eval/examples/state/custom_transitions.rs`
error: can't find example `states` at path `/.../bevy-eval/examples/state/states.rs`
error: can't find example `sub_states` at path `/.../bevy-eval/examples/state/sub_states.rs`
error: could not compile due to 4 previous target resolution errors
All four example files existed locally in the checkout, but were absent on the build node!
Root Cause Analysis
In crates/prod-code-mcp/src/sync.rs, the relevance filter is_relevant_code_or_manifest_file excluded non-code/cache directories:
if !under_code_dir
&& matches!(
s.as_ref(),
"target" | "node_modules" | "data" | "state" | "artifacts" | "research" | ...
)
{
return false;
}
However, under_code_dir only checked crates, packages, src, internal, pkg, cmd, api, Sources, Tests, include, lib. Standard Cargo directories—examples, example, tests, test, benches, bench—were omitted!
When fast-sync encountered examples/state/computed_states.rs, under_code_dir was false. When it reached the next directory component (state), it matched the blacklist and silently dropped the entire directory!
The Fix & Verification
- Reported as GitHub Issue #739 via
prod-code report-issue. - Expanded
under_code_dirincrates/prod-code-mcp/src/sync.rs:
if matches!(
s.as_ref(),
"crates"
| "packages"
| "src"
| "internal"
| "pkg"
| "cmd"
| "api"
| "Sources"
| "Tests"
| "include"
| "lib"
| "examples"
| "example"
| "tests"
| "test"
| "benches"
| "bench"
) {
under_code_dir = true;
}
- Bumped
RELEVANCE_VERSIONto 10 to trigger automatic rescan of affected directories. - Added regression test
cargo_examples_and_tests_under_state_and_data_are_relevant. - Committed (
ff6fcad) and deployed. Switched the local client build to an unoptimizeddevdebug profile, cutting compile times to 2 seconds. - Re-ran
prod-code check: all example targets synced and passed in 0.8s.
5. Semantic & Structural AST Search
Querying 661K lines of Rust without generating compiler-level AST indexes results in slow, noisy grep searches.
Semantic Natural Language Search (code_search)
Searching for spawn entity with bundle components indexed and ranked 45,503 declarations across 1,913 files in 492 ms:
10 hit(s) for `spawn entity with bundle components` in 492 ms (45,503 declarations, 1,913 files)
1. [function] Commands::spawn crates/bevy_ecs/src/system/commands/mod.rs:283
pub fn spawn<T: Bundle>(&mut self, bundle: T) -> EntityCommands<'_>
- [`spawn_empty`](Self::spawn_empty) to spawn an entity without any components.
2. [function] RelatedSpawner::spawn crates/bevy_ecs/src/relationship/related_methods.rs:583
pub fn spawn(&mut self, bundle: impl Bundle) -> EntityWorldMut<'_>
Spawns an entity with the given `bundle` and an `R` relationship...
3. [function] World::spawn crates/bevy_ecs/src/world/mod.rs:1243
pub fn spawn<B: Bundle>(&mut self, bundle: B) -> EntityWorldMut<'_>
Structural AST Pattern Search (code_structural_search)
We searched for all occurrences of the AST pattern $x.is_empty():
⚡ prod-code Structural AST Search: `$x.is_empty()`
────────────────────────────────────────────────────
510 match(es) in 223 file(s) (1,917 scanned in 374.56 ms)
In 374 ms, prod-code scanned 1,917 Rust files and extracted 510 exact syntax matches across 223 files with bound meta-variables.
6. Type Hierarchy & The 28 Traits of Entity
Bevy’s central currency is the Entity struct. We used code_supertypes (Tool 14) and code_hover (Tool 15) to inspect its exact representation:
📍 Definition: crates/bevy_ecs/src/entity/mod.rs:447:12
pub struct Entity {
#[cfg(target_endian = "little")]
index: EntityIndex,
generation: EntityGeneration,
#[cfg(target_endian = "big")]
index: EntityIndex,
}
Trait Hierarchy Discovery
Entity implements 28 distinct traits across standard library and ECS contracts:
QueryData,ReadOnlyQueryData,SingleEntityQueryData,ContiguousQueryData,ArchetypeQueryData,IterQueryData,ReleaseStateQueryDataWorldQuery,WorldEntityFetchRelationshipSourceCollection,MapEntities,SparseSetIndex,ContainsEntity,EntityEquivalentFromTemplate,Reflect,Serialize,DeserializeClone,Copy,Debug,Display,Eq,PartialEq,Ord,PartialOrd,Hash,FromStr
Using code_references (Tool 10), prod-code traced 3,160 incoming references to Entity across all 97 crates in 2.57 seconds.
Call Graph Navigation (code_callers & code_callees)
code_callersonApp::run: identified 300+ incoming caller sites across examples, benchmarks, and tests in 1.1s.code_calleesonApp::run: identified its 5 core outgoing dispatch targets (tracing::Span::entered,App::is_building_plugins,core::mem::replace,Box::new,App::empty).
7. Program Slicing: Bounding Massive ECS Structures
When an LLM coding agent inspects a symbol in a 661K-line codebase, reading entire files consumes the context window. code_slice (Tool 8) computes minimal forward/backward data-flow closures.
Slicing Entity (99% Reduction)
$ prod-code slice crates/bevy_ecs/src/entity/mod.rs --line 447 --depth 1
BOUNDED slice of `Entity`: 3 item(s), 371 bytes from 66,477 bytes of source (99% smaller)
The slice trimmed 66 KB of source down to 371 bytes containing only EntityIndex, EntityGeneration, and Entity.
Cross-Crate Slicing App (99% Reduction)
$ prod-code slice crates/bevy_app/src/app.rs --line 85 --depth 1
BOUNDED slice of `App`: 4 item(s), 825 bytes from 100,872 bytes of source (99% smaller)
The slice walked across crate boundaries, returning App and SubApps from bevy_app, and pulling in ErrorHandler from crates/bevy_ecs/src/error/handler.rs.
8. Guarded AST Refactorings & Integrity Loop
We tested semantic transformations under our strict 5-step integrity loop (Execute → Diff → Compile → Test → Revert).
Multi-Callsite Semantic Rename
We renamed is_building_plugins to is_any_subapp_building_plugins using code_rename:
--- a/crates/bevy_app/src/app.rs
+++ b/crates/bevy_app/src/app.rs
@@ -156,7 +156,7 @@ impl App {
pub fn update(&mut self) {
- if self.is_building_plugins() {
+ if self.is_any_subapp_building_plugins() {
panic!("App::update() was called while a plugin was building.");
}
@@ -185,7 +185,7 @@ impl App {
pub fn run(&mut self) -> AppExit {
- if self.is_building_plugins() {
+ if self.is_any_subapp_building_plugins() {
panic!("App::run() was called while a plugin was building.");
}
@@ -297,7 +297,7 @@ impl App {
- pub(crate) fn is_building_plugins(&self) -> bool {
+ pub(crate) fn is_any_subapp_building_plugins(&self) -> bool {
self.sub_apps.iter().any(SubApp::is_building_plugins)
}
prod-code renamed the definition and updated all call-sites in 2.57 seconds. We validated compilation and ran 20 unit tests remotely via prod-code exec (100% passed), then cleanly reverted with git checkout -- ..
Safe-Delete Guard Refusal
When attempting to delete Entity via code_safe_delete:
safe delete refused: prodCode/safeDelete failed: 3160 usage(s) reference this item; delete refused:
crates/bevy_transform/src/helper.rs:6:14
crates/bevy_ecs/src/query/filter.rs:5:24
crates/bevy_ecs/src/query/mod.rs:118:35
… 3140 more
The semantic guard refused the deletion and listed all 3,160 incoming usages.
Invert Boolean Predicate
Using code_invert_boolean, we inverted any_spawned to is_empty_spawned in crates/bevy_ecs/src/entity/mod.rs:
- pub fn any_spawned(&self) -> bool {
- self.meta.iter().any(|meta| meta.location.is_some())
- }
+ pub fn is_empty_spawned(&self) -> bool {
+ !(self.meta.iter().any(|meta| meta.location.is_some()))
+ }
The analyzer verified the negation in scope with 0 compiler errors.
Dead-Code Orphan Prune Guard
Running prod-code prune flagged two unreferenced items in benchmarks:
the analyzer rejects the result:
not all trait items implemented, missing: `fn from` [E0046] (benches/benches/bevy_ecs/entity_cloning.rs:229:6)
nothing was written
prod-code detected that removing fn from would break the impl From<FilterScenario> for String trait implementation, and automatically aborted the change!
9. Speculative Synthesis, RAM Validation & Shadow Runs
In-Memory Pre-Flight Validation (code_validate_edit)
We injected a deliberate type error into an in-memory buffer of crates/bevy_app/src/app.rs:
app.sub_apps.main.update_schedule = "invalid string type";
Without saving anything to disk, prod-code validate evaluated the buffer in server RAM against the live Salsa database:
crates/bevy_app/src/app.rs: 1 error(s), 1 warning(s)
(131 diagnostic(s) the file already had before this edit are not counted)
error: expected Option<Interned<dyn ScheduleLabel>>, found &'static str [E0308] (crates/bevy_app/src/app.rs:111:45)
[prod-code] analysed in 10.66s
The analyzer caught the exact type mismatch in 10.66s while ignoring 131 pre-existing baseline warnings.
Typed Builder Synthesis (code_generate_fixture)
We synthesized a typed builder fixture for EntityLocation:
$ prod-code fixture EntityLocation --builder --path crates/bevy_ecs/src/entity/mod.rs
builder `EntityLocationBuilder` for `EntityLocation` (4 fields), declared in crates/bevy_ecs/src/entity/mod.rs:1278-1299;
verified: the analyzer checked it in the scope of the declaration: 0 errors
nothing was written
prod-code generated EntityLocationBuilder with 4 fields, typed setters, and validation errors, fully verified in AST context.
Expression Synthesis (code_propose_expression)
Querying prod-code propose-expression crates/bevy_app/src/app.rs 301 bool synthesized 22 ranked in-scope boolean expressions:
!_bevy_app_run_span(Confidence: 70%)!overall_plugins_state(Confidence: 70%)app.is_empty()(Confidence: 65%)
Multi-Hypothesis Shadow Runs (code_shadow_run)
We submitted two concurrent hypotheses to prod-code shadow-run to test competing patches in isolated copy-on-write RAM overlays on the cluster:
$ prod-code shadow-run spec.json -- cargo check -p bevy_app
candidate_valid exit 0 in 6.4s ~0 changed line(s) <- winner
candidate_broken exit 101 in 6.4s ~2 changed line(s)
winner: candidate_valid
--- candidate_broken output ---
error[E0425]: cannot find type `BrokenNonExistentType` in this scope
Both hypotheses ran in parallel on dedicated cluster cores, selecting the winning candidate in 6.4 seconds without touching the local working tree.
10. Remote 32-Core Build, Lint & Bench via prod-code exec
All build, test, and benchmark runs executed remotely on node booster via prod-code exec, returning structured execution metrics:
# Remote Cargo Check
$ prod-code exec -- cargo check -p bevy_app
Finished `dev` profile in 0.14s
[prod-code exec] exit 0 in 0.8s (server 0.4s, cpu 0.1s user, peak 101 MB)
# Remote Unit & Doc Tests (88 tests)
$ prod-code exec -- cargo test -p bevy_app
running 54 tests ... 54 passed; 0 failed
Doc-tests bevy_app ... 34 passed; 0 failed; 1 ignored
[prod-code exec] exit 0 in 1.6s (server 1.2s, cpu 1.0s user, peak 308 MB)
# Remote Clippy
$ prod-code exec -- cargo clippy -p bevy_app -- -D warnings
Finished `dev` profile in 0.17s
[prod-code exec] exit 0 in 0.8s (server 0.3s, cpu 0.1s user, peak 101 MB)
# Remote Release Benchmark Suite
$ prod-code exec -- cargo test -p bevy_app --release -- --nocapture
Finished `release` profile in 16.58s
running 54 tests ... 54 passed; 0 failed
Doc-tests bevy_app ... 34 passed; 0 failed; 1 ignored
[prod-code exec] exit 0 in 18.0s (server 17.6s, cpu 13.2s user, peak 403 MB)
Conclusion & Evaluation Summary
| Benchmark Suite | Evaluated Scope | Outcome & Latency |
|---|---|---|
| 1. Node Topology & Sync | 661K lines, 3,044 files across 97 crates | 18.2s cold sync (81 MB), 613 µs LAN RTT, 0% laptop CPU |
| 2. Architecture & DAG | 84 crates, 708 edges, 17 ECS modules | 2 circular dev-loops detected; 8-module circular knot in bevy_ecs |
| 3. AST Clone Harvester | 1,917 files scanned | 204 Type-1 and 633 Type-2 clone groups detected |
| 4. Fast-Sync Bug Fix | Issue #739 in is_relevant_code_or_manifest_file |
Bug identified, reported, fixed in sync.rs (v10), deployed in 1 turn |
| 5. Semantic AST Search | 45,503 declarations in 1,913 files | 492 ms intent query; 374 ms structural AST pattern match (510 hits) |
| 6. Navigation & Supertype Hierarchy | Entity & Plugin across all crates |
Entity implements 28 traits; 3,160 usages traced in 2.57s |
| 7. Program Slicing | Entity & App symbol bounds |
99% context reduction (66 KB → 371 B; 100 KB → 825 B) |
| 8. Semantic Guards | Multi-callsite rename & safe-delete | Rename in 2.57s; safe-delete refused on 3,160 usages; prune guarded |
| 9. Synthesis & Pre-flight | In-memory RAM typecheck & shadow runs | RAM caught E0308 in 10.66s (0 disk I/O); shadow run picked winner in 6.4s |
| 10. Remote Execution | 32-core AMD EPYC on booster |
Check in 0.8s, 88 tests in 1.6s, Clippy in 0.8s, Release in 18.0s |
By shifting language server databases, AST compilation, and test execution to a cluster node via prod-code, even monolithic 661,000-line Rust codebases like Bevy can be navigated, sliced, and refactored by AI coding agents with zero local latency and zero laptop battery drain.
Cite this article
Alexander Panasenko (2026-09-30). 204 Code Clones and Two Self-Loops: Dissecting Bevy's 97-Crate Engine with 67 AST Tools. https://prod.codes/blog/204-code-clones-and-two-self-loops-inside-bevy/