Health Report: apollo-federation-ruby

2026-02-01T02:06:27.555996Z 1y history
89Health Score
Declining
-0.14 pts/mo
46
Files Analyzed
0
Critical Issues
4.1%
Duplication

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

Critical - Address Soon
Cross-train a developer on the tracing subsystem

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.

Extract shared directive-type mixin from 4 identical files

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.

Extract shared v2 directive initializer for argument.rb and enum_value.rb

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.

Add dedicated specs for has_directives.rb, entity.rb, and federated_document_from_schema_definition.rb

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.

Important - Plan For
Refactor spec/apollo-federation/service_field_v2_spec.rb

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.

Clean up stale TODOs in entity resolution code

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.

Introduce an adapter layer for graphql-ruby version compatibility in tracer.rb

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.

Improve cohesion of FederatedDocumentFromSchemaDefinition

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.

Maintenance - Keep Doing
Monitor has_directives.rb fan-in growth

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.

Monitor tracer.rb complexity against churn

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.

Require cross-reviewer assignments on ownership silos

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.

Track cohesion trend across releases

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.

ClassFileLanguageLCOMWMCCBO

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.

46
Modules
73
Dependencies
3.2
Avg Degree
0
Dependency Cycles
FileLangPageRankBetweennessInOutInstability

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.

8
Total Issues
0
Critical
0
High
0
Medium

By Severity

By Category

SeverityCategoryFileLangLineComment

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.

4.1%
Duplication Rate
5
Clone Groups
258
Duplicate Lines
6339
Total Lines

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:

  1. 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.

  2. 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.

  3. 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.

  4. spec/apollo-federation/schema_spec.rb (B+, score 89.6) -- 27.1% duplication.

  5. 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.

46
Files Scored
93.7
Average Score
A
Average Grade
0
Failing Files

Grade Distribution

FileLangScoreGradeStructuralSemanticDuplicationCoupling

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.

68
Total Changes
34
Files Changed
922
Lines Added
1543
Lines Deleted
FileLangChangesContributorsChurn 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.

3
Bus Factor
7
Knowledge Silos
46
Total Files
15.2%
Files at Risk

Top Contributors

Score Breakdown

How the health score is calculated from individual components

Complexity25%
95
Duplication20%
92
Cohesion15%
67
Debt Gradient15%
94
Known Debt10%
86
Coupling10%
88
Code Smells5%
100

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.