Health Report: apollo-federation-ruby
Quick Insights
Key findings from the analysis - prioritized by impact
Healthy Codebase
Overall health score of 89 indicates a well-maintained codebase. Continue current practices and address issues as they arise.
Executive Summary
The apollo-federation-ruby codebase scores 88.7/100 (grade B) across 46 files and 74 classes, with zero critical issues and zero architectural smells. Production code is nearly pristine -- no library file scores below A-. The score has been flat at 88 for nearly a year, declining only 1 point since February 2025, indicating a stable and mature codebase with no active degradation.
However, stability masks two structural risks. First, knowledge ownership is dangerously concentrated: the tracing subsystem (tracer.rb, node_map.rb, proto.rb) is effectively owned by a single contributor (Lenny Burdette), and this same subsystem is the most complex and defect-prone area of the codebase, with repeated breakage against upstream graphql-ruby API changes. If Lenny becomes unavailable, no one else has sufficient context to maintain the tracing layer. Second, 4 production files (enum.rb, input_object.rb, scalar.rb, union.rb) are byte-for-byte identical copies differing only in module name, representing a missing abstraction that creates a real divergence risk when adding new directives.
The path forward is straightforward: cross-train a second developer on the tracing subsystem, extract the two missing shared modules from duplicated production files, and clean up the 6 stale TODOs (some over 6 years old) that clutter entity resolution logic. Cohesion (67/100) is the weakest metric and the only one with a declining trend, driven primarily by FederatedDocumentFromSchemaDefinition (LCOM 15) and the Tracer classes (LCOM 13), though some of this is inflated by Ruby's mixin pattern.
Recommendations
Prioritized actions to improve code health
lib/apollo-federation/tracing/tracer.rb (283 lines, cognitive complexity 52) is the most complex and defect-prone file, owned primarily by Lenny Burdette. The entire tracing subsystem (tracer.rb, node_map.rb, proto.rb, node_map_spec.rb, generate-protos.sh) has a bus factor of 1. Pair another developer into this subsystem and require cross-reviewer assignments on all tracing PRs.
lib/apollo-federation/enum.rb, input_object.rb, scalar.rb, and union.rb are byte-for-byte identical (23 lines each, 92 duplicated lines total). Extract a shared module (e.g., ApolloFederation::DirectiveType) providing the ClassMethods with tag() and inaccessible(). This eliminates 69 lines and removes the risk of adding a directive to one file but not the others.
lib/apollo-federation/argument.rb and lib/apollo-federation/enum_value.rb are entirely identical (25 duplicated lines each). Extract a shared module containing VERSION_2_DIRECTIVES, the initialize override, and add_v2_directives. This eliminates a likely source of divergence bugs when new v2 directives are added.
lib/apollo-federation/has_directives.rb is the most structurally central module (PageRank 0.033, 9 dependents) but has no dedicated test file. lib/apollo-federation/entity.rb and lib/apollo-federation/federated_document_from_schema_definition.rb are also untested directly. These are core federation modules that should not rely solely on indirect coverage.
The worst-graded file in the codebase (C+, score 77.6) with 49.7% duplication and cyclomatic complexity 56. Extract shared test setup and repeated assertion patterns. Consider sharing spec helpers with service_field_v1_spec.rb (35.3% duplication, grade B+) to lift both files.
entities_field.rb contains 4 TODOs, two dating to the initial commit (2019). Remove the stale TODO at line 41 (question already answered in practice) and at any.rb line 10 (decision made implicitly). For entities_field.rb lines 49 and schema.rb line 82 (interface @key support), either implement or document the limitation and remove the TODOs.
Churn analysis shows repeated breakage of tracer.rb against upstream graphql-ruby API changes (2.3, 2.5.12+). An abstraction layer isolating version-specific tracer behavior would reduce defect risk and simplify future graphql-ruby upgrades.
LCOM score of 15 (16 methods, 1 shared field) makes this the least cohesive class. Most methods are independent build_*_node wrappers. Consider grouping related builder methods into smaller collaborator classes or a builder pattern to improve cohesion from its current 67/100 component score.
Currently at 9 dependents (hub threshold is 20). This is the closest module to becoming a coupling bottleneck. Track fan-in as new federation type extensions are added.
At cognitive complexity 52 and cyclomatic 47, tracer.rb is the closest file to becoming a hotspot. If change frequency increases above its current 4 commits/197 days, it should be split into separate legacy-tracer and trace-based modules.
7 files have 100% single-owner concentration. Enforce a policy that PRs touching silo files must be reviewed by someone other than the primary owner to gradually spread knowledge.
Cohesion is the only component with a negative slope (-0.29). If it drops below 65, prioritize decomposition of the highest-LCOM classes. Current trajectory suggests this could happen within 2-3 releases if the pattern from graphql-ruby compatibility changes repeats.
Key Findings
Ownership concentration: 50% of files (23/46) are primarily owned by Rylan Collins. The tracing subsystem has a bus factor of 1 (Lenny Burdette owns 100% of proto.rb, 84% of node_map.rb, 54% of tracer.rb). 7 files are complete single-owner silos.
Tracing subsystem fragility: lib/apollo-federation/tracing/tracer.rb is the most complex file (cognitive 52, cyclomatic 47) and has the highest sustained source-code churn (4 commits over 197 days). Commit messages reveal repeated breakage against graphql-ruby version changes, signaling fragile upstream coupling.
Production code duplication -- missing abstractions: enum.rb, input_object.rb, scalar.rb, and union.rb are identical 23-line files (92 duplicated lines total). argument.rb and enum_value.rb are another identical pair (50 duplicated lines). These represent two missing shared modules.
Test file duplication: All debt below grade A is concentrated in spec files. spec/apollo-federation/service_field_v2_spec.rb is the worst file in the codebase (grade C+, score 77.6) with 49.7% duplication and cyclomatic complexity of 56.
Stale technical debt: 6 of 8 SATD items are over 3 years old. 4 TODOs cluster in entities_field.rb around unresolved design questions about type lookup, error handling, and interface key support. At least 2 TODOs (any.rb line 10, entities_field.rb line 41) are fully stale and should be removed.
Cohesion is the weakest metric: Scored 67/100 with a declining trend (slope -0.29). 48 of 74 classes have LCOM violations. FederatedDocumentFromSchemaDefinition (LCOM 15, 16 methods, 1 shared field) is the strongest candidate for decomposition.
Test coverage gaps in central modules: lib/apollo-federation/has_directives.rb (9 dependents, highest PageRank) has no dedicated spec. lib/apollo-federation/entity.rb and lib/apollo-federation/federated_document_from_schema_definition.rb also lack direct test files.
Zero architectural smells: No cyclic dependencies, no hub modules, no central connectors. The dependency graph is clean with 46 nodes, 73 edges, and balanced instability (avg 0.44).
Low Cohesion Classes
Classes doing too many unrelated things - candidates for splitting
The weakest component at 67/100, with a slight declining trend (69 to 67, slope -0.29). 48 out of 74 classes have LCOM violations. The worst offenders are FederatedDocumentFromSchemaDefinition (LCOM 15, 16 methods sharing only 1 field) and the Tracer classes (LCOM 13, 13 methods sharing 0 fields). The Object module (LCOM 8, 8 methods, 1 field) and Schema module (LCOM 8, 11 methods, 4 fields) also contribute. Many LCOM violations stem from Ruby's module/mixin pattern where methods are defined without direct field access, inflating LCOM artificially. However, FederatedDocumentFromSchemaDefinition is a genuine candidate for decomposition given its 16 methods and single shared field.
| Class | File | Language | LCOM | WMC | CBO |
|---|
Dependency Structure
Import graph analysis - cycles, bottlenecks, and instability
The dependency graph is structurally healthy: 46 nodes, 73 edges, and zero import cycles. The most central file by PageRank is lib/apollo-federation/has_directives.rb (PageRank 0.033, in-degree 9, out-degree 0). It is a pure dependency sink used by 9 GraphQL type modules (object, interface, field, enum, enum_value, scalar, union, input_object, argument) and has no outgoing dependencies, which is the correct posture for a foundational module. However, no spec file directly targets has_directives.rb -- it is only tested indirectly through its consumers. Given its centrality, a dedicated spec would reduce risk. The highest betweenness node is lib/apollo-federation.rb (betweenness 0.070), which also has the highest fan-out at 15 outgoing edges against only 2 incoming edges (instability 0.88). This is expected for a top-level entry point that re-exports the library, but any change to its require list ripples across the entire graph. lib/apollo-federation/schema.rb is the second most structurally important library file (betweenness 0.028, in-degree 5, out-degree 3) and acts as the bridge between the schema definition layer (entities_field, service_field, entity) and the rest of the library; it is well-covered by schema_spec, entities_field_spec, and both service_field specs. lib/apollo-federation/field_set_serializer.rb (in-degree 4, PageRank 0.012) is a second foundational sink depended on by object, interface, and field, and it does have a dedicated spec. Two library files lack any direct test coverage: lib/apollo-federation/entity.rb and lib/apollo-federation/federated_document_from_schema_definition.rb. entity.rb is only reachable through schema.rb and federated_document_from_schema_definition.rb is an isolated leaf with zero in-degree -- neither has a corresponding spec file. The example/ subtree is cleanly isolated behind example/graphql_server.rb (betweenness 0.048), which funnels all four example services into the library via a single edge to lib/apollo-federation.rb. No structural actions are urgently needed, but adding targeted specs for has_directives.rb, entity.rb, and federated_document_from_schema_definition.rb would harden the most central and least-tested parts of the graph.
| File | Lang | PageRank | Betweenness | In | Out | Instability |
|---|
Known Issues
Problems the team has documented but not yet fixed (TODO, FIXME, HACK)
All 8 SATD items are TODOs (lowest severity), but 6 of 8 are over 3 years old, dating back to the project's earliest commits (2019-2020). The most concerning pattern is a cluster of 4 TODOs in entities_field.rb, the core entity resolution logic. Two of these (lines 41 and 49) question fundamental design decisions about type lookup and interface handling that were never resolved. The interface-related TODOs in entities_field.rb line 49 and schema.rb line 82 represent an acknowledged gap in federation spec compliance: interfaces with @key directives are silently ignored. The error handling TODO at entities_field.rb line 44 means entity resolution failures produce generic RuntimeErrors instead of catchable typed exceptions. Overall debt density is low (0.38) and the weighted count (2.0) is modest, but the age and clustering in entity resolution indicate these are abandoned questions rather than planned work.
By Severity
By Category
| Severity | Category | File | Lang | Line | Comment |
|---|
Duplicated Code
Code that appears in multiple places - bugs fixed in one spot may need fixing elsewhere
Overall duplication is low at 4.1% (258 duplicated lines across 6,339 total), but the duplication that exists is structurally significant -- it reveals two missing abstractions in the production code.
[Group 5 -- 4 identical files, 92 total duplicated lines] The files enum.rb, input_object.rb, scalar.rb, and union.rb are byte-for-byte identical (23 lines each). Each defines a module with the same self.included hook, the same ClassMethods block including HasDirectives, and the same tag/inaccessible directive methods. The only difference across files is the module name. This is a textbook case for a shared mixin. Extract a common module (e.g., ApolloFederation::HasTagAndInaccessible or ApolloFederation::DirectiveType) that provides the ClassMethods with tag() and inaccessible(), then have each of these four modules simply include it. This would eliminate roughly 69 lines (3 of the 4 copies) and, more importantly, ensure that any future directive additions only need to be made in one place.
[Groups 1 and 3 -- argument.rb and enum_value.rb, 50 total duplicated lines] These two files are entirely identical: same initialize method that strips custom kwargs and calls super, same add_v2_directives private method. They share a different pattern from Group 5 (instance-level mixin with constructor override vs. class-level extension), but the logic is the same. Extract a shared module (e.g., ApolloFederation::V2DirectiveInitializer) containing VERSION_2_DIRECTIVES, the initialize override, and add_v2_directives. Both Argument and EnumValue would then just include it. This eliminates roughly 25 lines and removes a real bug risk: if a new v2 directive is added to one file but not the other.
[Group 2 -- tracer.rb internal clone, 22 duplicated lines] The execute_field and execute_field_lazy instance methods (lines 53-75) are identical 11-line wrappers that delegate to their self-class-method counterparts with the same argument unpacking. This is a minor within-file clone driven by the graphql-ruby tracer API requiring separate methods for eager and lazy field execution. The delegation pattern is nearly identical. A small helper method could reduce this, but the benefit is marginal given it is a single file and the methods are intentionally parallel.
[Group 4 -- integration test clone, 94 lines, 73% similarity] Two test blocks in integration.test.js share the same structure: build a query, call executeOperation, assert no errors, and compare data. This is typical integration test boilerplate. The difference is only in the query content and expected data. A shared helper like assertQueryResult(gatewayServer, query, expectedData) could reduce this, but test readability often benefits from explicitness, so this is low priority.
Recommended actions in priority order: (1) Extract a shared directive-type mixin to deduplicate enum.rb, input_object.rb, scalar.rb, and union.rb -- highest impact at 69 lines saved and 4 files that currently must be kept in sync manually. (2) Extract a shared v2 directive initializer module for argument.rb and enum_value.rb -- 25 lines saved and eliminates a likely source of divergence bugs. (3) The tracer.rb and test file clones are low risk and can be left as-is.
Duplication Level
Technical Debt Gradient
Per-file debt scores - prioritize cleanup where it matters most
The codebase is in excellent health. Of 46 files, 37 score A and 4 score A-, meaning 89% of the codebase carries no meaningful technical debt. The grade distribution has no D or F grades, and there are zero critical defects across all files.
The only dimension driving debt is code duplication, which is the sole penalty source in nearly every penalized file. There is no multi-dimensional compounding -- only one file (spec/apollo-federation/service_field_v2_spec.rb, grade C+) is penalized on two dimensions simultaneously.
Worst files by grade:
spec/apollo-federation/service_field_v2_spec.rb (C+, score 77.6) -- The only file below B. It compounds high cyclomatic complexity (56) with severe duplication (49.7%). This is the single file most worth refactoring: extract shared test setup and repeated assertion patterns to reduce both duplication and structural complexity.
spec/apollo-federation/tracing_spec.rb (B, score 86.0) -- Deep nesting (7 levels) combined with 30.2% duplication. Flatten nested describe/context blocks and extract shared examples.
spec/apollo-federation/service_field_v1_spec.rb (B+, score 87.9) -- 35.3% duplication. Likely shares duplicated patterns with the v2 spec above; consider shared spec helpers between v1 and v2.
spec/apollo-federation/schema_spec.rb (B+, score 89.6) -- 27.1% duplication.
spec/apollo-federation/entities_field_spec.rb (B+, score 89.7) -- 26.3% duplication.
All debt is concentrated in test files under spec/. The library source code (lib/) is nearly pristine -- only lib/apollo-federation/field.rb and lib/apollo-federation/tracing/tracer.rb carry minor duplication penalties (~10%), both still grading A-. No production code file scores below A-.
Key takeaway: duplication in test files is the only systemic pattern. The highest-impact action is refactoring spec/apollo-federation/service_field_v2_spec.rb by extracting shared test contexts and reducing nested complexity. Addressing the duplication across the service_field v1/v2 specs together would likely lift both files significantly.
Grade Distribution
| File | Lang | Score | Grade | Structural | Semantic | Duplication | Coupling |
|---|
Historical Trends
How code quality has changed over time
The codebase is stable with a very slight downward trend (slope: -0.14, R-squared: 0.43). The overall score dropped 1 point from 89 to 88 between Feb and Apr 2025, then held steady for nearly a year. The initial drop was driven by minor declines in duplication (92 to 91) and cohesion (69 to 67), coinciding with graphql-ruby 2.3 compatibility work (commits #294, #282) and the v3.10.0/v3.10.1 releases. Since Apr 2025, the score has been flat at 88 despite significant CI/infrastructure churn (GitHub Actions migration, semantic-release fixes). Complexity (95), smells (100), TDG (93), and SATD (86) have shown zero trend over the entire period. Cohesion is the weakest and most declining component (slope: -0.29), sitting at 67 with an average LCOM of 3.3 across 74 classes. Coupling shows a marginal decline from 88 to 87 in the latest measurement. This is a mature, low-activity library where the health metrics are essentially flat -- no intervention is urgently needed, but cohesion remains the primary area for improvement.
Health Score Over Time
Component Trends
Notable Events
| Period | Change | Driver | Description |
|---|---|---|---|
| Feb 2025 | 0 pts | complexity | |
| Apr 2025 | -1 pts | cohesion | |
| Oct 2025 | 0 pts | coupling | |
| Feb 2026 | 0 pts | coupling |
Change Activity
How frequently code is being modified
Churn in this repository is low overall (mean churn score 0.21, median 0.16) with most activity concentrated in non-source files. The top 5 files by raw change volume account for 54% of all additions and deletions, and three of those five are generated or configuration artifacts: Gemfile.lock (8 commits, 257 days active), CHANGELOG.md (6 commits, automated releases), and version.rb (7 commits, all semantic-release bumps). These are expected churn from the release pipeline and do not indicate design problems. The one source file with genuinely sustained and concerning churn is lib/apollo-federation/tracing/tracer.rb (4 commits over 197 days, relative churn 0.30). Its commit history is dominated by bug fixes: 'tracer crash with graphql-ruby 2.5.12+', 'add support for multiplex', 'properly handle parsing and validation errors'. This repeated fix pattern across different graphql-ruby versions signals a fragile coupling to upstream API changes; the tracer module is the primary defect-prone area in the codebase. Its companion test file spec/apollo-federation/tracing_spec.rb mirrors this pattern with a high relative churn of 0.73 across 705 LOC. The .github/workflows/ci.yml file shows an intense burst of 7 commits in just 19 days (relative churn 1.14), but these are all CI configuration adjustments for the CircleCI-to-GitHub-Actions migration and semantic-release token setup -- operational churn, not a code quality signal. The proto file apollo_pb.rb has the highest relative churn (6.07) but from a single commit that rewrote most of the file; this is a one-time regeneration, not a pattern. Overall, the only actionable finding is the tracer module's repeated breakage against upstream graphql-ruby changes, which would benefit from an adapter or abstraction layer to isolate version-specific behavior.
| File | Lang | Changes | Contributors | Churn Score |
|---|
Code Ownership
How knowledge is distributed - concentrated knowledge is a risk
Ownership is dangerously concentrated around Rylan Collins, who is the primary owner of 23 out of 46 files (50%), including 4 sole-ownership silos in core library code (entity.rb, service.rb) and infrastructure (jest.config.js, babel.config.js). Lenny Burdette is the sole knowledge holder for the entire tracing subsystem (tracing/proto.rb, tracing/node_map.rb, and primary owner of tracing/tracer.rb). Dan Weinand owns 5 near-identical type extension files (argument.rb, enum_value.rb, enum.rb, input_object.rb, scalar.rb, union.rb) with only Joel Turkel as a minor secondary contributor. There are 7 complete silos (100% single-owner files) and 23 high-risk files total. The bus factor of 3 is misleading -- it reflects the repo-wide metric, but at the subsystem level the bus factor is effectively 1 for tracing (Lenny Burdette) and for core federation types (Dan Weinand). Recommended actions: (1) Require cross-reviewer assignments so Rylan Collins's files get reviewed by at least one other contributor. (2) Pair another developer into the tracing subsystem urgently, as tracer.rb (283 lines) is the most complex single-owner-dominated file in the project. (3) Have Dan Weinand or Joel Turkel document the pattern used in the type extension files so others can maintain them.
Top Contributors
Score Breakdown
How the health score is calculated from individual components
Glossary of Terms
▼Health Score
A weighted composite metric (0-100) measuring overall code quality. Above 80 is good, 60-80 needs attention, below 60 is concerning.
Hotspot
A file that is both complex AND frequently changed. These are the highest-risk areas because they're hard to change safely but get changed often.
Bus Factor
The minimum number of people who would need to leave before critical knowledge is lost. Higher is better; 1 is a serious risk.
Knowledge Silo
A file that only one person has ever touched. If that person leaves, the knowledge goes with them.
Cyclomatic Complexity
The number of independent paths through code. More paths means more test cases needed and more ways things can go wrong.
Cognitive Complexity
How hard code is to understand. Accounts for nesting, breaks in flow, and things that make humans struggle to follow the logic.
LCOM (Cohesion)
How well a class's methods work together. High LCOM means methods don't share data - the class is probably doing too many unrelated things.
CBO (Coupling)
How many other classes a class depends on. High coupling means changes ripple through the codebase - everything is connected to everything.
Churn Rate
How frequently a file changes. High churn indicates instability - the code may be unclear, have bugs, or be undergoing active development.
Self-Admitted Technical Debt (SATD)
Code issues the team has documented (TODO, FIXME, HACK, XXX). These represent known shortcuts that need eventual attention.
Code Clone / Duplicate
Similar code appearing in multiple places. When a bug is fixed in one spot, it may need fixing elsewhere. Candidates for refactoring into shared functions.
Technical Debt Gradient
Per-file quality score (0-100) combining complexity, duplication, coupling, and other factors. Lower scores mean more accumulated debt.
Temporal Coupling
Files that frequently change together in the same commits. High temporal coupling suggests hidden dependencies not visible in the import graph.
Architectural Smell
A structural pattern that violates design principles: cyclic dependencies, hub-like modules, or stable code depending on unstable code.
Instability
Ratio of outgoing to total dependencies. 0 = maximally stable (many things depend on it), 1 = maximally unstable (depends on many things).
PageRank
Importance of a file in the dependency graph. Files with high PageRank are depended on by many other important files.