Working with a large codebase with an untyped codebase is just a nightmare, even with powerful IDEs like RubyMine that are able to cover some of the paint points. I wonder how good Sorbet is these days, though, especially the RoR experience
Loco follows up the Rails formula pretty closely, and makes easier to learn Rust by taking care of a load of boilerplate code.
It is surprising to me seeing the rust web backend scene; many libraries, server frameworks, and users, but they are all Flask-analogs, without the benefit of the reasonably-robust ecosystem Flask has. My suspicion is that people are using them for micro-services and not websites/webapps, but I haven't been able to get a straight answer on this about how people are using these tools. I.e. even though rust is my favorite overall language and see no reason it couldn't be used for web work, I still use Django.
Axos, Axum, Rocket, Diesel etc, are all IMO not in the same league as Django. My understanding is that addressing this is Loco's Raison d'etre.
Another aspect of the Rust web ecosystem: It's almost fully gone Async.
I'll say this, coding agents make the lack of a "batteries included" framework like rails or Django somewhat less daunting.
But "convention over code" and having a default structure / shape for projects is extremely helpful and you feel it when it's missing.
For my last small project I looked at Loco but ended up passing on it because I felt like adoption wasn't great yet. I really hope it takes off, though.
I think rust programers are more likely to want flask/rack than Django/rails.
Also, every enterprise rails app I've seen (seven, to date) has been really poorly written/architected in a way that other backends just weren't. Even the fairly new ones felt like legacy code already.
I tried sorbet a couple of times and totally get why it’s useful but imo it’s not just lacking (e.g. compared with what can be done with TS or even a simple type system like golang) but it also removes all the fun parts of ruby / rails.
At least in Python (as a comparison example), imports are only available in the module doing the importing - it's more explicit. You'd have to really work to get the same "everything all at once" as Rails has.
But if someone is actually relying on literal true/false instead of truthiness, you now have a bug.
I say this as a Ruby evangelist and apologist, who deeply loves the language and who’s used it professionally and still uses it for virtually all of my personal projects.
I always liked Rich Hickey's point, that you should program on the inside the way you program on the outside. Over the wire you don't rely on types and make sure the entire internet is in type check harmony, it's on you to verify what you get, and that was what Alan Kay thought objects should do.
That's why I always find these complaints a bit puzzling. Yes in a dynamic language like Ruby, Python, Clojure, Smalltalk you can't impose global meaning, but you're not supposed to. If you have to edit countless of existing code just because some sender changed that's an indication you've ignored the principle of letting the recipient interpret the message. It shouldn't matter what someone else puts in a map, only what you take out of it, same way you don't care if the contents of the post truck change as long as your package is in it.
that's not a big deal, when we exchange generic information across networks we parse information all the time, in most use cases that's not an expensive operation. The gain is that this results in proper encapsulation, because the flipside of imposing meaning globally is that your entire codebase is one entangled ball, and as you scale a complex system, that tends to cost you more and more.
In the case of the OP where a program "breaks" and has to be recompiled every time some signature propagates through the entire system that is significant cost. Again if you think of a large scale computer network as an analog to a program, what costs more, parsing an input or rebooting and editing the entire system every time we add a field somewhere to a data structure, most consumers of that data don't care about?
this is how we got micro-services, which are nothing else but ways to introduce late binding and dynamism into static environments.
I never used Ruby, but Python code bases love mixing in strings that are actually enums and overloading functions that accept all kinds of types. You just had to hope that the documentation was correct to avoid a crash.
Java 1.7 to Python feels very freeing from all the boilerplate. Kotlin, or any other modern language with a well designed standard library, to Python just feels like a bunch of extra mental work and test to write to just avoid brackets.
Nowadays C# is anyways much more expressive than before. Meanwhile Ruby is still very slow.
Not to mention how poorly maintained are most Rails projects. People have been "vibe coding" forever.
A well-organized and maintained Rails app is great though. I'd definitely consider working with it again, but it really depends on what company it is.
Why would anyone ever choose ruby, python, etc when you don’t need to write it?
These languages are undoubtedly dead as of now. Python may live on in ML for a bit but probably not much longer
I certainly wouldn’t want to go back to working in dynamic languages without typing on top. That takes too much brain power, I’m too old for that now.
I would say Sorbet seems more “basic” than something like Typescript. It handles function calls matching signatures, potential nulls, making sure properties actually exist, that kind of thing. Whereas TS can get quite abstract, and at times you’re fighting to convince it that a value actually is the type you say it is.
TS is very powerful and expressive, to the point that it’s possible to do computation within type code. I’m not convinced I always need that power, or that it’s always more help than hindrance.
irb(main):005:0> Foo.new.method(:bar).source_location => ["tmp/test.rb", 5]
I vouched for your reply below, and to answer in the meantime:
Yes, it's runtime, but that only matters if your code can't be initialized without unacceptable side effects.
In which case you don't have a functioning test suite either, and have much larger problems.
Otherwise, just load the code you struggle to figure out into irb, or pry, or a simple test script, and print out source-location.
If that is impossible (aside from the fact that codebase is broken beyond all reason), the marginally harder solution is to use ruby-lsp[1] and look up the definitions.
This is only hard if you insist on refusing to use the available - and built in, in the case of source_location - tooling.
> and at times you’re fighting to convince it that a value actually is the type you say it is.
Might just be allocating that brain power to the same task but calling it a different thing.
Did you use AI? .. Nah I hand coded it.