DEV Community

Cover image for Sorbetting a gem, or the story of the first adoption

Sorbetting a gem, or the story of the first adoption

Vladimir Dementyev on June 24, 2019

NOTE: this post was written a few days after the first public Sorbet release (on 2019-06-20). If you're reading this months after that happened, mo...
Collapse
 
rhymes profile image
rhymes • Edited

Hi Vladimir, thanks for your article. In the current state I don't think I'd adopt it either. To be honest I'm not sure it's going to scale easily considering how most Rails apps load tons of gems full of metaprogramming and DSLs.

Stripe AFAIK does not use Rails, Sorbet should be easier in a Ruby-only project.

A few comments:

I hope this will change in the future. The raw size (w/o .git/) of rubanok increased by 2.1MB, from 124KB to ~2.2MB.

2 Megabytes for a small library... It's probably a fixed increase of size though, I don't think it's directly proportional to how many signatures or definition files.

Problem #1. Unsupported features.

I think this is the same problem the Javascript community had when trying TypeScript. The type checker needs to evolve with the language and AFAIK there's no formal spec of Ruby so you have to track each change manually anyway.

BTW I didn't know Sorbet is written in C++

Problem #2. Limitations of flow-sensitivity

You did a lot of wrangling there. It's the thing that annoys me from these checkers, the time spent to make the tool happy

Problem #3. Signatures vs modules.

Can I say that this signature looks ridiculous? I'm thinking of the cognitive complexity that adds for devs at all levels.

sig do
  params(
    fields: Symbol,
    activate_on: T::Array[Symbol],
    block: T.proc.void
  )
  .returns(T::Array[Rubanok::Rule])
end

I agree when you say that the tool should move the signatures away from the code, or at least have a switch that can do that.

Code is read much more than it's written.

Problem #5. Runtime checks.

7% of CPU overhead is a lot for an already slow language. Stripe has a ton of VC money, they can just scale up the machines :D

When I was debugging the code with type signatures, I found it's very hard to step into the method

:((((

Collapse
 
palkan_tula profile image
Vladimir Dementyev

Thanks for the comment!

Stripe AFAIK does not use Rails, Sorbet should be easier in a Ruby-only project.

Yep. But we also have Shopify, an early adopter/beta user of Sorbet. We haven't heard a lot about this adoption yet though.

And there are already some tools to make it easier to integrate with Rails: github.com/chanzuckerberg/sorbet-r....

Collapse
 
shevegen profile image
markus heiler

So everyone is now shopify or [insert random company xyz]?

I mean there is a reason why this abomination has been created by a COMPANY. At the least they finally open sourced it - it was ludicrous that they promoted it while it was closed source.

Collapse
 
rathrio profile image
Radi • Edited

Is debugging with binding.pry still an issue if runtime checks are disabled? I don't see how sorbet could mitigate this cleanly without digging deeper into one specific Ruby implementation.

BTW: Do the runtime checks work with Rubies other than MRI? Looks like a pure Ruby gem but that's not a guarantee for compatibility.

Collapse
 
joshcheek profile image
Josh Cheek

I haven't tried it, but I'd expect not. Presumably they implement it with TracePoint, which can register and unregister for events (ie I would expect it is implemented without changing the actual structure of the code)

Collapse
 
palkan_tula profile image
Vladimir Dementyev

As far as I can see, there is no TracePoint usage. They use method_added instead to redefine the method and wrap it with the signature validation.

So, it doesn't seem to have anything MRI specific.

Collapse
 
ptolemybarnes profile image
Ptolemy

Nice article. It seems like a really ambitious project. If I were working on it I would target a strict subset of Ruby and try to creep forward from there (I wonder if this is what Stripe have been doing internally).

Collapse
 
katafrakt profile image
Paweł Świątkowski

After adding all the signatures to the codebase (full adoption), I want to be able to dump them into a .rbi file and clean up the codebase. Thus, my Ruby code stays the same: less verbose and more readable.

Nice idea, although I think it kind of beats the purpose of types annotations, because every time you need to consult or adapt two files, instead of one.

I've been thinking too whether libraries should adopt Sorbet or not. As library maintainer, I don't want to force users to install Sorbet. I wish there would be a ways to enable those typechecks only if Sorbet is already present (because parent project decided to use that, or because I'm just developing my own library).

Probably it's a matter of time before some sorbet-dummy lib comes out, which would defined methods used by Sorbet (such as sig), but does nothing and steps off when it detects that "real" Sorbet is available.

Collapse
 
joshcheek profile image
Josh Cheek

The examples make it look like you can noop it with a simple def sig(*) end. Is that not why you defined it in the block form?

Collapse
 
bodacious profile image
Gavin Morrice

Honestly, I still can't see why type checking is ever necessary in Ruby?

What does it add to your codebase or workflow?

Collapse
 
palkan_tula profile image
Vladimir Dementyev

I think, type checking could bring value at a large scale: huge codebases, bigger teams (this post shares some thoughts on this topic).

For example, when you work on a large application you might not know all the internal APIs; type checking could help you to avoid Undefined method foo for NilClass kind of errors when writing code using these unknown APIs. Documentation could also help but it could easily get out of sync.

Refactoring with type system is also much easier and faster even if you have a decent test coverage: when test fails you have to spend time to figure out why; when type checking fails you know the exact place in code. That becomes useful when you have hundreds of such places to change.

Collapse
 
mereghost profile image
Marcello Rocha

type checking could help you to avoid Undefined method foo for NilClass kind of errors

If type checking fixed this we would never have seen NullPointerException in Java/.NET static typed worlds.

Collapse
 
jaydanielian profile image
jaydanielian

Totally disagree that type checking helps with refactoring. My experience with Java and C# is exactly the opposite. It becomes a large pain to either a) need to change the param of a method to a different type or b) add a parameter to an existing method. Yes tools like resharper help, but most of the time you play compiler jujutsu trying to find just the right generic or whatever type necessary to satisfy all your existing code.

These changes become more cumbersome to implement. Duck typing allows these kind of changes with ease, and ruby method signatures are usually pretty flexible with keyword args, option splats etc being common.

I think we’ll tested systems mean type checking is totally superfluous. I moved to ruby from a C# /Java background and I have not missed types/generics one iota in Ruby. Keep ruby ducky :)

Collapse
 
vinistock profile image
Vinicius Stock

Thanks for the great post! Your findings are extremely valueable for anyone trying to adopt the Sorbet.

Collapse
 
shevegen profile image
markus heiler

Why exactly?

It does not really address the type annotation situation from an objective point of view. To be fair it is also not that subjectively biased either; but even then he makes huge claims such as:

"this is a huge leap forward for Ruby itself (especially, compared to other evolutionary features like pipeline operator)."

He does not explain why it would be a "huge" leap forward. I don't see what is leaping here.

And he contrasts it to the pipeline syntax sugar. Well, I don't like the pipeline syntax either; don't hate it but just don't see a point. But this is still so totally different to the SCOPE of sprinkling type annotations all over a code base with the deliberate attempt to make it uglier and less readable than it was before, for the promised gain in return that certain errors will be caught (which real ruby hackers don't run into - you only have this problem IN BIG COMPANIES where the worker drones have to churn out code for a living).

Keep ruby clean - that should be the main motto.