| name | write-rbs |
| description | Use when writing, reviewing, or modifying RBS type signatures (sig/**/*.rbs, vendor/rbs/**/*.rbs, or inline #: annotations) or running Steep – e.g. "write rbs", "add type signatures", "type this class", "run steep", "check types". Enforces dd-trace-rb RBS conventions. |
Writing RBS
Signatures live in sig/, mirroring lib/ one .rbs per .rb. Vendored gem
stubs live in vendor/rbs/. ALWAYS check for an existing .rbs before writing a
new one
Core rules
- MUST leave a blank line between every signature
- NEVER add a leading
:: to a Ruby core class – write Hash, not ::Hash
- MUST keep the
:: when the enclosing namespace shadows a core or
standard-library name – class Datadog::Core::Logger < ::Logger, else it
inherits from itself
- NEVER add a leading
:: to the library's own modules; omit the namespace
instead – write Internal::ClassName, not ::Datadog::Internal::ClassName
- ALWAYS write a nilable as
Type?, never Type | nil
- NEVER copy Ruby comments into the
.rbs
- NEVER use
untyped when the type is inferable from the code
- NEVER loosen a signature just to satisfy Steep – a signature MUST harden the
code's real contract, not paper over a checker error
- Use
any only when every possible type is intentionally valid and the code
does not depend on the concrete type; use untyped when the type is merely
not-yet-modeled, and be sparing with any
- SHOULD reuse the signatures already present, and upgrade any
untyped you can
infer
Vendored signatures
- NEVER write a
vendor/rbs signature from how the calling code uses the gem –
inference from usage can be wrong, and the caller may misuse the gem
- ALWAYS read the gem's real source first; ask for its location if you lack it
- SHOULD update stale or missing stubs, but only after seeing that source
- NEVER use
any in a vendored stub – any is a dd-trace-rb type alias, not a
vendored concept; use untyped for genuinely open values there
Advanced techniques
Open the matching reference when a value calls for more than a plain type:
- duck-typed – you call methods, not a concrete class →
references/interfaces.md
- a shape that repeats or carries a domain meaning →
references/type-aliases.md
- an output type that depends on the input type →
references/generics.md
- a callable →
references/procs.md
- a
Class.new/Struct.new constant → references/class-definitions.md
- correct Ruby the checker can't infer, or a one-off cast →
references/steep-annotations.md
- a file whose whole signature lives in the
.rb (rbs-inline) → references/inline-rbs.md
Signature generation
bundle exec rake "rbs:prototype[lib/<path>.rb]"
Prototypes over-emit untyped and leading ::; clean the result against the
rules above
Steep checks
While iterating, scope the check to the edited file's Ruby source – a sig/
path silently passes even on a broken signature:
bundle exec steep check lib/datadog/<path>.rb
This checks only that file; it will NOT catch breaks in signatures that
reference it. ALWAYS run the full check before treating types as done:
bundle exec steep check