I often do a more restrained version of this where I leave the first two or three params as normal, but group any additional optional params as an options or config object. I guess doing it across the board has the advantage that it takes less bikeshedding around what should be where.
I’ve been using this for a while now. The reason is simple: it makes it clear what needs to be passed and forces intentional argument usage, especially in big projects where user_id must explicitly be user_id. This is crucial with AI tools like Copilot, where people might pass values blindly. It also keeps TRPC inputs clean, allows easy expansion without refactoring, and makes Git history changes more readable by avoiding unnecessary reformatting.
Same. I also pair it with an ideal of as many values as possible being optional and having sensible defaults. Don’t listen to the people going on about micro-optimizations either. That stuff is a waste of time.
I feel this is, uuhh - not great advice. TLDR: with this pattern I think you are polluting the run-time for a (in my opinion) superficial 'improvement' to your developer experience that can easily be done by tooling and/or other patterns statically. The first reason is unintended side effects. The primitive data types are passed by value. That means that if you would update such a parameter in your function body then the change only applies to the scope of the function. However, because JavaScript objects live on the heap, the reference is passed by value and points to the same JavaScript object! That means that if you forget to destructure it then you can easily create some serious unintended side effects that are difficult to debug. This matters because you may be aware of this at the moment, but your (junior) colleague, an AI tool that generates code or worse - your future self may not be aware of this behavior. When you start using objects as parameters all over it is a (and in my opinion: unnecessary) ticking time bomb. Ironically, at about 05:00 you actually use the same argument but phrase it as a benefit for new developers that are not familiar with the setup and/or language. The second reason is performance. As this is TypeScript, and based on other comments, people may use this pattern not just in the backend but also in the frontend. Via this pattern you create a new object for every function call. And unlike regular parameters, this object lives on the heap and needs to be cleaned up again by the garbage collector. This is significant overhead that is invisible for just a few function calls. But as the complexity of your application increases (think about when the function is called in dozens, or hundreds of places as you mention at about 05:20) it will start to add up. Your fast development computer may be fine, but slow devices may start to notice this. I'm not sure whether the used interpreter can optimize out the JavaScript object. If that is the case, then the performance argument is irrelevant. But if you use Babel to transpile your code to guarantee your website works for all users then that does not matter anyway - it is transpiled to regular JavaScript object access. And then it certainly is relevant, and becomes more and more relevant as the complexity of your application increases. edit: after toying around a bit it appears that in basic benchmarks the TurboFan compiler that is behind the V8 JavaScript Engine of Chrome can optimize out the JavaScript object in some cases. See also my reaction to Shuyinz 2nd post below. This applies to both the destructuring and the JavaScript object access approaches. What bothers me most I suppose is that the problem that you're trying to solve is understanding what the parameters are. You mention at 02:00 several ways to fix this. These fixes would apply for all code bases, regardless whether they use this pattern or not. The argument that 'not all tooling supports this' is not a real argument - just get the better tooling. But instead of recommending that, you instead suggest to trade run-time performance for a (in my opinion) superficial 'improvement' to the development experience. For what it matters, I would instead: - 1) Use proper tooling. For example, IntelliJ IDEA has support for parameter hints. You of course can't change their order, but that is (in my opinion) a gimmick. - 2) To prevent boolean blindness you can use const enums in TypeScript. These should be transpiled away (read: replaced with their value) when turning TypeScript into JavaScript, which means you get the benefits but with no runtime costs. - 3) To prevent making mistakes with passing in arguments of the same type but different meaning (as an example: userID vs bookID), use flavored types instead. For example, you can flavor a user ID to make sure that only user IDs can be passed into that argument even though book IDs are strings too. This way the compiler literally prevents you from compiling your code if you make a mistake. And the flavor only exists in TypeScript, it is transpiled away. Therefore there's no impact on your runtime. For a great article about flavored types, search for 'Flavoring: Flexible Nominal Typing for TypeScript' by Drew Colthorp.
You have a very extreme stance on speed and performance. I would like to challenge you by giving us concrete proof provide data, results, and details on how you implemented your approach in scientifically based evidence approach. I've met many developers who talk about speed and performance, claiming that doing XYZ is bad. Whenever I ask for concrete proof real examples and testing data to analyze, not a single one has been able to provide it. They couldn't even show an actual implementation.
@@Shuyinz I can appreciate both sides of the coin: striving for optimal performance, even if theoretical, but also just getting shit done. I personally find that striking the right balance between both is the most challenging part of not only development, but with similar types of trade-offs in any other discipline, ESPECIALLY entrepreneurship!
@@readywhen Finding to strike the "right balance between the both" is Axioms. I would lean more towards as best as possible by finding proof than doing a "Broscience" approach. It's all about rhetoric here.
@@Shuyinz I completely agree that there's a right balance between both. I personally do not think I have an extreme stance on performance. I just do not appreciate potentially polluting the run time with something that can be statically computed by good tooling and TypeScript conventions (see also the suggestions at the bottom). I hope you can see how that is objective. And I would still not be in favor of this pattern simply because in my opinion there are better options that do their work at the level of the TypeScript compiler. I've never benchmarked JavaScript before but out of curiosity I looked into it. For a very basic setup the (relative) results are: functionOne: 13907 // normal parameters functionTwo: 13977 // destructuring functionThree: 14060 // object access I learned that the JavaScript engine that is used in Chrome optimizes the code. Apparently this is called the TurboFan compiler. It compiles the JavaScript and removes inefficiencies where it can safely do so. I think the results just indicate noise, the actual interesting bits that I would like to benchmark (the JavaScript object creation) are optimized out by the compilation step. I make this observation based on the heap in the profiler of Chrome - it is a flat line in all three cases. Not a single additional byte allocated. The difference in the results is therefore not significant. I don't know how to force the engine to benchmark what I want it to. I'm fairly certain that my benchmark does not represent how actual code would look like. And I'm not sure if we can trust the compiler to always make this optimalization. I'm not going to dive into it deeper. I will adjust my comment stating that the JavaScript engine of chrome can optimize out the use of an object in some scenario's. I disagree calling what I wrote broscience. I was open about possible counter arguments. And those lined up with my findings. It's less broscience then the title of the video, which states as a fact that this pattern increases team productivity. Which is based on... ?. Definitely not concrete proof, data, results, and details in a scientifically based evidence approach. Just to quote you, mind you 😄.
Really thank you for the long post, I just went to the comments to try find someone dicenting and why, and you have an absolute awesome point and data to back it up. I do 100% agree with you that, if a tool (that is not expensive, or is free) can do it, then any change is denied... use better tooling if it exists, period. I will not "downgrade" my code because you code on "notepad", that is your issue, not mine. Is like if we were still using the abacus instead of a graphical calculator... switch already... learn tools that help you code better, not the other way around...
I think this makes more sense for more complex-argument functions. However, in the event you don't want to change the signature or can't for some reason, I'd recommend another way to find out where `sendEmailToWaitingList` is set/referenced. You can change the type annotation of it to something other than `boolean`, and that will flag any set/references to that which expect it to be a `boolean`. This will flag all of these are errors in the terminal when checking with `tsc`, and you can go to each location to find how you want to address this change.
i always recommend to my team use this instead of lots of args in any function, because someone could easily invert some params of same type and u wouldn't catch the bug when u build. with an object, u have to pass the name of the property, so there is no chance to do that mistake.
Cool little workaround to make it "named parameters", I've been using this for a long time now. Only problem is, you can't be sure that you found all the usages by searching a param, because it can still be covertly passed by destructuring or just complete object etc.
Yeah, I favor this approach myself for clarity reasons. The other reason is that you're not limited by the order of arguments, which introduces its own pitfalls, especially when combined with optional arguments.
Does passing an object to a function like this opposed to passing them directly break memorisation? For example if we have a function wrapped in an useCallback , would we need to ensure that the object being passed as parameters is also memoed so as to preserve referential stability of the object?
Apparently I did follow and tried out this pattern without knowing its a pattern. Although I do really like that since it has the very big benefit of memory saving and easier handling of data passed to several functions I did come along the Problem quite often that its quite "boiler-platey" to work with. You will always have a wrapper around the values you want. Some may find it super I though found it quite cumbersome at times. I think just like with all sorts of pattern using this in an configuration-ish situation where you would have many options or a subject to change function/class whatever, this pattern shines the most
Actually this is used by React and other JSX libraries, Props is an object that is passed down to inner components. It's required because HTML has attributes that have name and value. This style isn't a silver bullet, I wouldn't use it if the function accepts two or less arguments but this is working solution
This is why I really like Kotlin's named arguments. Instead of creating a wrapper type for the args, you can just call the function like this: func(foo=123, bar=100);
If you just want to find all usages of the functions that declare the "sendEmailToWaitingList" arg, why not find all usages and filter via regex for more than 2 commas between closing brackets? So from my persepective, i would only do this, if changes need to also occur to the params given, since objects are naturally passed as refs (imagine the quantity being updated of that object)
Man, I do this all the time in our front end react app. Our api is C# and one of the things that greatly annoys me about C# is how difficult this pattern is in to implement. A list of method/class args is so annoying.
there are tools where you can map backend types to front end types and never have to declare types in your frontend by hand. It could be done with Graphql. But can also use w/e to generate classes and types from a C# source file
I really love watching your videos. I'm new to web development world and I have being learning javascript and now focusing on typescript. I really love how to break things down. some of them a little complex but I always get the context and hopefully with better fluency will come better understanding of the codes in general. Thanks plenty
That's really convenient thing. React components also use a similar approach for passing props within a single object. By the way, who are your top three authors on software engineering?
@@andrew-burgess the only thing I can think of is because the engine cannot memoise the arguments since the object reference might be different every time it's called. But I'm no expert and would also like to know.
Lol its looks so pathetic from you to say that you dont get what that argument is so you offer to make arguments as object and descruture it😂im 100% sure you learned vim only to say that you are using vim btw. Truly internet trendy dev without actual knowledges
probably the first sign of the need of this refactoring is the fact that your function takes some bools.
Or function callbacks
Or more than 1 optional argument
Sarcasm
I smell skill issues here
@@Adelwho true
I often do a more restrained version of this where I leave the first two or three params as normal, but group any additional optional params as an options or config object. I guess doing it across the board has the advantage that it takes less bikeshedding around what should be where.
Yes, as an "options" arg
I’ve been using this for a while now. The reason is simple: it makes it clear what needs to be passed and forces intentional argument usage, especially in big projects where user_id must explicitly be user_id. This is crucial with AI tools like Copilot, where people might pass values blindly. It also keeps TRPC inputs clean, allows easy expansion without refactoring, and makes Git history changes more readable by avoiding unnecessary reformatting.
Y'know, I wonder if this pattern is easier for AI tools to get right. Or just harder to mess up :) Maybe that's another benefit of it.
@andrew-burgess it helps a lot hhh.
Same. I also pair it with an ideal of as many values as possible being optional and having sensible defaults. Don’t listen to the people going on about micro-optimizations either. That stuff is a waste of time.
did you ever think about cost of creating object vs using params in stack?
I feel this is, uuhh - not great advice. TLDR: with this pattern I think you are polluting the run-time for a (in my opinion) superficial 'improvement' to your developer experience that can easily be done by tooling and/or other patterns statically.
The first reason is unintended side effects. The primitive data types are passed by value. That means that if you would update such a parameter in your function body then the change only applies to the scope of the function. However, because JavaScript objects live on the heap, the reference is passed by value and points to the same JavaScript object! That means that if you forget to destructure it then you can easily create some serious unintended side effects that are difficult to debug. This matters because you may be aware of this at the moment, but your (junior) colleague, an AI tool that generates code or worse - your future self may not be aware of this behavior. When you start using objects as parameters all over it is a (and in my opinion: unnecessary) ticking time bomb. Ironically, at about 05:00 you actually use the same argument but phrase it as a benefit for new developers that are not familiar with the setup and/or language.
The second reason is performance. As this is TypeScript, and based on other comments, people may use this pattern not just in the backend but also in the frontend. Via this pattern you create a new object for every function call. And unlike regular parameters, this object lives on the heap and needs to be cleaned up again by the garbage collector. This is significant overhead that is invisible for just a few function calls. But as the complexity of your application increases (think about when the function is called in dozens, or hundreds of places as you mention at about 05:20) it will start to add up. Your fast development computer may be fine, but slow devices may start to notice this.
I'm not sure whether the used interpreter can optimize out the JavaScript object. If that is the case, then the performance argument is irrelevant. But if you use Babel to transpile your code to guarantee your website works for all users then that does not matter anyway - it is transpiled to regular JavaScript object access. And then it certainly is relevant, and becomes more and more relevant as the complexity of your application increases.
edit: after toying around a bit it appears that in basic benchmarks the TurboFan compiler that is behind the V8 JavaScript Engine of Chrome can optimize out the JavaScript object in some cases. See also my reaction to Shuyinz 2nd post below. This applies to both the destructuring and the JavaScript object access approaches.
What bothers me most I suppose is that the problem that you're trying to solve is understanding what the parameters are. You mention at 02:00 several ways to fix this. These fixes would apply for all code bases, regardless whether they use this pattern or not. The argument that 'not all tooling supports this' is not a real argument - just get the better tooling. But instead of recommending that, you instead suggest to trade run-time performance for a (in my opinion) superficial 'improvement' to the development experience.
For what it matters, I would instead:
- 1) Use proper tooling. For example, IntelliJ IDEA has support for parameter hints. You of course can't change their order, but that is (in my opinion) a gimmick.
- 2) To prevent boolean blindness you can use const enums in TypeScript. These should be transpiled away (read: replaced with their value) when turning TypeScript into JavaScript, which means you get the benefits but with no runtime costs.
- 3) To prevent making mistakes with passing in arguments of the same type but different meaning (as an example: userID vs bookID), use flavored types instead. For example, you can flavor a user ID to make sure that only user IDs can be passed into that argument even though book IDs are strings too. This way the compiler literally prevents you from compiling your code if you make a mistake. And the flavor only exists in TypeScript, it is transpiled away. Therefore there's no impact on your runtime.
For a great article about flavored types, search for 'Flavoring: Flexible Nominal Typing for TypeScript' by Drew Colthorp.
You have a very extreme stance on speed and performance. I would like to challenge you by giving us concrete proof provide data, results, and details on how you implemented your approach in scientifically based evidence approach.
I've met many developers who talk about speed and performance, claiming that doing XYZ is bad. Whenever I ask for concrete proof real examples and testing data to analyze, not a single one has been able to provide it. They couldn't even show an actual implementation.
@@Shuyinz I can appreciate both sides of the coin: striving for optimal performance, even if theoretical, but also just getting shit done. I personally find that striking the right balance between both is the most challenging part of not only development, but with similar types of trade-offs in any other discipline, ESPECIALLY entrepreneurship!
@@readywhen Finding to strike the "right balance between the both" is Axioms. I would lean more towards as best as possible by finding proof than doing a "Broscience" approach. It's all about rhetoric here.
@@Shuyinz I completely agree that there's a right balance between both. I personally do not think I have an extreme stance on performance. I just do not appreciate potentially polluting the run time with something that can be statically computed by good tooling and TypeScript conventions (see also the suggestions at the bottom). I hope you can see how that is objective. And I would still not be in favor of this pattern simply because in my opinion there are better options that do their work at the level of the TypeScript compiler.
I've never benchmarked JavaScript before but out of curiosity I looked into it. For a very basic setup the (relative) results are:
functionOne: 13907 // normal parameters
functionTwo: 13977 // destructuring
functionThree: 14060 // object access
I learned that the JavaScript engine that is used in Chrome optimizes the code. Apparently this is called the TurboFan compiler. It compiles the JavaScript and removes inefficiencies where it can safely do so. I think the results just indicate noise, the actual interesting bits that I would like to benchmark (the JavaScript object creation) are optimized out by the compilation step. I make this observation based on the heap in the profiler of Chrome - it is a flat line in all three cases. Not a single additional byte allocated. The difference in the results is therefore not significant.
I don't know how to force the engine to benchmark what I want it to. I'm fairly certain that my benchmark does not represent how actual code would look like. And I'm not sure if we can trust the compiler to always make this optimalization. I'm not going to dive into it deeper. I will adjust my comment stating that the JavaScript engine of chrome can optimize out the use of an object in some scenario's.
I disagree calling what I wrote broscience. I was open about possible counter arguments. And those lined up with my findings. It's less broscience then the title of the video, which states as a fact that this pattern increases team productivity. Which is based on... ?. Definitely not concrete proof, data, results, and details in a scientifically based evidence approach. Just to quote you, mind you 😄.
Really thank you for the long post, I just went to the comments to try find someone dicenting and why, and you have an absolute awesome point and data to back it up. I do 100% agree with you that, if a tool (that is not expensive, or is free) can do it, then any change is denied... use better tooling if it exists, period. I will not "downgrade" my code because you code on "notepad", that is your issue, not mine. Is like if we were still using the abacus instead of a graphical calculator... switch already... learn tools that help you code better, not the other way around...
You could also set JSDoc annotations to make your code more readable!
I think this makes more sense for more complex-argument functions. However, in the event you don't want to change the signature or can't for some reason, I'd recommend another way to find out where `sendEmailToWaitingList` is set/referenced. You can change the type annotation of it to something other than `boolean`, and that will flag any set/references to that which expect it to be a `boolean`. This will flag all of these are errors in the terminal when checking with `tsc`, and you can go to each location to find how you want to address this change.
i always recommend to my team use this instead of lots of args in any function, because someone could easily invert some params of same type and u wouldn't catch the bug when u build. with an object, u have to pass the name of the property, so there is no chance to do that mistake.
Cool little workaround to make it "named parameters", I've been using this for a long time now.
Only problem is, you can't be sure that you found all the usages by searching a param, because it can still be covertly passed by destructuring or just complete object etc.
Yeah, I favor this approach myself for clarity reasons. The other reason is that you're not limited by the order of arguments, which introduces its own pitfalls, especially when combined with optional arguments.
Does passing an object to a function like this opposed to passing them directly break memorisation? For example if we have a function wrapped in an useCallback , would we need to ensure that the object being passed as parameters is also memoed so as to preserve referential stability of the object?
Dependency inversion is awesome
Apparently I did follow and tried out this pattern without knowing its a pattern.
Although I do really like that since it has the very big benefit of memory saving and easier handling of data passed to several functions
I did come along the Problem quite often that its quite "boiler-platey" to work with. You will always have a wrapper around the values you want.
Some may find it super I though found it quite cumbersome at times.
I think just like with all sorts of pattern using this in an configuration-ish situation where you would have many options
or a subject to change function/class whatever, this pattern shines the most
Actually this is used by React and other JSX libraries, Props is an object that is passed down to inner components.
It's required because HTML has attributes that have name and value.
This style isn't a silver bullet, I wouldn't use it if the function accepts two or less arguments but this is working solution
This is why I really like Kotlin's named arguments. Instead of creating a wrapper type for the args, you can just call the function like this:
func(foo=123, bar=100);
To not have it like this in the first place is a tooling shortcoming that never suggested a better way of doing things.
I was stunned when i realised that TS hasn't have named arguments, i was sure that was the one of the first things it should add to JS...
what is this font
I like the pattern however, I think about if this creates an object just to throw it away by destructuring in the function call
So?
In this specific case i sometimes opt to cover this function with two named clearly they send email or not and remove this version if it possible
Is that neovim ? If yes, how to get that font ?
does React follows that pattern
If you just want to find all usages of the functions that declare the "sendEmailToWaitingList" arg, why not find all usages and filter via regex for more than 2 commas between closing brackets?
So from my persepective, i would only do this, if changes need to also occur to the params given, since objects are naturally passed as refs (imagine the quantity being updated of that object)
Is it possible do declared the arguments and i'ts types only once, with some TS magic?
so you telling us about types in typescript? undexpected
can be enforced easily with linter and formatter.
Man, I do this all the time in our front end react app. Our api is C# and one of the things that greatly annoys me about C# is how difficult this pattern is in to implement. A list of method/class args is so annoying.
there are tools where you can map backend types to front end types and never have to declare types in your frontend by hand. It could be done with Graphql. But can also use w/e to generate classes and types from a C# source file
@proesus7446 I mean when I swap to working in the api. C# annoys me when it comes to method arguments not being as comfortable as TS/JS arguments.
Great vid!
Side effects, events, notifications, logging etc should be handled in the class, not in the function.
This is a nice pattern, to bad it's hard to implement in other languages that are not as dynamic. Java comes to mind.
Also known as DTOs
I really love watching your videos. I'm new to web development world and I have being learning javascript and now focusing on typescript. I really love how to break things down. some of them a little complex but I always get the context and hopefully with better fluency will come better understanding of the codes in general. Thanks plenty
That's really convenient thing. React components also use a similar approach for passing props within a single object. By the way, who are your top three authors on software engineering?
By chance what source is you using in the editor?
bad advice - how to make your code slower for really rare case
Oh, interesting, that's a take I haven't heard regarding this patterns. How do you figure it's slow? Just because of the object creation?
@@andrew-burgess the only thing I can think of is because the engine cannot memoise the arguments since the object reference might be different every time it's called. But I'm no expert and would also like to know.
Care to explain?
@@Guergeiro Why would you want to memoize function arguments?
If you care about micro-performance you shouldn't be using JavaScript at first place.
ctrl+shift+f... as simple as that.
Lol its looks so pathetic from you to say that you dont get what that argument is so you offer to make arguments as object and descruture it😂im 100% sure you learned vim only to say that you are using vim btw. Truly internet trendy dev without actual knowledges