I also think Stripe’s API (external) should not be moving ids and objects. Given some payload in which ‘account_id’ is always present and ‘account’ may be the object (using ‘expand’ IIRC?) or not makes a lot more sense to me.
I also think Stripe’s API (external) should not be moving ids and objects. Given some payload in which ‘account_id’ is always present and ‘account’ may be the object (using ‘expand’ IIRC?) or not makes a lot more sense to me.
1. Ambitious team who wants types does work to get the initial version passing in CI. Importantly, it's only checking at `# typed: false`, which basically only checks for missing constants and syntax errors.
2. That initial version sits silently in the codebase over a period of days or weeks. If new errors are introduced, it pings the enthusiastic Sorbet adoption team; they figure out whether it caught a real bug or whether the tooling could be improved. It does not ping the unsuspecting user yet.
3. Repeat until the pings are only high-signal pings
4. Turn Sorbet on in enforcing mode in CI. It's still only checking at `# typed: false` everywhere, but now individual teams can start to put `# typed: true` or higher in the files they care about.
5. Double check that at this point it's easy to configure whatever editor(s) your team uses to have Sorbet in the editor. Sorbet exposes an LSP server behind the `--lsp` flag, and publishes a VS Code extension for people who want a one-click solution.
6. Now the important part: show them how good Sorbet is, don't tell them. Fire up Sorbet on your codebase, delete something, and watch as the error list populates instantly. Jump to definition on a constant. Try autocompleting something.
In my experience trying to bring static types to Ruby users, seeing is really believing, and I've seen the same story play out in just about every case.
One final note: be supportive. Advertise one place for people to ask questions and get quick responses. Admit that you will likely be overworked for a bit until it takes off. But in the long run as it spreads, other teammates will start to help out with the evangelism as the benefits spread outward.
Luckily I think Stripe is a well-functioning org with smart and reasonable leaders—much easier to present that case than e.g., trying to win debates on static vs dynamic typing on the internet!
Nowadays, its extremely rare for me to come across projects that don't use Typescript already!
I understand that types are helpful, especially within the context of learning new code (both as a new dev, and more easily understanding new code written by your team), but my hesitation to "jump ship" to them comes from weighing all the "boilerplate" code necessary to get them working versus the tangible benefits I feel they'd give.
To that end, maybe enumerating over the benefits for your friends (ideally more specifically than just "fewer bugs" or "easier to read/write") might tilt the scale more towards using them.
The article shows some good examples of problems that types solve, but many of them could also be "fixed" with better naming schemes, standardizations, and/or refactors -- and then wouldn't also be littered with type annotations (which take up 50% of the code in their example, [1], where I've also added a couple other easy-to-understand "solutions" with less cruft.
IMO, most of the typing syntax in their examples is distracting from the actual code -- especially when just writing better or more standardized code (which you still need to do with types) would make the typing syntax, again IMO, less necessary.
[1] https://sorbet.run/#%23%20typed%3A%20true%0Aextend%20T%3A%3A...
In Haskell for example, writing signatures is actually both unnecessary (they will be inferrable, most of the time so it's basically only for humans to read) and a net positive (they fit well prose wise and you can use them as documentation) but that's a harder hill to climb.
I do want to note that one big advantage of specifying signatures is that now your IDE can help you when using the functions and detect when something is going wrong.
Maybe "no more trying to figure out what the input/output of a function that you didn't write is supposed to be" would be a draw?
I've moved on from Ruby to Elixir and we use typespecs at my current job, but I still never do in any of my own code. Elixir does have a way to do type hinting which I do use and appreciate when appropriate. So I'm not "against" types and like the idea in theory, but I've never really felt the pain.
The most tangible argument I've bought is that types help out in a huge codebase. I can definitely see this. This is really solving a social problem, of course, because on smaller teams, it's much easier to enforce coding standards, especially if you follow XP and pair all the time. For example, in large dynamic codebases I've worked on, calling something `object` when it really means `object_id` would never fly. Variables and functions always have to be full words describing exactly what they do. Functions are only ever allowed to return one type. And of course everything is thoroughly tested. Of course I understand that not all these things are practiced everywhere and nor do they need to be. But for me that has always made for incredibly readable code.
I'm writing this just to give the perspective of a dynamic weenie and maybe looking to be convinced a little more? I also know three incredibly good programmers with 20-50 years experience each who all wrote in typed languages for years and then couldn't be happier not to have types when they moved to Ruby. So ya, I dunno what I'm expecting here, but mostly offering my perspective.
But the TL;DR: if I join your team and you want me to use types, I'll do it! But I really feel there are ways around it but those ways may not be for everyone (and sometimes perhaps impractical).
hmmm, I dunno, this was longer than I meant it to be, haha.
The thing I left out was that it's only been four month of being forced to write typespecs (I actually secretly resent it when I am forced to add them to private functions) so maybe I'll come around?
> I also know three incredibly good programmers with 20-50 years experience each who all wrote in typed languages for years and then couldn't be happier not to have types when they moved to Ruby.
Typed languages from 20 to 50 years ago were very inconvenient to use and had poor compiler diagnostics that made type checking a lot less intuitive, so this isn't surprising. Modern typed languages though are very different and should not be conflated with legacy or historical ones.
I use types at work in Typescript and Swift but honestly I'm not very convinced it's worth the hassle in most projects. I feel that if the build tool already can tell me what type I should be using, why do I need to pedantically add them everywhere?
@spec full_name(String.t, String.t) :: String.t
def full_name(first_name, last_name) do
"#{first_name} #{last_name}"
end
As someone who historically hasn't done any typing, I still find this a little noisy, but it lets me read the function as normal and can look at the spec only if there is any confusion.- type signatures offer succinct descriptions of what a function does - I write less unit tests with proper usage of types (if you need a natural number, take a natural number type, not an integer) - building your domain and giving names to structures that are in the application (passing around MailerOptions as opposed to an 'object that contains ...') - concrete interfaces for components/services/modules in my codebase
> it's much easier to enforce coding standards, especially if you follow XP and pair all the time.
IMO where it's possible to add process that makes the bad thing an error, I do that instead of requiring/trying to enforce discipline.
> or example, in large dynamic codebases I've worked on, calling something `object` when it really means `object_id` would never fly. Variables and functions always have to be full words describing exactly what they do. Functions are only ever allowed to return one type. And of course everything is thoroughly tested.
Agree on all fo this, but regarding tests, using types does prevent me from having to worry too much.
There was a point in time where writing lots of unit tests that tested non-user inputs (is the incoming thing a number? is it less than zero somehow) was seen as clever (and it still is a good idea where appropriate). These classes of bugs just don't exist in a properly typed codebase. If you need an Integer that can't be less than zero, you want a Natural (haskell parlance).
Excessive unit tests in untyped codebases are the result of not properly specified types. If you have a function that takes a certain type, there is no reason to check if you got a string (or not that type). If you pick a language with a top of the line type system, you don't even have to check for nil.
Realizing that nothing is actually safe in languages with nullable types was a big jarring point for me way back when. I think it was when Java introduced Optional<T> that I realized it should actually be everywhere, and all the Haskell I'd been doing on the side just made way more sense.
> I'm writing this just to give the perspective of a dynamic weenie and maybe looking to be convinced a little more? I also know three incredibly good programmers with 20-50 years experience each who all wrote in typed languages for years and then couldn't be happier not to have types when they moved to Ruby. So ya, I dunno what I'm expecting here, but mostly offering my perspective.
So I say the "convincing" part a bit lightly -- I know that people must come to their own conclusions, I can only suggest the idea. People in both typed and untyped languages can be incredibly productive (my friend is much like you, so productive and disciplined that types don't seem to add much), and I can admit that adding types requires more up front thought (good) and more ceremony (~bad, especially if you don't have type inference).
Maybe the big problem is that the really good implementation of types is in languages that can be hard to approach (Haskell, OCaml, other ML langauges). The second best IMO is Rust but that has the whole separate ramp of ownership/borrowing which is hard for different reasons. Go has some good concepts (protocols) but a bunch of other warts.
I find that writing software with a good type system gives me confidence that the code is correct. It's almost axiomatic because you're building the structures that are being checked/used by the code, but nevertheless I have that much more confidence in a Typescript codebase or a Haskell codebase than an equivalent untyped codebase.
Basically I think like this:
- If I want correct, I write Haskell
- If I want quick, I write Typescript
- If I want performance, correctness and efficiency, I write Rust
Also, even if you don't believe me at all, there is a trend -- python adding typing, sorbet and other systems for ruby, typescript gaining popularity, etc. The crowd sometimes has some wisdom.