name: ruby-gem-architecture-review description: > Perform architectural review of Ruby gems and libraries. Evaluate API design, module organization, dependency management, Rails integration safety, testability, and extension patterns. Use when reviewing an existing gem's architecture, auditing a gem before adoption, preparing a gem for open-source release, or refactoring a library's internal structure. Complements the andrew-kane-gem-writer skill which handles gem creation.
Ruby Gem Architecture Review
Perform structured architectural review of Ruby gems and libraries. Evaluate design decisions against proven patterns from high-quality gems (Searchkick, Strong Migrations, Devise, Dry-rb, Sorbet, ActiveSupport).
When to Use
- Reviewing an existing gem's codebase before contributing or adopting
- Auditing internal libraries before extracting to standalone gems
- Preparing a gem for open-source release
- Refactoring a gem that has grown beyond its original design
- Evaluating whether a gem's architecture will scale
Review Process
Execute each phase in order. Skip phases only when explicitly irrelevant.
Phase 1: Structural Inventory
Map the gem's physical structure before evaluating quality.
Actions:
- Read the gemspec for metadata, dependencies, and file patterns
- Map the directory layout under
lib/ - Identify the entry point (
lib/gemname.rb) and trace the require graph - Count public classes/modules vs internal implementation
- Note any non-Ruby files (C extensions, FFI bindings, YAML configs, templates)
Produce a structural summary:
| Metric | Value | Notes |
|---|---|---|
| Entry point | lib/gemname.rb |
|
| Total Ruby files | N | |
| Public modules/classes | N | Exposed to consumers |
| Internal modules/classes | N | Implementation detail |
| Runtime dependencies | N | List them |
| Lines of code (approx) | N | Excluding tests |
| Test files | N | Framework used |
| C extensions / FFI | Yes/No |
Phase 2: API Surface Review
Evaluate what consumers interact with.
Check each item:
Entry point clarity — Does
require "gemname"make the full public API available? Does it load too much (eager) or too little (missing requires)?Public API size — How many public methods/classes must a consumer understand to use the gem? Fewer is better. Flag gems where the consumer must know >10 classes.
Configuration pattern — How is the gem configured?
- Preferred: Module-level
attr_accessorwith sensible defaults (Kane pattern) - Acceptable:
configureblock yielding a config object - Problematic: Global variables, environment-only config, scattered constants
- Preferred: Module-level
Error hierarchy — Does the gem define a base
Error < StandardError? Are errors specific enough to rescue selectively?Method signatures — Do public methods use keyword arguments for clarity? Are required vs optional parameters obvious? Flag positional args beyond 2.
Return types — Are return types consistent and predictable? Flag methods that return mixed types (sometimes Array, sometimes nil).
Thread safety — Does the gem use mutable class-level state? Flag
@@class_variables, unprotected@instance_variableson modules, and shared mutable hashes/arrays without synchronization.Namespace pollution — Does the gem monkey-patch core classes (String, Array, Object)? Does it add methods to classes it doesn't own without using
prependorrefinements?
Severity guide:
| Issue | Severity |
|---|---|
| Monkey-patching core classes | Critical |
| Thread-unsafe shared mutable state | Critical |
| No base error class | Important |
| Positional args > 2 | Suggestion |
| Mixed return types | Important |
| Configuration via globals/ENV only | Important |
Phase 3: Module Organization Review
Evaluate internal structure and separation of concerns.
Check each item:
Single Responsibility — Does each file/class have one clear purpose? Flag files >300 lines or classes with >15 public methods.
Layering — Is there separation between:
- Public API (what consumers call)
- Core logic (algorithms, transformations)
- Integration layer (Rails hooks, framework adapters)
- I/O layer (HTTP clients, database queries, file access)
Require graph — Are requires explicit (
require_relative) or implicit (autoload)? Flag circular requires. Trace the load order for correctness.Method decomposition — For large classes (>200 lines), does the gem use the include-modules-by-feature pattern? (See
references/decomposition-patterns.md)Internal vs Public distinction — Are internal modules clearly separated from the public API? Check for:
@api privateannotations or# :nodoc:comments- Private constants or nested modules consumers shouldn't touch
- Underscore-prefixed files or directories (convention)
Version file — Is
version.rbminimal (just the VERSION constant)? Flag version files that require other code.
Phase 4: Dependency Review
Evaluate runtime and development dependencies.
Check each item:
Runtime dependency count — Zero is ideal. Each dependency is a liability. Flag gems with >3 runtime dependencies. For each dependency ask: could this be replaced with stdlib?
Dependency version constraints — Are constraints appropriately loose?
~> 2.0(good for libraries — allows 2.x)>= 2.0, < 4.0(acceptable for broad compatibility)= 2.3.1(problematic — causes version conflicts)
Optional dependencies — Does the gem handle missing optional deps gracefully? Pattern:
defined?(SomeGem)checks orbegin; require "somegem"; rescue LoadError; endRails coupling — Does the gem require Rails components directly, or use
ActiveSupport.on_loadanddefined?(Rails)guards? Flag anyrequire "active_record"orrequire "action_controller"at top level.Ruby version constraint — Does the gemspec declare
required_ruby_version? Is it reasonable (supports current and previous major)?Gemfile.lock — Is it committed? (Should NOT be for gems, SHOULD be for apps.)
Phase 5: Rails Integration Review
Skip if the gem has no Rails integration. Otherwise, evaluate safety.
Check each item:
Railtie/Engine — Is Rails integration isolated in a Railtie or Engine? Is it loaded conditionally with
if defined?(Rails)?ActiveSupport.on_load — Are hooks into Rails models, controllers, jobs deferred via
on_loadrather than eagerinclude/extend?Namespace isolation — If using an Engine, is
isolate_namespaceset?Migration safety — If the gem provides migrations or generators: are they reversible? Do they use safe patterns for production (no
remove_columnwithoutsafety_assured)?Asset pipeline — If the gem provides assets, does it support both Sprockets and Propshaft? Are assets namespaced?
Configuration via Rails — Does the gem support
config_for(:gemname)for YAML-based configuration with ERB?
Phase 6: Testability & Quality Review
Evaluate test architecture and code confidence.
Check each item:
Test framework — Minitest or RSpec? Either is acceptable. Flag no tests.
Test isolation — Can tests run without a network, database, or external service? Flag tests that require running services without documenting it.
Multi-version testing — Does the gem test against multiple Ruby versions? Multiple Rails versions (if Rails-integrated)? Check for Appraisal gemfiles or matrix CI configs.
Test coverage distribution — Are edge cases tested? Are error paths tested? Flag test suites that only test the happy path.
Test helpers — Does the test suite provide helpers for consumers to use in their own tests? (e.g.,
GemName::TestHelper, factory methods, mock adapters)CI configuration — Is there a working CI setup? Does it test the matrix of supported Ruby/Rails versions?
Phase 7: Extension & Evolution Review
Evaluate how well the gem handles change.
Check each item:
Plugin/adapter pattern — Can behavior be extended without modifying the gem? Does it support registering custom adapters, strategies, or backends?
Callback/hook system — Does the gem provide hooks for consumers to tap into the lifecycle? (e.g.,
before_search,after_encrypt)Semantic versioning — Does the gem follow SemVer? Check CHANGELOG for evidence of breaking changes in minor versions.
Deprecation strategy — How does the gem handle deprecated features? Flag gems that remove features without deprecation warnings.
Upgrade path — For major version bumps, are migration guides provided?
Documentation — Is the README sufficient to use the gem without reading source? Does it cover: installation, quick start, configuration, common patterns, and errors?
Producing the Review Report
After completing all phases, produce a structured report:
# Gem Architecture Review: {gem_name} v{version}
## Summary Verdict
**Overall**: Ready for production / Needs work / Significant concerns
[2-3 sentence summary of the gem's architectural quality]
## Scores
| Area | Score | Notes |
|------|-------|-------|
| API Design | A-F | |
| Module Organization | A-F | |
| Dependencies | A-F | |
| Rails Integration | A-F or N/A | |
| Testability | A-F | |
| Extensibility | A-F | |
## Critical Issues
[Issues that must be fixed — security, thread safety, data loss]
## Important Issues
[Issues that should be fixed — API design, coupling, missing tests]
## Suggestions
[Improvements that would elevate the gem's quality]
## What's Done Well
[Positive patterns worth preserving or learning from]
## Recommended Actions
[Prioritized list of changes, ordered by impact]
Anti-Patterns Checklist
Quick-reference for common gem architecture problems:
| Anti-Pattern | What to look for | Why it matters |
|---|---|---|
| God module | Entry point >200 lines, does config + logic + integration | Hard to test, hard to extend |
| Require spaghetti | Circular requires, load-order-dependent behavior | Breaks in unpredictable ways |
| Rails assumption | require "active_record" at top level |
Gem unusable outside Rails |
| Global state | @@class_variables, mutable constants |
Thread-unsafe, test pollution |
| Leaky abstraction | Internal classes in public API, undocumented send calls |
Breaking changes on refactor |
| Dependency bloat | >3 runtime deps, or deps for things stdlib handles | Version conflicts, attack surface |
| Missing errors | raise "something went wrong" without custom class |
Consumers can't rescue selectively |
| Positional soup | def call(name, type, options, flag, format) |
Unreadable call sites |
| Test coupling | Tests require specific database, network, or file state | Can't run in CI or isolation |
| Monkey-patching | class String; def fancy_method; end; end |
Conflicts with other gems |
| Version lock | spec.add_dependency "foo", "= 1.2.3" |
Forces consumers into exact version |
| Silent failure | Swallowing exceptions, returning nil on error | Bugs hide until production |
Reference Files
For detailed patterns and examples:
- references/decomposition-patterns.md — Module decomposition strategies for large gems
- references/api-design-patterns.md — Public API design patterns from well-known gems
- references/dependency-evaluation.md — Framework for evaluating gem dependencies