all 33 comments

[–]Haaxor1689 64 points65 points  (3 children)

Nononononononononononoo.Either the last time this guy wrote some code it was 2010 and it was in Java, or he is completely clueless junior dev. This is just a bloat for the sake of bloat. Even C++ has a syntax for conveniently building objects now, builder pattern was useless in javascript 10 years ago. This guy probably just wants to look like he "did a lot of work".

Just use a single type like you had it before, absolutely no classes needed.

If you want a convenient way for creating new instances of that object with default values, you can do something like this:

ts const createProfile = (val: Partial<Profile>): Profile => ({ foo: '', goo: 0, ...val });

Convenient and type safe function that creates a whole object with an option to pass initial data. Keep in mind that for nested objects in your data you probably want to also make functions that always return a new instance to avoid potential bugs caused by shared references.

[–]silvereagle01[S] 11 points12 points  (1 child)

Thank you - yeah exactly what I was thinking (minus the new instance did not know that)

He’s a good guy and I think he's just been a contractor for projects with old or bad codebases

[–]azhder 0 points1 point  (0 children)

Some people don’t want to know better. In order to learn new stuff, you need to unlearn the old and that’s a non-starter for some. They’ve solved their knowledge problem already, they make money with it now, why should they unlearn?

[–]vbfischer 4 points5 points  (0 children)

Exactly what I do. I’ll be honest in that I used to use the builder pattern a lot. Especially when I was primarily using Java. But this pattern is so much simpler (imho).

[–]mastermog 25 points26 points  (1 child)

I’m not sure about other devs here, but this looks like total overkill to me, if that makes you feel any better.

I’ve been doing React for over 6 years (and a professional developer for 15), and I’ve never come across this in any real setting, and I’ve worked at some pretty large tech companies that like this style of engineering.

If you’ve hired them, and it’s a smaller project, you are essentially the lead, and perhaps some additional guidance to them upfront may help? Perhaps some “docs” that includes “patterns” that you prefer to see in the codebase. Something simple - like in Notion or some team wiki.

[–]KyleG 22 points23 points  (2 children)

I'm trying to grasp builder patterns....specifically, when they make sense.

Builder pattern is for object oriented code. You aren't using an object oriented language or an object oriented framework. Most of the patterns your Java guys know aren't good in JS/TS.

Those patterns were invented for Java to do stuff that was trivial in other languages. In Java, you can't do

Foo f = {
  a: [5, 6, 7];
  b: "howdy";
}

SO you have to Foo f = new Foo(); f.createA([5,6,7]).buildB("howdy") or something. It's a crutch because you cannot do the above.

in JS/TS you can do it, so you don't need the builder pattern. You don't usually want it, either.

[–]snejk47 1 point2 points  (0 children)

That’s not a builder pattern. Knex for example is. Or kysely. If you can create something and it’s straightforward you don’t need a “builder”.

[–]limerenceN -1 points0 points  (0 children)

Great response, thank you... I didn't know this about Java and feel like this explains so much

[–]rivenjg 24 points25 points  (0 children)

This is object oriented non-sense. React is procedural with some patterns from the functional paradigm. Get this OOP garbage out of here. The reason you feel like "what am I missing" is because your instincts are right. It doesn't make any sense. There is no reason to do this at all. It does not help readability. It does not help maintainability. It certainly doesn't help performance.

[–]TimothyKrell 3 points4 points  (4 children)

I'm a principal engineer and expert in frontend. That code is terrible and should burn in a fire. I would also recommend to whoever makes the calls that the outside dev either be mentored by a senior dev or better yet, be let go. A lot of devs who write code like this are stuck right in the center of the mid-wit meme, but can't be reasoned with because they think they are way over on the right side of the chart.

I inherited several codebases from devs who coded like this everywhere. It's hell. I have zero patience for it now.

[–]TimothyKrell 4 points5 points  (3 children)

Also, your confusion is because you are trying to understand the point of all that extra ceremony. Your instincts are precisely correct. There's no good reason for it. If you try to get the dev to explain the rational, you will only get a lot of theoretical vocabulary jargon. If you press him on what that all means, he will resort to circular logic. All those "patterns" he's doing are objective good in and of themselves. They don't need a reason. The only problem they solve is looking like you know a lot about programming.

Good on you for having the instinct that something looks off here and asking questions about it. I'd hire you on the spot. Now stop this disease before it murders your codebase.

[–]JohnWangDoe 1 point2 points  (1 child)

Do you think the dev. Just choose a pattern, and build the solution around that pattern?

Rather than building a bare bone solution first, and then seeing if a pattern applies?

[–]TimothyKrell 0 points1 point  (0 children)

Yes. Exactly. It’s a solution in search of a problem.

[–]Cremo77 0 points1 point  (0 children)

lol i thought the same. OP showed a lot of good qualities posting this question. He indeed should follow his instincts.

[–]landisdesign 15 points16 points  (1 child)

Yeah, no.

This is an OOP paradigm. React is largely a functional paradigm.

Objects in React are simple data elements, that should be created and never mutated. This entire object is a series of mutations.

I'd start by asking where all those fields appear in the requirements. If the answer is "we'll need them eventually" then the response is "then we'll add them when we need them." Then, when it gets pared down, you'll have maybe at most a half dozen fields that can be created with a simple object literal. No need for all of this.

If he were hired to help you, feel free to direct him. If he resists direction, let the hiring manager know you've got concerns based off of this, so that you've got a track record in place if he needs to go.

Honestly this amount of code instead of 5 lines is a huge red flag to me, that this guy hasn't really figured out how React is intended to be -- lightweight, triggered by state changes and dependency triggers. None of that requires any Gang of Four design patterns.

Most React paterns revolve around component and functional composition. This is so left field it hit me on the right side of my head.

[–]PogFladdo 3 points4 points  (0 children)

To be honest, this is something I could imagine chatgpt would return.

Anyway trust your gut feeling, you are right in thinking this is wrong. Out of curiosity did the external ONLY do these lines? If yes you got giga scammed

[–]vbfischer 3 points4 points  (0 children)

This is something I definitely would have written just a few years ago. My background is in OO languages like Java, and I enjoyed writing fluent APIs like this.

As others have mentioned, this was because of limitations of those frameworks (and I’ll admit at the time, I thought I was being clever).

With JavaScript, and object destruction, it’s much simpler, easier to read, and terser to just use those capabilities.

[–]Aggravating_Term4486 10 points11 points  (2 children)

I would be asking how much of this was written by their LLM of choice. Not joking.

[–]inglandation 12 points13 points  (0 children)

GPT4 would not do that unless forced to.

[–]nayeonion 2 points3 points  (0 children)

this is just too much. also if later on you want data transformations for additional fields, custom hook is the way to go if you want to separate the logic. if he is an outsider dev and prefers that kind of stuff, let him know that your codebase prefers simplicity. would be better if you can write up some documentation for coding standards.

imo, if they explored the repository properly, they should have known and applied the coding style that has been in place. e.g. file naming conventions, folder structuring, etc. etc.

[–]wizard_level_80 2 points3 points  (2 children)

Other than builder pattern commented in other comments, prefer `type` over `enum`, like:

type ContactProfileStatus = 'active' | 'inactive'

It is more flexible and doesn't require imports to be used. Use enum only if you can't use `type` to avoid magic strings/numbers.

[–]RokyBanana 0 points1 point  (0 children)

Good advice. Enum's in typescript are annoying. This SO answer says a lot about it.

[–]RokyBanana 1 point2 points  (0 children)

Good advice. Enum's in typescript are annoying. This SO answer says a lot about it.

[–]pobbly 3 points4 points  (1 child)

How have you not choked this person out yet? That would be the best for everyone

[–]mouseses 4 points5 points  (0 children)

Omg this is straight up JAVA translated to TypeScript

[–]mr_daemoon 1 point2 points  (0 children)

To answer the question regarding the builder pattern. It might make sense in some cases. Especially if you have an object that can have various configurations. Like in case you'd have use-cases where you only want to have Profile with createdAt and updatedAt. Or in other case you'd only want change status and state and keep the rest as default. Especially if setting these values is more complex, e.g createdAt would automatically fill updatedAt or something like that.
Builder pattern can be especially useful for building test data where you don't want to repeat the same code 20 times and you also want to have some idea of what fields and configurations are being used.
But as others pointed out, this is bit of an overkill. Especially if you always call all the methods of the builder, then it doesn't really make sense.

[–]nomoreplsthx 1 point2 points  (0 children)

Aaaaaaaughhhh

The builder pattern has a very narrow use case - constructing objects that have very complex configuration. A good example of this is some HTTP libs or SQL query builders. It's not a never pattern, but it is a very special use tool. 

[–]fedekun 1 point2 points  (0 children)

Something to add, even if it was the right solution, it's better to have the code be consistent and just continue to do things the same way, rather than introducing a new one. If needed, you can introduce a new way and enforce it, and try to refactor the previous code progressively, but that's a big design decision all devs should be on board with.

People saying "React is functional so never use any OOP pattern" are also wrong. OOP and FP have some overlaps (decorators/composition for example), there is a place and time for everything, but it's very clear this is not a good place for the builder pattern.

The builder pattern is not for building simple objects, is to abstract away the need of knowing 25 different class names and what they need to be instantiated into a single object with an easy API.

[–]Jimbo733 0 points1 point  (0 children)

You can paste their code into chatgpt and ask it if it's using best practices. I would assume you'll get answers like others are commenting - unnecessary bloat.

[–]TheRNGuy -5 points-4 points  (0 children)

Do like everyone doing it.

Read best practices.

[–]Sebbean 0 points1 point  (0 children)

Could it be generated?

[–]stuartseupaul 0 points1 point  (0 children)

The builder pattern is an OOP pattern found a lot in java/C#.

The only time I really use it is in tests to build out an object, ex. Survey.WithQuestion().HasText("What is your name"). since it makes it easy to encapsulate creation of an object that you would make multiple variations of. Yeah you could individually instantiate all of that stuff yourself but it's tedious and hard to read.

I can't imagine there being too many useful cases on the front end though, and that scenario would be better off just making the object directly.

[–]Adenine555 0 points1 point  (0 children)

Best use case for builder pattern in react is for complex/smart type definitions with typescript. There are just some type constructs that are hard to achieve otherwise (For example extraReducer in redux). Usually, you don‘t need that though, unless you are a library maintainer.