notes · · 9 min

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. 1. Node Topology & Fast-Sync Ingestion
  2. 2. Crate Topology & The Two Self-Loops
  3. 3. The AST Clone Harvester: 204 Exact Clones
  4. 4. Bug #739: The Hidden Cargo Directory Trap in Fast-Sync
  5. 5. Semantic & Structural AST Search
  6. 6. Type Hierarchy & The 28 Traits of Entity
  7. 7. Program Slicing: Bounding Massive ECS Structures
  8. 8. Guarded AST Refactorings & Integrity Loop
  9. 9. Speculative Synthesis, RAM Validation & Shadow Runs
  10. 10. Remote 32-Core Build, Lint & Bench via prod-code exec
  11. 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?

  1. Condition Evaluation Helpers: In crates/bevy_ecs/src/schedule/condition.rs, 10 identical blocks duplicate condition evaluation logic.
  2. Benchmark Iteration Boilerplate: 8 benchmarks under benches/benches/bevy_ecs/iteration/ duplicate identical setup boilerplate across iter_simple.rs, iter_simple_foreach_sparse_set.rs, etc.
  3. Primitive Reflection Trait Implementations: In crates/bevy_reflect/src/impls/core/primitives.rs, 14 occurrences of identical 10-line trait implementation blocks implement into_partial_reflect across scalar types.
  4. 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

  1. Reported as GitHub Issue #739 via prod-code report-issue.
  2. Expanded under_code_dir in crates/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;
}
  1. Bumped RELEVANCE_VERSION to 10 to trigger automatic rescan of affected directories.
  2. Added regression test cargo_examples_and_tests_under_state_and_data_are_relevant.
  3. Committed (ff6fcad) and deployed. Switched the local client build to an unoptimized dev debug profile, cutting compile times to 2 seconds.
  4. Re-ran prod-code check: all example targets synced and passed in 0.8s.

Querying 661K lines of Rust without generating compiler-level AST indexes results in slow, noisy grep searches.

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<'_>

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, ReleaseStateQueryData
  • WorldQuery, WorldEntityFetch
  • RelationshipSourceCollection, MapEntities, SparseSetIndex, ContainsEntity, EntityEquivalent
  • FromTemplate, Reflect, Serialize, Deserialize
  • Clone, 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_callers on App::run: identified 300+ incoming caller sites across examples, benchmarks, and tests in 1.1s.
  • code_callees on App::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
Citation
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/