Great point
Great point
Part of this depends on one's bar for quality. In Node.js I could write `JSON.parse(input).foo.bar` really quickly. In Rust, I'd probably write two struct definitions with `#[serde(Deserialize)]` first, then a similar line. It'd take 45 seconds longer, but when I'm done, the Rust program validates the input and prints decent error messages.
I like that this is enforced in Rust (and that many other things are well beyond what tsc guarantees), and I like it in Node too and I’m glad that a certain corner of the ecosystem takes it seriously.
In my experience writing a Zod schema to parse something into a typesafe form is quick and easy and you end up with full maintainability
- you actually care about type safety so you’re not going to just `as any`
- you have a linter that will block unsafe commits or merges
- you have some type you want to use after it’s established safe, and an idea you want to fill out without breaking flow for even those 45 seconds or whatever to define your schema
- despite your predilection for type safety, you iterate on ideas like that the way people who prefer dynamic languages talk about: damn the torpedoes, I’m in the REPL
Given those assumptions (which fit a lot of TS devs well), the really good thing about @ts-expect-error in particular is it allows you to save an intermediate step for later, catch it in CI or whatevz, and it'll yell at you when, and only when, you leave it unsafe unnecessarily. It’s a good way to have your safety cake and eat your dynamic prototyping cake too, especially if breaking flow for a yak you’d shave anyway is going to sink your flow.
If you were in typescript, zod would also automatically generate your interfaces too.
Does it match the experience of a team and its need to collaboratively deliver software quality across varying levels of engineer experience? If not, then I'm not sure your individual experience is pertinent to the subject matter at hand (using Rust at a startup).
If you don't need performance, you might be better off using any number of safe, GC languages. (That said, I actually find the ergonomics of Rust nicer than most other languages. A rust-like language with a GC would be very nice for web backends imo.)
As a consumer, I tend to prefer the thing that is available a bit later (or more expensive) and works well, versus the one that is available sooner (or cheaper) and is less reliable. Maybe that preference is a result of years spent fixing things that could've been designed better?
Many people would argue you with you that the "easier" programming languages are no less reliable, context depending. Framing this in the context of the article a CRUD app, there are plenty of reliable frameworks in many languages.
that seems a little alarmist, don't you think?
This is a problem in general, but it's not a good reason to use Rust in a startup, it's a good reason to use Java or C# or TypeScript or another well-worn high-level language with static typing.
Rust's niche is solving a set of security problems that most high-level languages don't have. Choosing it for a startup only really improves your security if the other language you're considering is C or C++.
It's a good reason to not collect/store information in the first place. If your startup depends on the collection of your user's PII, then I'd seriously question if there's a real purpose to your startup. IF your startup 100% does require the use of PII, then yes, slow the fuck down, and do it right.
If you're some other startup that doesn't actually need PII, but realize it is a fast way to make money, then you should really soul search to ask if you're as amoral as you look to others and if you're okay with that. If not, make a better product that doesn't mean selling your soul to make a buck.
> And this goes well beyond Rust and "mere" memory safety: this extends to every kind of taking things slow and being careful in your coding rather than just throwing something together and later finding out you've made a serious error.
That matters because that's the kind of "safety" we're talking about when we discuss externalities for end-users --- not servers that need extra monitoring because they might crash.
Java is more left-pad resistant than Typescript, granted.
Idiomatic Rust code is just as cavalier regarding NPEs as Java or Python code. Because the language starts to look really gross when you make your code panic-safe. So you have an `expect` here, an `unwrap` there, and now you're going to have the exact same runtime issues as the more expressive managed languages. No Rust programmer thinks their `expect` will panic, just like no Java programmer expects something will be null. Rust does not force you to "exhaustively match".
Most bugs are from the programmer having a misunderstanding about how the system works as they change it. The stronger the type system, the more potential misunderstandings can be verified statically by the compiler before they reach the user.
Other, dynamically typed languages, will require either having unit tests, or running it in prod to find that user who has a null email or whatever, and results in things going awry. Since we're talking about startups, there are of course no unit tests or users, so this is all largely academic. Like, if there are 0 users to trigger bugs, and a bug that could be statically detected by a good type system exists, does it make a sound?
I'm bringing this up because in discussions like this, people tend to play fast and loose with the definitions of "safety". The kind of safety we're talking about in a decision between Rust and Java is mostly an externality to the SRE team, not to customers --- in other words, not an externality at all. An internality, if you will.
I've decided to pick an arbitrary list of security issues where the fixes will be visible to gain some small anecdotal evidence. The top result for "gitlab CVEs" is this august release announcement, let's look at the first three vulns on it: https://about.gitlab.com/releases/2022/08/30/critical-securi...
1. Remote Command Execution via GitHub import
This one was a typeing issue where an object with special keys resulted in dynamic code execution. That could not happen in rust. See the hn discussion here - https://news.ycombinator.com/item?id=33155527
2. Stored XSS via labels color
This can be made into a type-system issue with a good enough type system, text and html should be different types. Arguably this could happen with rust, but honestly, probably would not.
3. Content injection via Incidents Timeline description
This one is also arguably a typing issue for the same reason as above.
----
Hey, look, 3 errors that type systems would help with and which had security implications.
> it's much less clear that stronger type systems reduce vulnerabilities; in fact, the evidence mostly cuts the other way.
What do you mean by that? In what case is a worse type-system (like java or go) going to make it harder to write vulnerabilities than a stronger type system (like haskell or rust)
There are exactly two types of vulnerabilities Rust (and some other modern memory-safe languages) mitigate beyond memory corruption:
1. Java, Python, and Ruby have deserialization libraries that can easily be misused to create RCE vulnerabilities.
2. Python, Ruby, and Javascript have eval and eval-equivalents (which is essentially what your first example is).
You can pretend anything is a type system issue, but neither of these two vulnerabilities are properly understood as type safety errors. Java has a sharply more prescriptive and policed type system than Ruby does, but both have deserialization issues; it's just a generational thing.
Deserialization in new Java code is unlikely; deserialization is much less common than SSRF, which plagues Rust code just like everything else. In 2022, there is no meaningful security benefit to Rust over Java.
There are other reasons to use Rust! People should just stop making up fake security reasons to do it.
However, type correctness won’t necessarily prevent a DDoS, stolen password, specter, timing attack, phishing attack, etc. Type safety is a nice-to-have but not sufficient for security in SAAS products.
Then you talk about why "startups need regulation" and the lack thereof is why "the world remains an insecure mess" which is another non sequitur drawn from statements which, while individually correct, would never combine to imply that conclusion.
The world is an insecure mess because some societies have not collectively agreed that it is more expensive to be insecure than secure.
Startups don't "need" regulation anymore than regulation is a cure for any problem. In practice, poorly drafted regulation exacerbates rather than solves the problem it is scoped to solve because it opens the door for regulatory capture based monopolies.
That said, I do agree with parts of many of your individual statements: more effective regulations would be good not just for citizens but for startups and society in general; memory safety oriented code tends to be higher ROI for everyone; the world remaining an insecure mess is a pox on global societies whereupon well deployed investment into secure infrastructure would produced significant ROI compared to the present state of the world.
This could be true for C++, Java or maybe C#. Against them, python/ruby run circles.
But Rust change the equation.
Is super-productive... BUT what is important to note is that you need developers that have done the initial climb and go fully rust with it.
After this, things start to click neatly:
- Modeling code is easy & fast
- Composing code, flow!
- Testing (all of it, including benchmarking and integration) flow.
- Refactoring (big!) is super-easy. This one is where the "velocity" is actually need.
But you CAN'T "code fast". You CAN'T skip in good practiques "let's ignore the use of clippy, Result, proper use of traits (like Into/From), etc". (and even write documentation with including doc-test!)
This is where the "I write code FAASSST bro!, not time for pesky good practiques!" get hurt with Rust.
You need to buy the whole package, because Rust is MADE to be used fully in harmony.
And when you finally do it. IS FAST TO CODE.
And even FASTER to refactor.
And probably so minimal post-fixes.
---
P.D: Major blow against Rust? Slow compiling. This is the one factor that blow the dream and where you truly need a decent machine and flow to make it nice. However, if you take the above advice, you are already with Test/DocTest/Clippy/Check flow and that part can be done to be efficient.
I really dislike python in production and have done it a lot more than I've ever wanted.
Are you actually a C++ programmer? I work at a firm that uses C++ and Rust and this isn't the case at all. Setting up dependencies, tests and benchmarks is a one-off cost, most C++ compiler errors are quite understandable to experienced devs unless they're doing something really hairy, and C++ has algebraic datatypes, as a library (std::variant) via variadic templates, something Rust doesn't support. For C++ devs comfortable with template metaprogramming Rust is missing a bunch of features, many things that are relatively simple in C++ are literally impossible in Rust (at least without writing macros). Rust also doesn't save much on debug time if writing single-threaded code, because memory errors are a very small proportion of the bugs one encounters in modern C++ written by experienced devs, rather most bugs are logic errors, which Rust doesn't prevent.
Well, Rust uses macros for that which are equivalent to C++ templates. Ever used println!() ?
If you're willing to blindly follow the suggestions given you by the compiler about references, and clone() things when it doesn't seem to work, you'll be just as productive as a Python/Java ... programmer. You'll just not be as memory efficient as a Rust programmer who actually groks that stuff. That's the tradeoff: coder productivity versus memory efficiency.