4 ms·
I'm curious how would look like a real example with different Email classes. It seems silly. You don't often need a VerifiedEmail. But VerifiedUser is more usef
by bvrmn 2y ago
I'm curious how would look like a real example with different Email classes. It seems silly. You don't often need a VerifiedEmail. But VerifiedUser is more useful from VerifiedEmail. You could get an implicitly verified email without introducing a new type.
- TeMPOraL 2y agoBut you don't want "implicitly verified", when there exist any code to which the difference between "verified" and "not verified" matters in some way. In your own example, if you happen to use e-mail verification as proxy for user verification, then the very code that creates VerifiedUser instances would want to have VerifiedEmail as input!
- bvrmn 2y agoYou don't need VerifiedUser in the moment user click verification link. It's a state changing action. You get verified user from db and work with it later.
- TeMPOraL 2y agoFine. It still has merit that the code pulling verified user data from verified users table returns VerifiedUser objects, so the code processing them is clear about its assumptions, and won't accidentally get reused or refactored in UnverifiedUser flows.
- noelwelsh 2y agoAbstracting the email example, if you have a finite state machine that verifies some fact, you can model each state as a type. Their example goes Email -> ValidEmail -> VerifiedEmail. You could equally go String -> Email -> VerifiedEmail, where String is what the user submits as their email address. If you are verifying emails in the context of verifying users you could go Map<String, String> -> User -> VerifiedUser, where Map<String, String> is what you receive from an HTML form submission. Getting hung up on the details of which type you use is missing the overall point.
- atoav 2y agoGood point, the names really don't matter and the first type could as well have been a string. I just wanted to illustrate the concept of using the type system to add important information and guarantees to certain variables. Your state machine analogy is a good one, because the conversion between different types is a bit like the transitions between states: you have to make explicit how exactly they are meant to happen (or whether they are actually possible). This is a good thing, having less degrees of freedom may seem like it makes it harder to code, but in fact it allows you to reason better about what the system is doing at any given point in your code.
- bvrmn 2y agoState machine is always a trivial part. Email verification is stupidly simple task. But User propagation through the system and available actions based on verified email are not.
- codetrotter 2y ago> You don't often need a VerifiedEmail. But VerifiedUser is more useful from VerifiedEmail. You could get an implicitly verified email without introducing a new type. Be careful about how the email is implicitly verified though, and how different login methods and assumptions interact when for example migrating users from one system or platform to another. https://krebsonsecurity.com/2024/07/researchers-weak-security-defaults-enabled-squarespace-domains-hijacks/ https://krebsonsecurity.com/2024/07/researchers-weak-securit... > analysis released by security experts at Metamask and Paradigm finds the most likely explanation for what happened is that Squarespace assumed all users migrating from Google Domains would select the social login options — such “Continue with Google” or “Continue with Apple” — as opposed to the “Continue with email” choice > Squarespace never accounted for the possibility that a threat actor might sign up for an account using an email associated with a recently-migrated domain before the legitimate email holder created the account themselves > since there’s no password on the account, it just shoots them to the ‘create password for your new account’ flow. And since the account is half-initialized on the backend, they now have access to the domain in question This kind of mistake makes me think that it’s better to be super explicit about the state of everything when it comes to accounts. So even if it may seem excessive to have VerifiedEmail as a type instead of marking the account as verified or not. I will prefer being explicit. And in medium to big size systems with multiple pieces of user account data that require separately keeping track of verification state it will be necessary anyway. For example even something as simple as having one email and one phone number associated with a user. Or beyond that one user having multiple email addresses or multiple phone numbers etc.
- atoav 2y agoThank you for beinging up this excellent display of hidden dangers to my very crude example about why type systems should be used if they are there. My point was specifically about complexity. Using a string for all kind of emails of course works, tying guarantees to other things like VerifiedUser works as well. These implicit guarantees just fail to safe your ass once things get more complex and there is an edge case you didn't think of.
- bvrmn 2y agoSo in the end users were in state "not verified" after migration. Seems email is a secondary property.