Live data from Hacker News

Self-Documenting Code

lackofimagination.org

41–50 of 131 posts

Re: Self-Documenting Code

#41
post #36

I find the comment at the end interesting // Creates a user and returns the newly created user's id on success Hmm, it returns an id? But the @returns is Promise ? The code as written will change when userService.create changes... without the actual, human readable bit of prose, that potential code issue could be easily overlooked. Of course, here the code could have a newtype for UserId and return Promise , making t…

This, but move isPasswordValid below if (!isUserValid)

Re: Self-Documenting Code

#43
Types are the best form of documentation because they can be used to automatically check for user error, are integral to the code itself, and can provide inline documentation. The more I program in dynamically typed (or even weakly statically typed) languages the more I come to this conclusion.

Re: Self-Documenting Code

#44
post #34

My cut: const passwordRules = [/[a-z]{1,}/, /[A-Z]{1,}/, /[0-9]{1,}/, /\W{1,}/]; async function createUser(user) { const isUserValid = validateUserInput(user); const isPasswordValid = user.password.length >= 8 && passwordRules.every((rule) => rule.test(user.password)); if (!isUserValid) { throw new Error(ErrorCodes.USER_VALIDATION_FAILED); } if (!isPasswordValid) { throw new Error(ErrorCodes.INVALID_PASSWORD); } cons…

My only nitpick is that the const isPasswordValid = ... should be just before its use (between the first two ifs). Other than that, I prefer this approach (although I would inline the booleans in the ifs to avoid the one-use variables. But that's ok). > Don't use a bunch of tiny functions Exactly this. I only do that when the function is used in more than 10 places and it provides some extra clarity (like something a…

> My only nitpick is that the const isPasswordValid = ... should be just before its use (between the first two ifs).

Wouldn’t that cause the regexes to be recompiled every time you call the function?

Re: Self-Documenting Code

#45
post #34

My cut: const passwordRules = [/[a-z]{1,}/, /[A-Z]{1,}/, /[0-9]{1,}/, /\W{1,}/]; async function createUser(user) { const isUserValid = validateUserInput(user); const isPasswordValid = user.password.length >= 8 && passwordRules.every((rule) => rule.test(user.password)); if (!isUserValid) { throw new Error(ErrorCodes.USER_VALIDATION_FAILED); } if (!isPasswordValid) { throw new Error(ErrorCodes.INVALID_PASSWORD); } cons…

“ Don't use a bunch of tiny functions. This makes it harder for future eng to read the code …”

This is where the naming things bit comes in. You name the function correctly, then when the body is read to understand that it works as named, you can remove that cognitive complexity from your brain and continue on. Once you’ve built trust in the codebase that things do what they claim, you can start getting a top-down view of what the code does.

That is the power of proper abstraction.

Re: Self-Documenting Code

#46
post #34

My cut: const passwordRules = [/[a-z]{1,}/, /[A-Z]{1,}/, /[0-9]{1,}/, /\W{1,}/]; async function createUser(user) { const isUserValid = validateUserInput(user); const isPasswordValid = user.password.length >= 8 && passwordRules.every((rule) => rule.test(user.password)); if (!isUserValid) { throw new Error(ErrorCodes.USER_VALIDATION_FAILED); } if (!isPasswordValid) { throw new Error(ErrorCodes.INVALID_PASSWORD); } cons…

My issue with this is that you're using exceptions for control flow. A user not being valid is expected (duplicate username). A password not matching a regex is also expected.

Then, in general (not seen here as there are no types), I like to use a lot of types in my code. The incoming user would be of type UnvalidatedUser, whereas the return type of this function would be StoredUser or something like that to distinguish the incoming user type with the outgoing. I like to attach semantics to a type, not to conditions: https://existentialtype.wordpress.com/2011/03/15/boolean-bli...

Re: Self-Documenting Code

#48
post #14

Earlier quoted context omitted.

I would go further to say that syntax should never be used. for example with Go: > cheapFunction(...) || expensiveFunction(...) is not valid unless both functions return bool > car = car || "bmw" is not valid at all, because both types would need to be bool > funcA(...) && funcB_WhichMightBreakWithoutFuncA(...) not valid unless functions return bool. I think Go smartly realized this syntax is just sugar that causes m…

This has nothing to do with syntax and short circuiting and everything to do with Go's type system. Go, like most compiled languages, has no concept of "truthiness". JavaScript is not Go and has truthiness. We can debate the merits of truthiness and using it this way, but let's have that debate on the merits, not by invoking other languages with completely different design constraints. Your argument here is similar t…

just because a footgun exists, doesn't mean you should use it

Re: Self-Documenting Code

#49
post #44

Earlier quoted context omitted.

My only nitpick is that the const isPasswordValid = ... should be just before its use (between the first two ifs). Other than that, I prefer this approach (although I would inline the booleans in the ifs to avoid the one-use variables. But that's ok). > Don't use a bunch of tiny functions Exactly this. I only do that when the function is used in more than 10 places and it provides some extra clarity (like something a…

> My only nitpick is that the const isPasswordValid = ... should be just before its use (between the first two ifs). Wouldn’t that cause the regexes to be recompiled every time you call the function?

I don't think so, if it does it will now too. In fact with my suggestion the regex checks will not run if the user is not valid (as it is now it will always run)

    const isUserValid = ... 
    if(!isUserValid) ...

    const isPasswordValid = ...
    if(!isPasswordValid) ...
Etc

Re: Self-Documenting Code

#50
post #48

Earlier quoted context omitted.

This has nothing to do with syntax and short circuiting and everything to do with Go's type system. Go, like most compiled languages, has no concept of "truthiness". JavaScript is not Go and has truthiness. We can debate the merits of truthiness and using it this way, but let's have that debate on the merits, not by invoking other languages with completely different design constraints. Your argument here is similar t…

just because a footgun exists, doesn't mean you should use it

Then let's talk—with specifics!—about why it's a footgun and we shouldn't use it.

"Because Go doesn't support it" would also be a reason to avoid:

* Generics (at least until recently)

* Classes

* Prototypes

* Exceptions

* Async/Await

* Package managers (until recently)

* Algebraic data types

* Effect types

* Type inference

* Type classes

* Etc.

You could argue that one or more of these are footguns, but I seriously doubt you'd consider them all to be, so let's talk about what separates the footgun from the feature that just didn't fit in Go's design.

Post reply on HN