A builder to create a simple object? No! Just use named arguments instead of positioned arguments. If your language does not support them, then maybe you need a builder. Creating a builder takes time!
for anyone worried that using const variables will be worse for performance since you need to create variables for values now. First of all most compilers will substitute const variables for a set value anyways and is used exactly as if you just typed the number instead. Its a win win. Secondly, you should not do premature optimization anyways since it will make your code way harder to read, maintain, refactor and change later down. Only optimize when needed, prioritize more readable code before that.
Good point. Also should mention that in most cases, people won’t be needing to min max performance like this in modern applications anyways. There are many situations where readability is more important.
Context is important for that general rule. It's clearly true for your standard business and web applications, but if you are on a microcontroller or in your main game loop, you have to think about what you're doing to the compiled code. Constants are no problem anywhere of course.
For the last example, I would use keyword arguments (python) instead. Like createUser(name="Alice", age=30, ...). Less bloated and just as clear, and on top of that the order of the arguments doesn't matter.
1. Write self-documenting code 2. Avoid magic numbers (assign variables to numbers for a dynamic approach) 3. Avoid too many function parameters (if confusing)
Excellent advice. I disagree with the people who say that “self documenting code is a fallacy“. Code (small bits of functionality) can definitely be made self documenting - meaning that you should be able to understand what the function is doing by writing the code in ways that are clearer and more descriptive. However, this is not the same thing as saying “comments are bad.“ It’s not that the comments are bad, but rather that since comments are nonfunctional, it can be easy for them to fall out of step with the actual code. Comments are helpful, but they also have a built-in maintenance cost. There is definitely a place for both. If the function is complex, and cannot be easily subdivided into multiple smaller functions that are easier to understand, then yes, definitely provide well written comments for future developers. “Self documenting projects” are definitely a fallacy. README and CONTRIBUTING are critical documents for every repository.
Couldn’t have said it better myself. I finally found the time to respond in that thread where the person said the thing about it being a “fallacy” but not sure if it will do any good
I instituted a rule in my team that every directory's first check-in has to be a README file. Some of them were pretty stupid, but most of them came out pretty well, and I got kudos from a couple of co-workers for slowing them down and making them think for five seconds what they were doing before jumping into a new part of the project.
Not a fan of all that mutation with the builder. I’d just create the structure literal as an argument and pass that. No need for the user variable or any setter functions.
3:23 Except python allows named parameters, allowing you to pass in parameters in any order, just by using it's name. I'm just saying, if you're writing in python, you can use named parameters instead. Oh, and you can also force parameters to be named or positional.
The last point is a non-issue if you use a proper IDE. For example, I mainly code in Java using IntelliJ and if I encounter a function requiring many parameters I can press Ctrl+P and I get a tooltip displaying which arguments to pass and where.
Missing doc strings... I get that you don't want too many comments, but I shouldn't have to read the code to know what a function's effects, return types, or exceptions are.
Depends. On library always document functions. For applications don't because the cost is high to maintain such comments and they very often get out of date. Any modern IDEs can show the input/output automatically. And yes it's less detailed than comments but again for apps it's not worth and get out of date often.
@@chillie_dude I'm used to developing with plenty of people that don't have a clue why a function exists, so documentation makes it where I'm answering less questions.
@@erichlf well it's an interesting problem but I'm not convinced just commenting every function is the only solution. if a function is tricky then yeah great go ahead. Are the people not used to read code ? It's a skill to learn and an important one, you should ask them to try more. Is your code itself clear enough. Removing magical variables, better function name, explicit variable names, smaller (but not too small!) functions etc. Can help a lot too. So a mix of some comments, more reading and cleaner code might help. Just adding comments, which increase cost of maintainance and likelihood of being wrong, might not be the only solgion
@@chillie_dude i would say not updating comments is a problem. And should be addressed. Also docstrings shouldn't be so specific that they need to be updated, since they shouldn't explain the code. If you're changing a function in such a way that the doc string is changing you're probably doing something you shouldn't do.
These coding rules are great, and they could easily work across most languages. Ah, that builder pattern... It’s like a never-ending checklist: set...().set...().set...() - and just when you think you’re done, it’s like, “Surprise! .build()!” As if we haven’t been constructing it piece by piece already. Really, .build(), was that necessary?
It also makes refactoring harder because any new required thing you add, you have to find all the callers and make sure they're setting it. And the build() is only needed if you're going to return a different type than the builder, which is again unnecessary in OOP.
I would argue that the second example needs some readable function for the logic of the tax ratio - that way the two bits of logic are separate. There's the calculation of the tax percentage, and there's the calculation of the tax value itself: func calculateTax(price float64) float64 { float64 taxRatio = calculateTaxRatio(price); return price * taxRatio; } func calculateTaxRatio(price float64) float64 { if price > priceThreshold return highValueItemTax; return lowValueItemTax; }
Being literal, calculation of tax is what the greybeards call a Business Rule. The logic comes directly from real life, and should be unitary (not broken up.) Because we know that taxes go up periodically and vary from place to place, also take care that extrinsic business rules are somehow externalized, such as by dependency injection or service discovery.
With respect to parameters in a function, using an intelligent IDE (Eclipse, IntelliJ), if the function/subroutine/method is commented to describe the parameters, when writing the code to call the function, the IDE provides prompts for each argument: name/purpose, data type, and parameter position. (in Java, this is the power of JavaDoc-style comments) Very useful, to a point that programmer's expect to see that, and when they don't....who didn't document/comment correctly?!
OTOH, developers who have trouble naming things tend to not be better at writing clear comments either. In the cases where the variables are only for documentation of otherwise magic numbersin the code, it can also help to put the definition and value assignment near to the point there the variable is used so you don't have to scroll around when reading.
@@pepperdayjackpac4521 MONSTER_LANTERN_BURN_RAYCAST_LENGTH (constant) and concat_struct_of_structs_in_to_underline_separated_string (function) are some examples of my lengthy namings . And yes , this is from my code in commeriacl game :)
I would expect no better when writing comments, so what is your point? It is the whole point of code review, if the reviewer has to be told what the magic number is, or they figure it out by reading the code, it means that it should be pointed out to be renamed. This is the failure in your team, you must look out for the next developer and not rely on current knowledge.
I suppose if we were to be picky, we would need to say that function is called something even more descriptive - especially considering your updating example with diameter - like "circleAreaByRadius", literally locking in the description of the function and thoroughly documenting it. I guess my point with saying that is, there will always be a line drawn in how self-documenting the code actually needs to be.
Something I don't see addressed and I am really curious about is the UserBuilder allowing developers to write bugs. What if the email should not be null. I don't think there is a way to enforce the SetEmail method being called, which could cause weird bugs down the line. A constructor lets you explicitly set if something can be null, and if it shouldn't be null you'll get an error if you don't give a value. And for those saying python and javascript doesn't throw an error so it's not always true. There are ways to allow python and javascript to be typed, which will help you find bugs like this, so please use those
Hey thanks for the input. As to your concerns, you can simply add validation for required fields in the build() method. Same thing will have to be done with a constructor. There’s no avoiding validation in that case.
Another approach is to just ask for a struct with the required fields. The caller can instantiate the struct before calling, or on call, and (AFAIK) unless you instantiate a struct with all fields in order, then the compiler will have you specify field names. So it ends up being like a builder object, but built into the language and with no bloat shenanigans.
Great point! Yes, Go allows us to be explicit when creating struct literals so you are 100 percent correct. This tip is more geared towards non Go devs. For example in Java you’d need a class constructor with a bunch of params same as the createUser method.
That’s kind of missing the point imo. I’m not talking about a skill issue of not understanding code here. I’m talking about when experienced devs read code, what code is more annoying to read for example 😂 You can write code such that the person reading it experiences buttery smooth nirvana 🧈
Nice video, and I know this isn’t at all relevant to the coding points you were making..,, ,,,but that’s not how tax thresholds normally work. You would you the lower tax until the threshold is met and then the higher tax rate on what is left. Otherwise you could increase the price of an item by 1 cent and end up paying massively more in tax suddenly .
Many times, a fn with lots of params is built in and not worth making a wrapper just to key the values with an object. In that case, I will give each passed in value a descriptive variable. Even comments are fine there.
There is no need for builder in the last case. Use struct literals, it's better, or if your language support named arguments, just use that. A better approach than these two is to use different types (might be overkill though).
The last example could be solved with having multiple objects that represent the type of each and passing those in, no need for a builder. The added value of this, when a new requirement to add more values, you can add to an existing object or create a new one, depending on how that value is related to existing ones. For example, take the address value, what happens when you get a new requirement that states that the house number should be separate from the through way name? If you have an object represent the value, then you can just split them and add a method to return the full address.
@@Betadesk I didn’t say to make classes, structs are smaller and many languages can add methods to them and, if you’re using a language that doesn’t, you can use a function that you pass the struct into. It would add a type to a primitive and also add context.
These are very good points and it is a good alternative but it depends on the context in my opinion. Like @Betadesk mentioned, creating 5 structs to wrap primitive types might be cumbersome. It really depends on if that type of design would benefit the overall architecture which can’t really be determined in the small example I provided.
@@gearboxworks If you don't understand the power of types, then you need to research how newer languages are using them. This is a way to avoid hidden bugs from getting into your system. I agree, in a perfect world, we would not need all of this, but I have seen multiple times where null pointers cause an error due to incorrect values are passed due to not utilizing types. One thing to keep in mind is not all languages have powerful typing systems, and anything that can help that is good.
@@jfftck Using wrapper types is certainly very useful and robust, but wouldn't a builder object be a good balance between robustness and convenience in more trivial cases?
Hmm, no. By create user I mean actually persist the user. You’ll have to use your imagination for the logic in the function since I didn’t want to convolute the example with implementation details. An easy way to imagine it is that the user object in our code is different from what’s in the db. So maybe in the logic some ORM is used
Ah I get it now. So this createUser func is an interface of SQL insert to a database. I thought it's a constructor for user struct in memory, so that's looks like inception to me. The builder itself is the one that will replace user "constructor" (Need to create it manually in go, because go don't have classes) of having too much parameter. Thanks for sharing
Django ORM has both "create" and "save", and they almost do the same thing 💀 It may be better to name it insert() (maybe with a bool "update" parameter that turns it into an upsert), so the intent is clearer.
Issue with your Magic Numbers example. Although I agree with avoiding using magic numbers and even placing the data together in a structure, the problem with your approach is that it created a side effect. My suggestion would be to send in this data to the function or to create the data structure inside the function.
first rule is so important. Im almost done my cs degree and ive worked as a grader for many computer classes. so many students hear they need to write comments, and write the most useless redundant comments ever. like legit they will write i = 0; // initializing the variable i to 0
Yeah, I get a lot of people advocating for comments in the comments sections of many of my videos. Many of them do indeed seem like students or juniors. I’m not opposed to ALL comments but as you mentioned, many people just write useless redundant comments. I’m just trying to explain that there’s a time and a place. And usually you can avoid them.
I used to agree with the last one, but have gone back to separate arguments. Developers tend to be lazy so they would re-use existing generic classes and just keep adding properties they needed instead of passing an object with only the parameters needed for the method call. It also prevents overloading resulting in a method full of if statements. Lastly, in a well-architected n-tier application a layer should decouple its dependencies which results in the need for mapping objects between layers.
Hmm, some good points here but in an n-tier architecture it’s imo much easier to map objects between layers as opposed to trying to aggregate params between layers. You can also have common classes between layers depending on the overall design. Either way there’s going to be some decisions that need to be made based on the business requirements. I’d also argue that this type of architecture shouldn’t be referred to as inherently “well-architectured.” I’ve seen architectures like hex arch/ports and adapters as well as onion bloat simple applications with infrastructure that added no benefit. Just made the project more difficult to maintain and made it harder for devs to onboard. Anyways, I love your comment. Thank you
Well, you should be using a docstring of some sort to document your code (javadoc, python docstrings, doxygen). Code reviews should pick up unaltered docstrings. Personally I don't want to read a function to find out what it does, that is what the docstring is for. I want to read it to find out how it does what it does. Too many parameters? As other have said, if your language has named arguments, use those. If not, and it has dicts/hashes, use them instead, although I suppose that is a sort of a builder. But even when using named arguments, a style-guide enforcer could complain of too many arguments.
Creating a new object for User is good, but I calling that using Builder is just ugly. You can miss some variables to be updated when we alter the object even if we have default value (in most cases).
That last example didn't make any sense to me. If the original createUser function was used to create a user, and you instead put a builder in its place, whats the point of passing in an already created user into a createUser function?
Floating point cannot represent money accurately because you cannot represent 1/10 as a finite sum of powers of 2. Use an accurate representations like an integer containing the smallest unit (e.g. cents, pence) or a fixed point decimal type. Uncontrolled round-off errors are almighty difficult to diagnose and even harder to fix.
Yeah you’re right but It is not a real application. Don’t have time to explain that 10000 cents is 100 dollars and doing so would convolute the actual point I’m trying to explain.
While I'm a big fan of the builder pattern, I think that the example in your video is bad. The builder pattern shines when one's dealing with creating an object that has many optional parameters, but the User structure most likely requires majority of its properties to create a valid object. So, you're moving compile time checks to run time checks. You could say that you have to check if an email or a phone number is in valid format either way - ok, but then I still think it's not a responsibility of the createUser function. A dedicated Email value object that encapsulates the validation logic in a something like a factory function would be a better fit.
I disagree. I think that the builder pattern shines when creating complex objects with many fields, even if there aren’t so many optional fields. Although, the benefits of using the pattern do increase if there are optional fields, that doesn’t negate the benefits in the cases where there aren’t optional fields. Actually, your argument about a dedicated Email object that encapsulates validation is in support of the use of a builder in this case. Not against it. That’s because in many cases, when creating an object with lots of fields like the user object, there is a series of steps like assembling parts and performing validations. A builder allows us to handle these steps at different stages prior to calling build(). This is especially useful when we are creating immutable objects.
@@kantancoding the funny thing is... I actually searched up builder patterns after watching this video. The first that got recommended was yours. Without realizing it I watched and when I checked your videos right now I saw the same video 😂
You still have to create the instance of the object using the class constructor with the same amount of params. So you’d just move the problem to the constructor. Unless I’m mistaken (it’s been a while) JS/TS doesn’t support named params.
1st of all in the first question yes. Remember guys these are two different developers. So the validity of the code still true as long as the next developer is giving the val8d code.
I really dislike the common wisdom about "self-documenting code" as it is proven again and again by examples that are the simplest of simple code. I think it all starts somewhere different. And that's the distribution of logic into multiple methods when it does not need to be that way. When ever something gets so big that you might add in a comment, someone will say "just split it into descriptive methods", and yes, in the spirit of uncle bob you can split that 30 lines method into 10 simple methods. And instead of reading the code from lines 30 to line 60, the dev now has to scroll around trying to memorize 10 methods and what they do and build a mental model of how they are called. And after splitting one method into 10 methods, it's suddenly too much work to put a doc header of them, so we don't do it because we don't like tying shit after we just split that shit up for no reason. Three steps back. Lets not split up code if there's no reusing of the methods. Let's keep the code local, in context. Complex code exists, because complicated tasks exist. So you got complex code. Your coworker asks "what dos that do?" What do say? Do you go line by line saying things like "so this line checks if the first parameter is null" and so on? Of course not. Your coworker can read. Human language can is great to explain the concept of something. You will probably be able to formulate one sentence that can explain a complex block of code. Well, if you understand that code you should. And that's a good comment. Because three years later, someone will search for a bug and will find that block of code and ONE GOOD LINE may explain it better than anything else. "So what if the comments lie?" Comments are written with the assumption that code does what it is meant to do. Comments may "lie" when the code does not what it is meant to do, but in that case, the bug is the discrepancy between intended and actual behavior. You got two versions now, one telling you what it does, and one what it is supposed to do. You can now check if the assumptions where wrong from the beginning or if it's an implementation error and the fix is going to make the code do what the comment tells. That's my experience in working with complicated code.
Yeah, you make some good points but like with everything there is a balance to be found. When I say to write self documenting code.. it doesn't implicitly mean that you should NEVER write comments.
Comments should be used to tell why a certain code is there, especially for complicated logic, not what the code does. It does not hurt to have a private method that is not reused. It's pupose is to only split logic at this point.
If you can't write a simple name for what your code is doing, then it is more than likely hard to test, and that is the biggest code smell. I have been coding for over 15 years professionally, and have seen requirements change and comments still reflect the original intent, so in those cases, it is a lie on what is expected. I think it is more important to comment external APIs so you don't need to go back to the service provider's website, or you would be able to refer to older implementations when a provider breaks the contract that was given when you started using the service and would have legal grounds to ask for compensation for any losses due to service failures.
@@gearboxworks If you think comments are good, that is fine, but to say that comments are going to lead to better understanding of the code is being hopeful that the person who wrote it is able to express themselves in a completely understandable manner. My experience is that those same people write bad comments. Have you worked for a foreign company? You will quickly abandon comments and read the code to understand what is happening. It would be great if everyone could write comments that are perfect for every function, but I have experienced the exact opposite and have learned to distrust them.
Splitting up long and complex methods works best when you can create simple and functions that make sense on their own. Preferably these functions are pure/static, i.e. they don't change anything of importance for the calling method. Now, you don't have to scroll around anymore because the main method remains in control of the method's flow and of the object's state (if any).
I see your point but it doesn’t really solve the problem. For example if you have an Email type, createUser could take in fields primaryEmail and recoveryEmail both of type Email. They could still accidentally be swapped. With the builder you need to be explicit. setPrimaryEmail/setRecoveryEmail
@@kantancoding For such cases, yes, completely agree. Though even then I will still recommend domain types over primitives to be used with the builder. That way a PrimaryEmail or RecoveryEmail field will still hold a valid Email value, and not e.g. a street address.
Readable code, schmedable code. Make updated comments a requirement before code gets merged. Yes, comments can lie, but so can variable names, which objectively are just memory addresses with built-in comments. Their names are not for the computer, they are for you the developer. Make comment changes part of code review. You could create a function just like the one at the beginning, but if there is no circle that needs it's area calculated but some thing else that has a virtually identical formula, is the code really self-documenting? If we are okay with updating variable and function names, updating comments should be no different. Ontop of that, If I have a game that has 30 enemy types, and each attack pattern is created by a function, should I change each function name to "consecutiveNormalPunchesAttack" or "falconSmashAttack"? What if each has to conform to an interface? Should I make a private function that has the sole purpose of documenting what the attack is supposed to be like? Perhaps if there is some extra special logic, such as randomly chosen attacks that each have their own complex logic, but if I have a 20 simple enemies that basically have only simple but unique attack function, I think making extra and redundant functions is only going to make a file longer, bloated, and harder to follow. I'd rather have a clear comment that explains what the code is supposed to do, which is what the real purpose of comments is. It isn't about what the code does, it's why it's there.
Commenting is essential to code maintenance and yes they should be updated; however, comments should never describe what the code already does. Comments should include why the code was written or to encapsulate the code so the developer does not need to look at the code to use it. A comment like "x + y = z" is useless and redundant; however, a comment stating that we need to add such a such function to comply with a business or regulatory requirement warns the dev to not change the code unless that business or regulatory requirement is changed or there is an error in complying with that requirement as the code is currently written.
@@leerothman2715 The problem is when the requirements are not kept with the code then bugs creep in with each release. Devs are not going to go looking through all of the requirements docs every time they change the code nor will they look at the commit statements in git or any other code repository. In reality the only time anyone looks at those statements is after everyone's hair has been set on fire with a sev 1 or 2 bug.
@@alexaneals8194 Well that might be your reality, but it certainly isn’t mine. Writing & reading comments isn’t going to stop bugs, automated testing will do a much better job (especially if you practice TDD). Those tests will also let all developers know why the functionality is there. If comments are so important then why do IDE’s grey them out by default, they deliberately make them fade into the background for a reason. The application requirements are not just for developers they are for the whole team to discuss along with the stakeholders, how do you do that if is distributed all over many code files?
hate comments in general, only reasons I comment is about a strange edge case or to separate code blocks in sections given a larger function not sure I agree on the 3rd one, a proper lsp can help demostrate what each argument represents
@@МаксимГорюнов-м7и “language server protocol”, more specifically about something called “inlay hints” which shows the types and names of function arguments
@@МаксимГорюнов-м7и LSP is already standard in most code editors. When you try to call a function in VS code or nvim, it will show you a preview, or 'hint' of the arguments and types that the function takes
It depends on the language. In Go you can create a struct literal and be explicit when setting the fields but this video isn’t Go specific. In Java for example, to create an instance of the user you’d need to use the class constructor which would have the same problem with the params as the create user function.
I don't really agree that code needs to be self-documented. If you have to comment it in longhand English to reduce cognitive load then you should have just written it more clearly from the outset.
Imo the “higher tax” part is redundant. Especially when you look at the whole statement, it’s already clear that the threshold being exceeded results in higher tax.
"self documenting code" is a fallacy. Comments are not bad, they are necessary. Are they redundant with the code? They can be, but that's a good thing. Why? Because they provide a layer of confirmation, whereas code all by itself may be correct or may be wrong. Without the comments, there's no way to compare what a function was _intending_ to do (the comments), verses what it _actually_ does (the code). The problem in this video's example isn't that there were comments, its that the later developer was lazy and didn't update the comments, which is a bad developer.
Tests should provide your layer of confirmation. Not comments. Otherwise there’s so way of knowing which is wrong, the comment or the code. Also, I’m not arguing that all comments are bad/wrong. I’m arguing that self documenting code can eliminate the need for most comments which means that when you do actually need to write comments, they are actually useful as opposed to just being redundant noise. Also, I don’t think that the dev that didn’t update the comment is necessarily bad. Many factors go into why a dev might not update comments. I’ve seen comments written so poorly that I couldn’t even understand what they were trying to explain therefore updating them was impossible.
@@kantancoding What I was saying is that comments provide "a" layer of guidance and confirmation, not "the" confirmation. Yes certainly, there should be test cases. But in practical terms, the struggles around not knowing which is wrong, the code or the comments/tests is still valid in both paradigms, whether you're taking about comments or tests cases. Either way, when the bug report comes in, someone has to find the bug. When reading that code and wondering "hmm is this < supposed to be a
The struggle around not knowing which is wrong 「the comments or the code」 or 「the tests or the code」 is certainly not equally valid in both paradigms. The most obvious reason being that comments are far easier to overlook than tests. In most cases, if tests are wrong, your ci/cd pipeline breaks. In the rare cases that a unit test is written in such a way that it’s wrong but still able to pass, it’s still part of a test suite and some integration test will likely break even if that unit test is somehow passing. So the odds that multiple tests that depend on some core business logic are all able to somehow pass even if the tests are wrong are far lower than the odds that some random fragile comment is neglected or simply incorrect. There are really no checks and balances in place for comments other than the reviewers eyes.. and your whole argument is that good devs don’t miss these types of things but that, my friend, is the REAL “fallacy”
Commenting and documenting are two separate things. Documenting modules, classes, methods, functions is absolutely a good way to provide information of what a module, a class, a method or a function is supposed to do. Most modern languages offer tools to write proper API documentation right in the code. Commenting lines is seldom helpful IMHO. I use to line comments only to explain unconventional bits of code that can't be cleaned up for some good reason. The reason goes into the comment. Call me a bad developer, but the only way I'm going to "update" a redundant line comment is to remove it altogether.
The slogan "Write self documenting code" stands in contrast to the fact that for some reason, most Go programmers (including the language's creators) write code in such a way that most variables are meaningless, unreadable 1-3 letter "words"; functions are also exaggeratedly called abbreviations as if Go programmers were deducted from their paycheck for every letter in the code. Therefore, it should be made clear - idiomatic Go is often a contradiction of "self documenting code."
Self-documenting code is impossible. Code can only tell you what it is doing not why it was written. Most cases that I have run into where a dev has really screwed something up is not because they could not understand what the code was doing, but rather they didn't realize why the code written that way. A function returns null and the dev assumes that the function has a bug and changes the function to not return null and crashes a completely unrelated system. An address space is not initialized and a dev assumes that it's a bug and makes sure to initialize the space only to compromise the encryption algorithm (this occurred in a linux router software update that created an exploit).
The last problem is easily solved by typing. Instead of practicing primitive obsession and using raw strings, wrap them. This way it'll be impossible to accidentally swap the arguments and have the code still compiling after that.
Having different types would definitely help in the case shown in the video and most other cases, but there are situations where a function does actually accept multiple values of the same semantic type, such as multiple names, or multiple ages (such as to calculate who is older), etc. In those situations you usually only have two values of the same type though so it's not too bad, and if your language supports named arguments then that would be the best solution in those specific cases.
Creating a user struct to create a user 😂 Brilliant. Almost all modern languages support named parameters, this approach would be way clearer than the one explained. The other two pieces of advice are good ones though.
@@kantancoding I understood what you were doing there, for me it's not worth to create that User class/struct. If you are working with an ORM, maybe you can use a mapped entity of the Users table.
@@gearboxworks I would not create an object with a builder anyway. I'd rather create a lightweight structure/class just for the sake of passing around those params. And I would only create it if the context you described applies.
I agree he should have mentioned named parameters, but we're using Golang as an example which doesn't have them, almost every startup nowadays uses a Go backend so thats perfectly reasonable
Whether the language supports named arguments or not is irrelevant. It will always be cleaner and easier to see what the values are by a params struct. Otherwise, you'd have to check in the function itself to figure out what the arguments are. It still doesn't solve the problem of accidentally switching arguments around either. Regardless, if you find your function is taking in enough arguments to push it to the next line... you might want to reevaluate what it is you’re even doing...
Click here to Land Your Dream Job! Become a Microservice Expert With This Hands-On Golang Course 👉 kantancoding.io
A builder to create a simple object? No! Just use named arguments instead of positioned arguments. If your language does not support them, then maybe you need a builder. Creating a builder takes time!
Creating procedures also takes time. Let's write everything in main
Named arguments are the best! Seriously, why write more code if you can write less code?
Not all languages have named arguments though!
@@trwn87 Screw those languages that don't have named arguments! (jk)
@@TeverRus To be honest, kind of true... Except there is a good alternative, of course!
for anyone worried that using const variables will be worse for performance since you need to create variables for values now. First of all most compilers will substitute const variables for a set value anyways and is used exactly as if you just typed the number instead. Its a win win. Secondly, you should not do premature optimization anyways since it will make your code way harder to read, maintain, refactor and change later down. Only optimize when needed, prioritize more readable code before that.
Good point. Also should mention that in most cases, people won’t be needing to min max performance like this in modern applications anyways. There are many situations where readability is more important.
Context is important for that general rule. It's clearly true for your standard business and web applications, but if you are on a microcontroller or in your main game loop, you have to think about what you're doing to the compiled code. Constants are no problem anywhere of course.
For the last example, I would use keyword arguments (python) instead. Like createUser(name="Alice", age=30, ...). Less bloated and just as clear, and on top of that the order of the arguments doesn't matter.
That's exactly what I was thinking!
Yeah, would be great if all languages had that 💯
1. Write self-documenting code
2. Avoid magic numbers (assign variables to numbers for a dynamic approach)
3. Avoid too many function parameters (if confusing)
Thanks man, sometimes my codebases get very chaotic, these really help
Yeah man, me too! Thanks for watching 😊
Excellent advice.
I disagree with the people who say that “self documenting code is a fallacy“. Code (small bits of functionality) can definitely be made self documenting - meaning that you should be able to understand what the function is doing by writing the code in ways that are clearer and more descriptive. However, this is not the same thing as saying “comments are bad.“ It’s not that the comments are bad, but rather that since comments are nonfunctional, it can be easy for them to fall out of step with the actual code. Comments are helpful, but they also have a built-in maintenance cost. There is definitely a place for both. If the function is complex, and cannot be easily subdivided into multiple smaller functions that are easier to understand, then yes, definitely provide well written comments for future developers.
“Self documenting projects” are definitely a fallacy. README and CONTRIBUTING are critical documents for every repository.
Couldn’t have said it better myself. I finally found the time to respond in that thread where the person said the thing about it being a “fallacy” but not sure if it will do any good
I instituted a rule in my team that every directory's first check-in has to be a README file. Some of them were pretty stupid, but most of them came out pretty well, and I got kudos from a couple of co-workers for slowing them down and making them think for five seconds what they were doing before jumping into a new part of the project.
Not a fan of all that mutation with the builder. I’d just create the structure literal as an argument and pass that. No need for the user variable or any setter functions.
Good point but it depends on the langauge. In many languages you'll run into the same issue with lots of constructor params.
In Typescript, I'd use a or a type that enforced mandatory properties and made anything with a sensible default optional.
I was looking for this exact comment. This is what I do all the time, its super nice
Use "Keyword arguments" or "destructuring" for the last one.
If you use something like TS make the parameter a type interface.
3:23 Except python allows named parameters, allowing you to pass in parameters in any order, just by using it's name. I'm just saying, if you're writing in python, you can use named parameters instead. Oh, and you can also force parameters to be named or positional.
Yeah! That’s a good observation. Python is great 😊
The last point is a non-issue if you use a proper IDE. For example, I mainly code in Java using IntelliJ and if I encounter a function requiring many parameters I can press Ctrl+P and I get a tooltip displaying which arguments to pass and where.
Best practice not to assume the next person is using the same or similar tool.
@@NickCombs In no sentence have I said that I do it or that you should just not care about it. I just said how I'd deal with it.
If it's working, more power to you. I'm just chiming in in case someone else is thinking about leaning on the IDE in a more typical situation.
@@NickCombs The likelihood you're writing a function like "createUser" and not using an IDE is vanishingly small.
@@darrennew8211 No it's not. Text editors, CLIs, remote servers, and deployed apps exist.
Missing doc strings... I get that you don't want too many comments, but I shouldn't have to read the code to know what a function's effects, return types, or exceptions are.
Depends. On library always document functions.
For applications don't because the cost is high to maintain such comments and they very often get out of date. Any modern IDEs can show the input/output automatically. And yes it's less detailed than comments but again for apps it's not worth and get out of date often.
@@chillie_dude I'm used to developing with plenty of people that don't have a clue why a function exists, so documentation makes it where I'm answering less questions.
"All my functions are less than 10 lines bro, just read them bro" 💀
@@erichlf well it's an interesting problem but I'm not convinced just commenting every function is the only solution. if a function is tricky then yeah great go ahead.
Are the people not used to read code ? It's a skill to learn and an important one, you should ask them to try more.
Is your code itself clear enough. Removing magical variables, better function name, explicit variable names, smaller (but not too small!) functions etc. Can help a lot too.
So a mix of some comments, more reading and cleaner code might help. Just adding comments, which increase cost of maintainance and likelihood of being wrong, might not be the only solgion
@@chillie_dude i would say not updating comments is a problem. And should be addressed. Also docstrings shouldn't be so specific that they need to be updated, since they shouldn't explain the code. If you're changing a function in such a way that the doc string is changing you're probably doing something you shouldn't do.
but how do you use the classBuilder without a classBuilderBuilder??
That's the neat part, you don't
These coding rules are great, and they could easily work across most languages.
Ah, that builder pattern... It’s like a never-ending checklist: set...().set...().set...() - and just when you think you’re done, it’s like, “Surprise! .build()!” As if we haven’t been constructing it piece by piece already. Really, .build(), was that necessary?
It also makes refactoring harder because any new required thing you add, you have to find all the callers and make sure they're setting it. And the build() is only needed if you're going to return a different type than the builder, which is again unnecessary in OOP.
Love the tips. Thank you.
On the last example, how would you call the create user function?
I would argue that the second example needs some readable function for the logic of the tax ratio - that way the two bits of logic are separate. There's the calculation of the tax percentage, and there's the calculation of the tax value itself:
func calculateTax(price float64) float64 {
float64 taxRatio = calculateTaxRatio(price);
return price * taxRatio;
}
func calculateTaxRatio(price float64) float64 {
if price > priceThreshold
return highValueItemTax;
return lowValueItemTax;
}
Being literal, calculation of tax is what the greybeards call a Business Rule. The logic comes directly from real life, and should be unitary (not broken up.) Because we know that taxes go up periodically and vary from place to place, also take care that extrinsic business rules are somehow externalized, such as by dependency injection or service discovery.
With respect to parameters in a function, using an intelligent IDE (Eclipse, IntelliJ), if the function/subroutine/method is commented to describe the parameters, when writing the code to call the function, the IDE provides prompts for each argument: name/purpose, data type, and parameter position.
(in Java, this is the power of JavaDoc-style comments)
Very useful, to a point that programmer's expect to see that, and when they don't....who didn't document/comment correctly?!
When I read the legacy code , developers always give bad long name to the magic numbers, making the code more difficult to read.
🫠 yeah naming can be difficult!
OTOH, developers who have trouble naming things tend to not be better at writing clear comments either.
In the cases where the variables are only for documentation of otherwise magic numbersin the code, it can also help to put the definition and value assignment near to the point there the variable is used so you don't have to scroll around when reading.
can u give an example of a bad long name of a magic number?
@@pepperdayjackpac4521
MONSTER_LANTERN_BURN_RAYCAST_LENGTH (constant) and concat_struct_of_structs_in_to_underline_separated_string (function) are some examples of my lengthy namings .
And yes , this is from my code in commeriacl game :)
I would expect no better when writing comments, so what is your point? It is the whole point of code review, if the reviewer has to be told what the magic number is, or they figure it out by reading the code, it means that it should be pointed out to be renamed. This is the failure in your team, you must look out for the next developer and not rely on current knowledge.
I suppose if we were to be picky, we would need to say that function is called something even more descriptive - especially considering your updating example with diameter - like "circleAreaByRadius", literally locking in the description of the function and thoroughly documenting it. I guess my point with saying that is, there will always be a line drawn in how self-documenting the code actually needs to be.
Something I don't see addressed and I am really curious about is the UserBuilder allowing developers to write bugs. What if the email should not be null. I don't think there is a way to enforce the SetEmail method being called, which could cause weird bugs down the line. A constructor lets you explicitly set if something can be null, and if it shouldn't be null you'll get an error if you don't give a value.
And for those saying python and javascript doesn't throw an error so it's not always true. There are ways to allow python and javascript to be typed, which will help you find bugs like this, so please use those
Hey thanks for the input. As to your concerns, you can simply add validation for required fields in the build() method. Same thing will have to be done with a constructor. There’s no avoiding validation in that case.
Useful advice. the awkward silence at the end though.
Another approach is to just ask for a struct with the required fields. The caller can instantiate the struct before calling, or on call, and (AFAIK) unless you instantiate a struct with all fields in order, then the compiler will have you specify field names. So it ends up being like a builder object, but built into the language and with no bloat shenanigans.
Great point! Yes, Go allows us to be explicit when creating struct literals so you are 100 percent correct.
This tip is more geared towards non Go devs. For example in Java you’d need a class constructor with a bunch of params same as the createUser method.
@@kantancoding What the Java! lol
jokes aside, i'm of the opinion that the java person should learn the go way, not the other way around. 🤣
I’m of a similar opinion bro. The Java person should quit their job and learn Go.
Just come across your channel. I love the visual effects!
Understanding coding can be daunting in the beginning but becomes little understandable later.
That’s kind of missing the point imo. I’m not talking about a skill issue of not understanding code here. I’m talking about when experienced devs read code, what code is more annoying to read for example 😂
You can write code such that the person reading it experiences buttery smooth nirvana 🧈
Nice video, and I know this isn’t at all relevant to the coding points you were making..,,
,,,but that’s not how tax thresholds normally work.
You would you the lower tax until the threshold is met and then the higher tax rate on what is left.
Otherwise you could increase the price of an item by 1 cent and end up paying massively more in tax suddenly .
Many times, a fn with lots of params is built in and not worth making a wrapper just to key the values with an object. In that case, I will give each passed in value a descriptive variable. Even comments are fine there.
Good point. Sometimes it can’t be helped.
There is no need for builder in the last case. Use struct literals, it's better, or if your language support named arguments, just use that. A better approach than these two is to use different types (might be overkill though).
The last example could be solved with having multiple objects that represent the type of each and passing those in, no need for a builder. The added value of this, when a new requirement to add more values, you can add to an existing object or create a new one, depending on how that value is related to existing ones. For example, take the address value, what happens when you get a new requirement that states that the house number should be separate from the through way name? If you have an object represent the value, then you can just split them and add a method to return the full address.
So create 5 classes just to wrap primitive values instead of 1 builder class? That seems like a lot of overhead
@@Betadesk I didn’t say to make classes, structs are smaller and many languages can add methods to them and, if you’re using a language that doesn’t, you can use a function that you pass the struct into. It would add a type to a primitive and also add context.
These are very good points and it is a good alternative but it depends on the context in my opinion.
Like @Betadesk mentioned, creating 5 structs to wrap primitive types might be cumbersome. It really depends on if that type of design would benefit the overall architecture which can’t really be determined in the small example I provided.
@@gearboxworks If you don't understand the power of types, then you need to research how newer languages are using them. This is a way to avoid hidden bugs from getting into your system. I agree, in a perfect world, we would not need all of this, but I have seen multiple times where null pointers cause an error due to incorrect values are passed due to not utilizing types. One thing to keep in mind is not all languages have powerful typing systems, and anything that can help that is good.
@@jfftck Using wrapper types is certainly very useful and robust, but wouldn't a builder object be a good balance between robustness and convenience in more trivial cases?
so function createUser will create a user with a user that is already created?
Hmm, no. By create user I mean actually persist the user. You’ll have to use your imagination for the logic in the function since I didn’t want to convolute the example with implementation details.
An easy way to imagine it is that the user object in our code is different from what’s in the db. So maybe in the logic some ORM is used
Ah I get it now. So this createUser func is an interface of SQL insert to a database. I thought it's a constructor for user struct in memory, so that's looks like inception to me.
The builder itself is the one that will replace user "constructor" (Need to create it manually in go, because go don't have classes) of having too much parameter. Thanks for sharing
@@kevintanudjaja7597 yes you've got it! And thanks for always supporting :)
Django ORM has both "create" and "save", and they almost do the same thing 💀
It may be better to name it insert() (maybe with a bool "update" parameter that turns it into an upsert), so the intent is clearer.
Issue with your Magic Numbers example. Although I agree with avoiding using magic numbers and even placing the data together in a structure, the problem with your approach is that it created a side effect. My suggestion would be to send in this data to the function or to create the data structure inside the function.
Hey thanks for the input! There is no side effect. They are constants.
first rule is so important. Im almost done my cs degree and ive worked as a grader for many computer classes. so many students hear they need to write comments, and write the most useless redundant comments ever. like legit they will write i = 0; // initializing the variable i to 0
Yeah, I get a lot of people advocating for comments in the comments sections of many of my videos. Many of them do indeed seem like students or juniors.
I’m not opposed to ALL comments but as you mentioned, many people just write useless redundant comments.
I’m just trying to explain that there’s a time and a place. And usually you can avoid them.
I used to agree with the last one, but have gone back to separate arguments. Developers tend to be lazy so they would re-use existing generic classes and just keep adding properties they needed instead of passing an object with only the parameters needed for the method call. It also prevents overloading resulting in a method full of if statements. Lastly, in a well-architected n-tier application a layer should decouple its dependencies which results in the need for mapping objects between layers.
Hmm, some good points here but in an n-tier architecture it’s imo much easier to map objects between layers as opposed to trying to aggregate params between layers. You can also have common classes between layers depending on the overall design. Either way there’s going to be some decisions that need to be made based on the business requirements.
I’d also argue that this type of architecture shouldn’t be referred to as inherently “well-architectured.” I’ve seen architectures like hex arch/ports and adapters as well as onion bloat simple applications with infrastructure that added no benefit. Just made the project more difficult to maintain and made it harder for devs to onboard.
Anyways, I love your comment. Thank you
3rd one recommendation use typescript interface
Well, you should be using a docstring of some sort to document your code (javadoc, python docstrings, doxygen). Code reviews should pick up unaltered docstrings. Personally I don't want to read a function to find out what it does, that is what the docstring is for. I want to read it to find out how it does what it does.
Too many parameters? As other have said, if your language has named arguments, use those. If not, and it has dicts/hashes, use them instead, although I suppose that is a sort of a builder. But even when using named arguments, a style-guide enforcer could complain of too many arguments.
Creating a new object for User is good, but I calling that using Builder is just ugly. You can miss some variables to be updated when we alter the object even if we have default value (in most cases).
I see where you’re coming from but depends on the builder. You can add some validation for required fields.
A builder likely comes at a runtime cost. Use with care.
These are GREAT!!! keep em coming!!
Thanks for the feedback! Will do!
That last example didn't make any sense to me. If the original createUser function was used to create a user, and you instead put a builder in its place, whats the point of passing in an already created user into a createUser function?
Yeah, lots of people confused about this. The createUser function creates the user in the database
Floating point cannot represent money accurately because you cannot represent 1/10 as a finite sum of powers of 2. Use an accurate representations like an integer containing the smallest unit (e.g. cents, pence) or a fixed point decimal type. Uncontrolled round-off errors are almighty difficult to diagnose and even harder to fix.
Yeah you’re right but It is not a real application. Don’t have time to explain that 10000 cents is 100 dollars and doing so would convolute the actual point I’m trying to explain.
While I'm a big fan of the builder pattern, I think that the example in your video is bad. The builder pattern shines when one's dealing with creating an object that has many optional parameters, but the User structure most likely requires majority of its properties to create a valid object. So, you're moving compile time checks to run time checks. You could say that you have to check if an email or a phone number is in valid format either way - ok, but then I still think it's not a responsibility of the createUser function. A dedicated Email value object that encapsulates the validation logic in a something like a factory function would be a better fit.
I disagree. I think that the builder pattern shines when creating complex objects with many fields, even if there aren’t so many optional fields. Although, the benefits of using the pattern do increase if there are optional fields, that doesn’t negate the benefits in the cases where there aren’t optional fields.
Actually, your argument about a dedicated Email object that encapsulates validation is in support of the use of a builder in this case. Not against it.
That’s because in many cases, when creating an object with lots of fields like the user object, there is a series of steps like assembling parts and performing validations. A builder allows us to handle these steps at different stages prior to calling build().
This is especially useful when we are creating immutable objects.
Thje formula used in the first example actually is pi * r * r and not pi * r^2. While the result is the same, those are two different formulas.
@@Linuxdirk 😂 what?
could you make a tutorial on how to make a builder? you've got me curious on how to make those
I already have one somewhere :) I think you can just search in my videos for builder design pattern
@@kantancoding Alright! I shall have a look.
@@kantancoding the funny thing is... I actually searched up builder patterns after watching this video. The first that got recommended was yours. Without realizing it I watched and when I checked your videos right now I saw the same video 😂
I've finally found someone else using Agave font face :)
Great advice, thanks 😃
Glad it was helpful!
Last point is useles eg for js/ts. Just pass an object.
You still have to create the instance of the object using the class constructor with the same amount of params. So you’d just move the problem to the constructor.
Unless I’m mistaken (it’s been a while) JS/TS doesn’t support named params.
Clicked on the video just because i knew the comments would be spicy lol
😂you’ve come to the right place
3rd rule -> You are clearly breaking 1 entity (method call) between two lines. Never seen code style like this before:
a().
b().
c()
You'll return the object at Every call lol
Not really sure what the question is here 😂
It's called fluent interface, or method chaining
@@kantancoding he's like how are you calling a method to void but you're surely not
1st of all in the first question yes. Remember guys these are two different developers. So the validity of the code still true as long as the next developer is giving the val8d code.
😂 bro
I am not ensure constructing a User struct to....construct a user object is great coding practice.
🤔
Spot on!
I really dislike the common wisdom about "self-documenting code" as it is proven again and again by examples that are the simplest of simple code.
I think it all starts somewhere different. And that's the distribution of logic into multiple methods when it does not need to be that way.
When ever something gets so big that you might add in a comment, someone will say "just split it into descriptive methods", and yes, in the spirit of uncle bob you can split that 30 lines method into 10 simple methods. And instead of reading the code from lines 30 to line 60, the dev now has to scroll around trying to memorize 10 methods and what they do and build a mental model of how they are called. And after splitting one method into 10 methods, it's suddenly too much work to put a doc header of them, so we don't do it because we don't like tying shit after we just split that shit up for no reason.
Three steps back. Lets not split up code if there's no reusing of the methods. Let's keep the code local, in context.
Complex code exists, because complicated tasks exist.
So you got complex code. Your coworker asks "what dos that do?"
What do say? Do you go line by line saying things like "so this line checks if the first parameter is null" and so on? Of course not. Your coworker can read. Human language can is great to explain the concept of something. You will probably be able to formulate one sentence that can explain a complex block of code. Well, if you understand that code you should.
And that's a good comment. Because three years later, someone will search for a bug and will find that block of code and ONE GOOD LINE may explain it better than anything else.
"So what if the comments lie?"
Comments are written with the assumption that code does what it is meant to do. Comments may "lie" when the code does not what it is meant to do, but in that case, the bug is the discrepancy between intended and actual behavior. You got two versions now, one telling you what it does, and one what it is supposed to do. You can now check if the assumptions where wrong from the beginning or if it's an implementation error and the fix is going to make the code do what the comment tells.
That's my experience in working with complicated code.
Yeah, you make some good points but like with everything there is a balance to be found. When I say to write self documenting code.. it doesn't implicitly mean that you should NEVER write comments.
Comments should be used to tell why a certain code is there, especially for complicated logic, not what the code does.
It does not hurt to have a private method that is not reused. It's pupose is to only split logic at this point.
If you can't write a simple name for what your code is doing, then it is more than likely hard to test, and that is the biggest code smell. I have been coding for over 15 years professionally, and have seen requirements change and comments still reflect the original intent, so in those cases, it is a lie on what is expected. I think it is more important to comment external APIs so you don't need to go back to the service provider's website, or you would be able to refer to older implementations when a provider breaks the contract that was given when you started using the service and would have legal grounds to ask for compensation for any losses due to service failures.
@@gearboxworks If you think comments are good, that is fine, but to say that comments are going to lead to better understanding of the code is being hopeful that the person who wrote it is able to express themselves in a completely understandable manner. My experience is that those same people write bad comments. Have you worked for a foreign company? You will quickly abandon comments and read the code to understand what is happening.
It would be great if everyone could write comments that are perfect for every function, but I have experienced the exact opposite and have learned to distrust them.
Splitting up long and complex methods works best when you can create simple and functions that make sense on their own. Preferably these functions are pure/static, i.e. they don't change anything of importance for the calling method. Now, you don't have to scroll around anymore because the main method remains in control of the method's flow and of the object's state (if any).
Wait what? A method that builds a user, but its signature requires a user? The heck?
The method doesn’t build a user. It creates a user in the database.
The first rule is already thousands of light years away from the grasp of most developers. We are doomed.
Great advice Ty!
You bet!
Nowaday, just write your code then use gpt to enhance that
Better off asking a 6th grader…
Why would a function called createUser accept an instance of User?
The instance of user is in the application memory. when calling createUser the user is persisted to disk e.g. a database
Rather than a builder or even a struct to hold a set of values, instead create types for each domain concept to get rid of primitive-type-obsession.
I see your point but it doesn’t really solve the problem. For example if you have an Email type, createUser could take in fields primaryEmail and recoveryEmail both of type Email. They could still accidentally be swapped. With the builder you need to be explicit. setPrimaryEmail/setRecoveryEmail
@@kantancoding For such cases, yes, completely agree. Though even then I will still recommend domain types over primitives to be used with the builder. That way a PrimaryEmail or RecoveryEmail field will still hold a valid Email value, and not e.g. a street address.
Amazing 🎉
Thanks for watching!
What is the font used in the video?
Agave nerd font usually
as long as its english, code is readable to me😂 assembly is very readable
these are very good videos :)
Glad you like them! Thanks for watching!
Readable code, schmedable code. Make updated comments a requirement before code gets merged. Yes, comments can lie, but so can variable names, which objectively are just memory addresses with built-in comments. Their names are not for the computer, they are for you the developer. Make comment changes part of code review. You could create a function just like the one at the beginning, but if there is no circle that needs it's area calculated but some thing else that has a virtually identical formula, is the code really self-documenting? If we are okay with updating variable and function names, updating comments should be no different. Ontop of that, If I have a game that has 30 enemy types, and each attack pattern is created by a function, should I change each function name to "consecutiveNormalPunchesAttack" or "falconSmashAttack"? What if each has to conform to an interface? Should I make a private function that has the sole purpose of documenting what the attack is supposed to be like? Perhaps if there is some extra special logic, such as randomly chosen attacks that each have their own complex logic, but if I have a 20 simple enemies that basically have only simple but unique attack function, I think making extra and redundant functions is only going to make a file longer, bloated, and harder to follow. I'd rather have a clear comment that explains what the code is supposed to do, which is what the real purpose of comments is. It isn't about what the code does, it's why it's there.
Commenting is essential to code maintenance and yes they should be updated; however, comments should never describe what the code already does. Comments should include why the code was written or to encapsulate the code so the developer does not need to look at the code to use it. A comment like "x + y = z" is useless and redundant; however, a comment stating that we need to add such a such function to comply with a business or regulatory requirement warns the dev to not change the code unless that business or regulatory requirement is changed or there is an error in complying with that requirement as the code is currently written.
These posts are good advice.
@@alexaneals8194That just sounds like putting the requirements as comments in the code to me? We have better tools to capture this data.
@@leerothman2715 The problem is when the requirements are not kept with the code then bugs creep in with each release. Devs are not going to go looking through all of the requirements docs every time they change the code nor will they look at the commit statements in git or any other code repository. In reality the only time anyone looks at those statements is after everyone's hair has been set on fire with a sev 1 or 2 bug.
@@alexaneals8194 Well that might be your reality, but it certainly isn’t mine. Writing & reading comments isn’t going to stop bugs, automated testing will do a much better job (especially if you practice TDD). Those tests will also let all developers know why the functionality is there. If comments are so important then why do IDE’s grey them out by default, they deliberately make them fade into the background for a reason. The application requirements are not just for developers they are for the whole team to discuss along with the stakeholders, how do you do that if is distributed all over many code files?
Read code, not comments. That’s all
Which programming language is this? go?
yes
hate comments in general, only reasons I comment is about a strange edge case or to separate code blocks in sections given a larger function
not sure I agree on the 3rd one, a proper lsp can help demostrate what each argument represents
What do you mean by lsp?
@@МаксимГорюнов-м7иcode suggestions
@@МаксимГорюнов-м7и “language server protocol”, more specifically about something called “inlay hints” which shows the types and names of function arguments
@@cryptonative this is probably a level I haven't gotten to yet, thank you, I will look into it deeper
@@МаксимГорюнов-м7и LSP is already standard in most code editors. When you try to call a function in VS code or nvim, it will show you a preview, or 'hint' of the arguments and types that the function takes
I mean go isnt really design for the user business. Why not create a struct without the abstraction
It depends on the language. In Go you can create a struct literal and be explicit when setting the fields but this video isn’t Go specific.
In Java for example, to create an instance of the user you’d need to use the class constructor which would have the same problem with the params as the create user function.
I don't really agree that code needs to be self-documented. If you have to comment it in longhand English to reduce cognitive load then you should have just written it more clearly from the outset.
How do you edit your videos? I wisg mine looked so clean
Have you tried manim?
example of rule two is violating first rule, priceThreshold is not a good name. higherTaxPriceThreshold would be better.
Imo the “higher tax” part is redundant. Especially when you look at the whole statement, it’s already clear that the threshold being exceeded results in higher tax.
@kantancoding surely depends on what other constants you define in that file. If only this one I'm fine
"self documenting code" is a fallacy. Comments are not bad, they are necessary. Are they redundant with the code? They can be, but that's a good thing. Why? Because they provide a layer of confirmation, whereas code all by itself may be correct or may be wrong. Without the comments, there's no way to compare what a function was _intending_ to do (the comments), verses what it _actually_ does (the code). The problem in this video's example isn't that there were comments, its that the later developer was lazy and didn't update the comments, which is a bad developer.
Tests should provide your layer of confirmation. Not comments. Otherwise there’s so way of knowing which is wrong, the comment or the code.
Also, I’m not arguing that all comments are bad/wrong. I’m arguing that self documenting code can eliminate the need for most comments which means that when you do actually need to write comments, they are actually useful as opposed to just being redundant noise.
Also, I don’t think that the dev that didn’t update the comment is necessarily bad. Many factors go into why a dev might not update comments. I’ve seen comments written so poorly that I couldn’t even understand what they were trying to explain therefore updating them was impossible.
@@kantancoding What I was saying is that comments provide "a" layer of guidance and confirmation, not "the" confirmation. Yes certainly, there should be test cases. But in practical terms, the struggles around not knowing which is wrong, the code or the comments/tests is still valid in both paradigms, whether you're taking about comments or tests cases.
Either way, when the bug report comes in, someone has to find the bug. When reading that code and wondering "hmm is this < supposed to be a
The struggle around not knowing which is wrong 「the comments or the code」 or 「the tests or the code」 is certainly not equally valid in both paradigms.
The most obvious reason being that comments are far easier to overlook than tests. In most cases, if tests are wrong, your ci/cd pipeline breaks.
In the rare cases that a unit test is written in such a way that it’s wrong but still able to pass, it’s still part of a test suite and some integration test will likely break even if that unit test is somehow passing.
So the odds that multiple tests that depend on some core business logic are all able to somehow pass even if the tests are wrong are far lower than the odds that some random fragile comment is neglected or simply incorrect.
There are really no checks and balances in place for comments other than the reviewers eyes..
and your whole argument is that good devs don’t miss these types of things but that, my friend, is the REAL “fallacy”
Commenting and documenting are two separate things. Documenting modules, classes, methods, functions is absolutely a good way to provide information of what a module, a class, a method or a function is supposed to do. Most modern languages offer tools to write proper API documentation right in the code.
Commenting lines is seldom helpful IMHO. I use to line comments only to explain unconventional bits of code that can't be cleaned up for some good reason. The reason goes into the comment.
Call me a bad developer, but the only way I'm going to "update" a redundant line comment is to remove it altogether.
the devops playlist is gone
Hey bro. There's good news and bad news about that playlist. If you want more information feel free to DM me on Discord.
That s come clean code rules to keep your code elegant, good job*
The slogan "Write self documenting code" stands in contrast to the fact that for some reason, most Go programmers (including the language's creators) write code in such a way that most variables are meaningless, unreadable 1-3 letter "words"; functions are also exaggeratedly called abbreviations as if Go programmers were deducted from their paycheck for every letter in the code.
Therefore, it should be made clear - idiomatic Go is often a contradiction of "self documenting code."
Self-documenting code is impossible. Code can only tell you what it is doing not why it was written. Most cases that I have run into where a dev has really screwed something up is not because they could not understand what the code was doing, but rather they didn't realize why the code written that way. A function returns null and the dev assumes that the function has a bug and changes the function to not return null and crashes a completely unrelated system. An address space is not initialized and a dev assumes that it's a bug and makes sure to initialize the space only to compromise the encryption algorithm (this occurred in a linux router software update that created an exploit).
🙌🏽🙌🏽❣️
❤️
Method chaining, really? I thought Java demonstrated very well why NOT to use that crap.
The last problem is easily solved by typing. Instead of practicing primitive obsession and using raw strings, wrap them. This way it'll be impossible to accidentally swap the arguments and have the code still compiling after that.
Having different types would definitely help in the case shown in the video and most other cases, but there are situations where a function does actually accept multiple values of the same semantic type, such as multiple names, or multiple ages (such as to calculate who is older), etc. In those situations you usually only have two values of the same type though so it's not too bad, and if your language supports named arguments then that would be the best solution in those specific cases.
The last one can be avoided if different fields have different types. Overkill? NO! Useful type system
2:25 THATS THE SAME THING AS USING COMMENTS???
Creating a user struct to create a user 😂 Brilliant.
Almost all modern languages support named parameters, this approach would be way clearer than the one explained.
The other two pieces of advice are good ones though.
I think you’re confusing persisting a user and creating a user object in the application as being the same thing. Hope it helps! 😊
@@kantancoding I understood what you were doing there, for me it's not worth to create that User class/struct. If you are working with an ORM, maybe you can use a mapped entity of the Users table.
@@gearboxworks I would not create an object with a builder anyway. I'd rather create a lightweight structure/class just for the sake of passing around those params. And I would only create it if the context you described applies.
I agree he should have mentioned named parameters, but we're using Golang as an example which doesn't have them, almost every startup nowadays uses a Go backend so thats perfectly reasonable
Whether the language supports named arguments or not is irrelevant. It will always be cleaner and easier to see what the values are by a params struct. Otherwise, you'd have to check in the function itself to figure out what the arguments are. It still doesn't solve the problem of accidentally switching arguments around either.
Regardless, if you find your function is taking in enough arguments to push it to the next line... you might want to reevaluate what it is you’re even doing...
why code in this language a so ugly?