I run docscribe daily on client Rails apps. I cannot share that code, so every example below is synthetic and verified against docscribe 1.6.2 on Ruby 3.4.5.
Comments lie
This is the shape I keep finding:
# @param [String] count
# @return [String]
def notify(verbose:, count:)
puts "verbose!" if verbose # Boolean
count.times { send_ping } # Integer
count # Integer
end
The comment says two Strings in, one String out. The code takes a flag and a count and returns the count. Anyone who trusts the comment calls it wrong: "false" is truthy, and "3" has no times method.
What docscribe -a actually does
Safe mode adds what is missing and leaves the rest alone. On the file above:
$ docscribe -a notify_demo.rb
# @param [String] count
# @param [Object] verbose Param documentation.
# @return [String]
def notify(verbose:, count:)
puts "verbose!" if verbose
count.times { send_ping }
count
end
It added the missing @param verbose. It did not touch the lying String tags. That is the contract: safe mode never rewrites a type you wrote. -A rebuilds the block from scratch; I always git diff after it.
Where types really get fixed
Type correction lives in update_types, fed by RBS. Given this signature:
class Object
def notify: (bool verbose, Integer count) -> Integer
end
this command:
$ docscribe update_types --rbs --sig-dir sig notify.rb
produces:
# @param [Boolean] verbose
# @param [Integer] count
# @return [Integer]
def notify(verbose:, count:)
puts "verbose!" if verbose
count.times { send_ping }
count
end
No prose placeholders here because the original had no descriptions to keep. When descriptions exist, update_types keeps them.
The reverse direction exists too: docscribe rbs generates .rbs from YARD. Types flow both ways, YARD stays the human-readable cache in the middle.
source: tells the editor which fix to offer
Every change in 1.6.2 carries source: rbs | infer | syntax through JSON, SARIF, and daemon responses. The IDE uses it to pick the right lightbulb: rbs offers "Update types", anything else offers "Fix YARD". That one field is what makes the VS Code and RubyMine plugins feel fast instead of noisy.
Rails specifics I use daily
Types from schema.rb, not from code. t.boolean :is_admin becomes @!attribute [r] is_admin returning Boolean in the model, via the collector plugin in examples/plugins. No hand-written docs for columns.
One daemon per project serves both editor plugins over a Unix socket JSON-RPC (check, fix, check_batch, update_types, ping). When the daemon is off, the plugins fall back to CLI. CI mode runs on exit codes (0 clean, 1 missing docs, 2 errors) plus check_for_comments, with json/sarif output.
- Live demo and install: https://unurgunite.github.io/docscribe
- Source: https://github.com/unurgunite/docscribe
- Gem: https://rubygems.org/gems/docscribe
Next in 1.7.0
docscribe sorbet - same YARD to signature flow as docscribe rbs, but generating .rbi/sig blocks for srb tc. Plus validate_types on by default (opt-out with --no-validate-types). Plus tighter metaprogramming support: better types for define_method and DSL-generated methods, less Object fallback.
Limits
Heuristics, not a prover, so metaprogramming lands in Object today. Each release narrows that gap.
Question I keep asking: do you keep YARD and RBS in sync by hand, or did you give up on one of them?


Top comments (3)
Safe mode being the default is the feature that would make me actually install this. A tool that rewrites my hand-written types on its own is one I would stop trusting after the first bad merge. The source field is a smart touch too: suggesting the right fix per case is what turns this from another report nobody reads into something that actually gets used.
Thanks, Pawel. Safe by default is a deliberate choice. A tool that rewrites a hand-written type once loses trust for good.
The
source:field came from the editor plugins. I needed a way to offer "Update types" for RBS-backed fixes and plain "Fix YARD" for the rest.Would be glad to see you as a user. Start small and tell me where it gets types wrong. I am writing a series on working with the gem and real type data (schema columns, returns, associations, two-way RBS sync). Next part lands in a few days.
I will give it a run on a small project and report back. The metaprogramming gap is exactly where I expect my codebase to stress it, so that will be the real test. Appreciate you taking feedback in the thread.