You don't need a strong type system or even really ANY compile-time type system for this strategy to work! I use all these techniques in plain JS and I can still get the benefits of correct-by-construction code style just by freezing objects and failing fast.
You're about a decade too late with that argument. The best argument for dynamic types systems, is enjoying debugging in production.
In dynamic languages, you are the type system.
> The best argument for dynamic types systems, is enjoying debugging in production.
This comment is either severe snark or severe ignorance.
Please, do elaborate...
If you remove passes of a compiler, you, looking at it whilst running, are the compiler.
I agree. So I write tests. I use architecture to defend against the risk of super-rare code paths where I wouldn't rapidly notice if they broke. I dogfood so I find prod bugs before users do.
None of this seems that new. Even people who write TS code still write tests and still ship bugs and still have to think about good architecture patterns, like the ones in the linked post.
It's a bit ironic that now we went down the static rabbit hole so much that we don't realize it's more the opposite: by adding more to your language and compiler to enforce static checks, you turn the compiler itself more and more into a runtime that you are just going to run ahead of time, according to a potentially vastly different set of rules compared to your real runtime. There is no reason why you couldn't do something like this by will but it's just not worth it after a certain point clearly.
Is this a methodology you use at work or only for personal projects ? I'm curious how common this culture is among companies/teams.
I'm not personally aware of any companies doing this in plain JS aside from my own (I am co-founder/CEO of a two-person startup). I really like working in plain JS. It feels malleable where TS code feels brittle, almost crystalline. Even though I don't have compile-time types there's still only a small handful of different shapes of objects in the core of my software (far fewer than the average TS codebase, I'd wager), and it shouldn't take long at all for people to learn the highly consistent naming conventions that tip you off to what type of data is being handled. The result is that I'd expect that it would only be a handful of days learning the mental model for the codebase before the average person would find it far easier to read the JS code as opposed to TS code, thanks to the lower amount of visual clutter.
I also ship code super fast. When I find bugs I just fix them on the spot. When I find variables named wrong, I just rename them. The result that I often smash bugfixes and features and cleanup together and have a messy git history, but on the flip side you'll never find bugs or naming deceptions that I've left sitting for years. If something is wrong and I can reproduce it (usually easy in functional code), the debugger and I are going to get to the bottom of it, and quickly. Always and only forward!
> […] it shouldn't take long at all for people to learn the highly consistent naming conventions that tip you off to what type of data is being handled.
I’ve used languages with an approach like this. The difference in what I’ve used is that you separate the conventional part from the rest of the name with a space (or maybe a colon), then only refer to the value by the non-conventional part for the rest of the scope. Then the language enforces this convention for all of my co-workers! It’s pretty neat.
I don't get your 2nd sentence: "The difference in what I’ve used" ... Can you give an example, and name the language used?
Sure! Let’s say I want to enforce that a variable only ever holds an integer. Rather than put the conventional prefix and the name together, like this:
…I separate the conventional prefix with a space: …so now my co-workers don’t need to remember the convention – it’s enforced by the language.(I hope this makes the joke more obvious.)
I wasn't talking about Hungarian notation. I meant more like if you see a variable named `user` or `activeUser` you know that it's going to contain a predictably-shaped data object that describes a user. E.g. it will always have a `user.id` property. I would never call an string-ish ID a user, then. I would call it `activeUserId` or `userId` or just `id` if the distinction between those was already obvious from context... But that's very different from writing `strUserId` which I never do: I try to make sure my names always convey semantic distinctions.
Mhm! Exactly! In the system those other languages use, once you see the variable’s declaration:
…you’ll always know that `activeUser` contains a User value – something that might have an `Id` property. And the convention is enforced by the language, so it’s easy to communicate. These semantic distinctions are very useful, I agree.Haha I knew you'd say that. I'm not pretending there aren't advantages to strict systems of declared types. There are many! But my point is simple to the point of stupidity: there's just more stuff on screen when you have to write `User` twice. In this simple example it looks trivially simple to write the word "user" twice, but in a reasonably-complex real example the difference will be far more noticeable.
I should add a few more things: much of how I got here was exposure to Facebook's culture. Move fast and break things. React with prop types. Redux. Immutable.js. I did UI there on internal tools for datacenter operators and it was a drinking-from-the-firehose experience with exposure to new programming philosophies, tools, and levels of abstraction and refactoring velocity beyond anything I had previously encountered. Problems which in other companies I had learn to assume would never be resolved would actually consistently get fixes! Well, at that time. This was before the algorithm was fully enshittified and before the disastrous technopolitical developments in the way facebook and facebook messenger interact with each other.
Perhaps the most direct inspiration I took from there though was from the wonderful "opaque types" feature that Flow supports (https://flow.org/en/docs/types/opaque-types/) which for reasons known only to Hejlsberg and God, Typescript has never adopted; thus most people are unfamiliar with that way of thinking.
Yes, I am wondering if opaque types would be difficult to implement somehow in TypeScript? It should really be part of TypeScript if at all reasonably possible.
I'm not that familiar with the TS internals. They'd have to add a keyword to the language which could break stuff. The smart move would be to reserve the `opaque` word a few versions in advance of introducing the feature that gives it a meaning
I don't think the keyword is the problem, I am wondering more about the internals of their type inference algorithm.
godspeed with that :)