mcpField
S
simplifying-ruby-code

simplifying-ruby-code

github.com/bostonaholicDevelopment264 installsUpdated 1d ago
Repository

About simplifying-ruby-code

A Ruby code review and refactoring skill focused on simplifying structure while preserving meaningful behavior. It offers patterns for replacing unnecessary classes, separating decisions from I/O, and checking callers before removing code.

Key features

  1. 01Replace stateless command objects with functions
  2. 02Choose Hash, Struct, or Data for values
  3. 03Prefer composition over deep inheritance
  4. 04Separate pure decisions from I/O
  5. 05Check callers before removing classes
  6. 06Guide review and stepwise refactoring

Use cases

  1. 01Reviewing Ruby code for simplification opportunities
  2. 02Refactoring Rails service objects and concerns
  3. 03Planning safe removal of classes
  4. 04Separating computation from database or file effects

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/bostonaholic/skills --skill simplifying-ruby-code

After installing, type/simplifying-ruby-codein Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

agents/
openai.yaml
references/
review-findings.md
scripts/
find-callers.sh

Details

From the project README

Simplifying Ruby Code

Prefer plain data (Hash, Array, Struct, Data) and pure functions over classes that add no state or behavior. Separate decisions from effects: a decision takes data and returns data; an effect does I/O (database, network, files, time). Keep them in separate methods or modules so decisions test without mocks.

When the user asks for a review, report findings and edit nothing unless they ask. Cite patterns by number ("Pattern 1") or by anti-pattern name.

Patterns

  1. Command objects to module functions. A class whose only public method is call, perform, run, or execute, holding nothing beyond its constructor arguments, becomes a direct call or a module function (UserCreator.new(params).call becomes User.create(params)). Keep the object when it holds state across calls, runs a multi-step algorithm worth naming, or must be serialized, as a background job is.

  2. Value objects to Data, Struct, or Hash. A class that only stores attributes, with hand-written initialize, readers, or == and no behavior or validation:

    UseWhen
    HashTransient data, varying keys, JSON in or out
    StructFixed attributes, mutation acceptable
    DataFixed attributes, immutable (check ruby -v >= 3.2)
    Custom classValidation, invariants, or real domain behavior
  3. Class-method-only classes to modules. A class never instantiated, defining only self. methods, becomes a module with module_function.

  4. Deep inheritance to composition. More than 2 levels counting only the project's own classes (framework and stdlib bases such as ApplicationRecord or StandardError do not count), or an abstract base with a single subclass.

  5. Missing Ruby protocols. Callers unpack a collection-like or value-like class by hand instead of it implementing each with Enumerable, to_h, <=> with Comparable, or hash and eql?.

  6. Mixed decisions and effects. Tests need heavy mocks, or a method computes and writes in one body. Extract the computation into a pure function and leave a thin method for the I/O.

Rails anti-patterns

  • Service objects: a single-method, stateless service belongs in a model method or module function (Pattern 1). Keep complex orchestration as a service but separate decisions from effects (Pattern 6). Jobs are legitimate objects because they serialize.
  • Models: keep ActiveRecord models as classes; move business calculations into pure functions.
  • Concerns: a concern shares behavior across models; flag one used as a dumping ground for a single model.
  • Helpers: view formatting only; business rules go in a module.
  • Reinvented Rails: custom DSLs or base classes that duplicate scopes, validations, callbacks, enums, or ActiveModel.

Plain Ruby anti-patterns

  • Template methods: a base class with hook methods where a block would do.
  • Builders: for objects that keyword arguments or Data.define build directly.
  • Wrappers: classes that wrap Array, Hash, Set, or another stdlib type and forward most calls.
  • Metaprogramming: method_missing, define_method, or instance_eval DSLs where plain methods work.
  • Dependencies: a gem pulled in for a few lines of stdlib code.

In a gem, every public constant is API that other projects may call.

Before removing or inlining a class

  • Run bash <skill-dir>/scripts/find-callers.sh <Constant> <project> for each class.
  • Exit 0: account for every reference and require hit, and decide for each dynamic hit whether the name it builds can be the class; that takes reading the code, so it stays with you.
  • Exit 1: no caller found. Exit 2: fix the argument. Exit 3: install the named tool. Exit 4: report the grep error.
  • A controller is also reached from config/routes.rb by its snake_case resource name, which the script does not search; read that file.
  • In a review, drop or downgrade a finding whose callers you cannot account for.

Review findings

Read review findings when reporting a review: the grouping, the fields of each finding, and the closing order.

Refactoring

Get a green test baseline first. Apply one pattern at a time and run the suite after each; on failure, fix or revert that step before continuing. Update tests that mocked the removed layer to call the pure function or the direct API instead.

Comments

Markdown supported