Web Developer by day, and aspiring Swift developer at night.

  • 2 Posts
  • 342 Comments
Joined 1 year ago
cake
Cake day: July 1st, 2023

help-circle



  • dohpaz42@lemmy.worldtoScience Memes@mander.xyzGet good.
    link
    fedilink
    English
    arrow-up
    11
    ·
    10 days ago

    I baby talked my kids (now it’s Brain Rot). I also talk to them like an adult. I’ve always encouraged them to ask questions when they don’t understand something. My 9-year-old is not shy about stopping mid-conversation and asking what a word or phrase means.


  • My question is why is this a backend universal question? This should be a per user/instance frontend solution; meaning I would curate my communities into a group on @lemmy.world and it’s unique to me.

    Now I should be able to export or share my groupings if I want, and it should be read-only in the sense that if I post, I post to a single community and not the group as a whole. The only thing a backend should do is allow the frontend to retrieve posts from multiple communities in one call.

    In other words, keep it simple.




  • I wouldn’t. Not from this example anyway. YAGNI is an important paradigm and introducing plenty of classes upfront to implement trivial checks is overengineering…

    Classes, functions, methods… pick your poison. The point is to encapsulate your logic in a way that is easy to understand. Lumping all of the validation logic into one monolithic block of code (be it a single class, function, or methods) is not self-documenting. Whereas separating the concerns makes it easier to read and keep your focus without mixing purposes. I’m very-engineering (imo) would be something akin to creating micro services to send data in and get a response back.

    Edit: Your naming convention isn’t the best either. I’d expect UserInputValidator to validate user input, maybe sanitize it for a database query, but not necessarily an existence check as in the example.

    If you go back to my example, you’ll notice there is a UserUniqueValidator, which is meant to check for existence of a user.

    And if you expect a validator to do sanitation, then your expectations are wrong. A validator validates, and a sanitizer sanitizes. Not both.

    For the uninitiated, this is called Separation of Concerns. The idea is to do one thing and do it well, and then compose these things together to make your program — like an orchestra.


  • 👆 This. In my experience, I’ve seen a lot of developers get upset about “their code” not being used, time wasted, or someone else changing the code after the fact. Who cares? Once you commit that code, it’s no longer your code. It’s the company’s code. Your paycheck will reflect the same amount of money regardless — and if it doesn’t, you may want to find a better employer. 😅


  • async function createUser(user) {
        validateUserInput(user) || throwError(err.userValidationFailed);
        isPasswordValid(user.password) || throwError(err.invalidPassword);
        !(await userService.getUserByEmail(user.email)) || throwError(err.userExists);
    
        user.password = await hashPassword(user.password);
        return userService.create(user);
    }
    

    Or

    async function createUser(user) {
        return await (new UserService(user))
            .validate()
            .create();
    }
    
    // elsewhere…
    const UserService = class {
        #user;
    
        constructor(user) {
            this.user = user;
        }
    
        async validate() {
            InputValidator.valid(this.user);
    
           PasswordValidator.valid(this.user.password);
    
            !(await UserUniqueValidator.valid(this.user.email);
    
            return this;
        }
    
        async create() {
            this.user.password = await hashPassword(this.user.password);
    
            return userService.create(this.user);
        }
    }
    

    I would argue that the validate routines be their own classes; ie UserInputValidator, UserPasswordValidator, etc. They should conform to a common interface with a valid() method that throws when invalid. (I’m on mobile and typed enough already).

    “Self-documenting” does not mean “write less code”. In fact, it means the opposite; it means be more verbose. The trick is to find that happy balance where you write just enough code to make it clear what’s going on (that does not mean you write long identifier names (e.g., getUserByEmail(email) vs. getUser(email) or better fetchUser(email)).

    Be consistent:

    1. get* and set* should be reserved for working on an instance of an object
    2. is* or has* for Boolean returns
    3. Methods/functions are verbs because they are actionable; e.g., fetchUser(), validate(), create()
    4. Do not repeat identifiers: e.g., UserService.createUser()
    5. Properties/variables are not verbs; they are state: e.g., valid vs isValid
    6. Especially for JavaScript, everything is const unless you absolutely have to reassign its direct value; I.e., objects and arrays should be const unless you use the assignment operator after initialization
    7. All class methods should be private until it’s needed to be public. It’s easier to make an API public, but near impossible to make it private without compromising backward compatibility.
    8. Don’t be afraid to use if {} statements. Short-circuiting is cutesy and all, but it makes code more complex to read.
    9. Delineate unrelated code with new lines. What I mean is that jamming all your code together into one block makes it difficult to follow (like run-on sentences or massive walls of text). Use new lines and/or {} to create small groups of related code. You’re not penalized for the white space because it gets compiled away anyway.

    There is so much more, but this should be a good primer.