Stop Using Booleans in Your Code! | Code Cop
ฝัง
- เผยแพร่เมื่อ 9 ก.พ. 2025
- Until the 30th of September get 30% off any course on Dometrain with code BTS30: dometrain.com/...
Subscribe to my weekly newsletter: nickchapsas.com
Become a Patreon and get special perks: / nickchapsas
Hello, everybody. I'm Nick, and in this episode of Code Cop, I will talk about boolean blindness and why it is not what the author of this LinkedIn post described.
Workshops: bit.ly/nickwor...
Don't forget to comment, like and subscribe :)
Social Media:
Follow me on GitHub: github.com/Elf...
Follow me on Twitter: / nickchapsas
Connect on LinkedIn: / nick-chapsas
Keep coding merch: keepcoding.shop
#csharp #dotnet #codecop
Clean code means readable code. If you need to come up with your own data structure for every parameter you use, it's a nightmare to debug. Maybe it's just me, but I like to use basic datatypes whenever possible. It's especially good in the backend if you can match your datatypes in code to datatypes in the database.
if you don't have named parameters in your language of choice, then having concrete types (like enums) makes the code quite a bit more readable on the client side
One day the "advice" will be so horrible that at the end of the video you will say "Stop Coding".
That's an April Fools video for next year sorted
Thats AI in 3 years
I agree with you. If the boolean is obvious (apply discount vs do not apply), then go with the boolean. If it's not obvious, like "apply discount pro-rated to all items" vs "apply discount to highest price item" (stupid example I know but best I could come up with on short notice), then an "applyDiscountToAll" boolean can be confusing, since it's not obvious what false does. Then you use an enum.
Better example would be apply discount, apply upcharge, none. That kind of business logic can be complicated fast
I have a thing for enum switch statements. They just are so clean and easy to read.
Bonus as complexity grows your switch case can be a bitmask on any set of bits.
0 = 0b000, enum = no_discount,
1 = 0b001 = discount_highest,
2 = 0b010 = discount_all,
4 = 0b100 = discount_couponed.
Then just evaluate each bit
If((discount_flags && ( 1 >> i) !=0)
//add bitmask and switch
My biggest problems with booleans historically has been the calling methods don't give visibility into what parameter the bool is for. But now IDEs pretty much all describe the parameter for you.
My previous practice before this was a thing was to use the parameter name in the method call (kind of like you do for optional parameters).
Why the Boolean? ProcessOrder(1000) vs. ProcessOrderWithDiscount(1000). Some code higher up decided if a discount is to be applied, why can this code part not call order processing accordingly?
@@hb-man Because the caller might not be the one making the decision. The discount flag could come from an object passed via an API or pulled from a database.
This is why the example code is so bad. You wouldn't write order processing functions like this. It's not even remotely realistic to how you'd handle order discounts.
avoiding boolean parameter and splitting in two functions often will mean to have the "if else" statement at an upper level :)
thats exactly what Chapsas said
No you wouldn't usually. What people do is they have some functionality that deserves it's own function. But then they start using that functionality in other higher level function but they require slight difference in behavior and instead of making two clear lower level functions (where one can still call the other for the common functionality) they decide to use a bool param and use it to enable the change in behavior. You're adding an if where there doesn't have to be one and it's actually slower.
Exactly. That makes flow control clearer to the reader.
@@gileee yeah, when you have this situation you have two options. 1. Break it up into two functions with similar but slightly different code. 2. Take a page from functional programming, and pass in a function that represents the difference between the two processes.
You could also use two types
Clean code appears to have fallen to the same fate as Martin's other evangelisms - e.g. Agile. There are virtues in patterns, but once they become a religious dogma that all code needs to be coerced into conforming, they lose much of their value. The argument of avoiding passing booleans because it means branching inside a function, as apposed to branching outside to call separate functions, appears on the surface to be a case of following a pattern for its own sake.
Exactly. In that scenario you're having to branch at some point anyway. If you're having to do many complex conditionals/branching then you're probably trying to do too much in one place and should rethink your business case/scenario to separate concerns appropriately.
I think in general people need to stop saying that there is any one correct way to program. Even if something is a good rule of thumb the vast majority of the time, that doesn't mean it does or should apply to every situation. Sometimes it's better to forget these "best practices" and just use your common sense.
Sortof agree. But don't forget the advice. Follow it usually but be willing to make bold choices when you think it's wise.
While true, we have to understand there is a learning curve to programming, and this isn't something that most people will understand right out of college. These design rules are training wheels for inexperienced coders. At some point you can take them off, and you'll have an understanding of when to use them and when to break them. That understanding isn't something that comes naturally, it is something that is learned.
@@evancombs5159 I don't think you should use a rule if you don't know why it's there, and the only way you'll know why it's there is if you have the fundamental understanding. Once you have that you can look at the rules and decide if you want to use them for each specific case.
For example when I was new I learnt the "keep your methods small" rule by having absolutely massive methods to the point where they became unreadable and extremely difficult to change.
Because I learnt this the hard way, I now have the nuance that sometimes it's okay to have large methods, for example when initialising flags/objects at the start of a program.
I don't think it would be helpful to just be told "keep your methods small" without having any idea why you should do that.
The only correct program that exists is an empty file
Enum is even more complicated because evaluating it defensively, requires checking the else (unknown) value state, to ensure a later developer hasn’t added states that your method isn’t aware of.
Ok, now I want a video of Nick refuting those *blergh* CleanCode™ tipes from Uncle Bob
Uncle Bob is the Life Coach who tells you how to live whilst having $50k debt, and regularly misses his car repayments .. his job is to make up problems with code
If you need a good argument against clean code, just try developing games using those practices. Instant single-digit frames per second at best!
Also it might be just me, but many "Clean Code" practices just make the code less readable for me. While I often give very descriptive names to classes (which I sometimes use in "unusual" ways), structs, functions, etc., I don't annotate code by putting them into their own functions.
@@ZILtoid1991 Game coding, at least the high performance part, as with code for high frequency trading, comes with its own challenges and you need to make trade-offs in several areas regarding your code.
In any other regard, a good design is way more important than outright speed, because what really counts for most applications is the time it takes to enhance your software with new functionality and how to deal with changing requirements and correctness. This is where "Uncle Bob" code comes from: Separation of concerns, dependency injection/inversion of control and as a consequence: injecting functions (or Enums that can easily be changed to contain functions) rather than Booleans, things like the DRY principle or not leaking abstractions/the Law Of Demeter, etc. they all serve to make your code more testable, more robust and easier to change.
If you injected a function/object to handle the discount, rather than a boolean, you could rather easily add another discount, without changing the method itself and by not polluting it with foreign functionality.
I've seen booleans like that ripple into the domain model, including respective SQL statements and when the requirement changed not only did you have to change wide parts of the code base, but also, and more importantly, deal with migrating a legacy data base, or even worse: other services that consume the same data, but sit with another team. You don't want this.
@@ZILtoid1991Uncle Bob isn't exactly focussing on Game development, is he?
And if you know the inverse square root function from the Quake or Doom engine, that's obviously the opposite of clean code, but it is lightning fast - but it had to be invented only once, so isn't subject to changing business requirements.
Speed optimization is the opposite of readable, changeable code.
@hb-man Game Development is the one field where your Default option must be performance unless the programmer knows it's inconsequential.
The kinds of idiocy and jank I've seen some 'cleancode' auditors suggest to game devs is just utterly ludicrous. Just the amount of virtual method calls over a 5ms timespan is Very relevant.
Coming back to, it's Highly context dependent. I absolutely do not write business service functions like i do say a serializer or a game loop.
Becoming proficient in your domain and in your tools should always be #1, then you can know which styles and processes work for what you need to do.
To uncle Bob's defense I think it is worth remembering where he comes from. Being "a senior" developer myself, I have also been through that period of time where object oriented programming was new enough that everybody were designing class hierarchies so deep that they were reaching hell. People were writing methods several hundreds of lines long. Nobody paid attention to number of arguments for functions/methods.
My impression is that nowadays new developers are learning better OOP skills during their education, and therefor those somewhat extreme measures put forward by uncle Bob seems completely out of place. There are still some valuable points to what he is saying/writing, I think.
There's a coding principle called KISS, with which the author of this advice does not seem to be fully familiar...
Why not? KISS does not mean writing code in assembler and risking that you are absolutely prone to simple typos.
@@DevelTime What are you talking about? Point is that you don't need to over engineer code with enums when you should just use a bool
@@DevelTime I think writing code in assembler is definitely not simple and the other s.
Ah yes, Knights In Satans Service.
@@Ilix42 You're wrong, it's actually Keepers of the Solid State.
Passing booleans is a bigger problem in languages that don't support named arguments. Js/ts is the one that immediately comes to mind.
Edit: also it's definitely valid to start with the boolean, evolve to an enum if there are more than 2 options and finally inject the discount as a strategy if that gets too complicated. Refactoring is allowed!
The context of the code matters. In many instances, automatically using Enums could be over engineering. If it only requires a Boolean, then use that. In many instances, an Enum works well, especially for parameters. Or if something else would work better, then use that. But don't implement unused code features that could lead to confusion, like implementing a base class that contains no functionality, or an Enum where a Boolean value would suffice, or applying a more complex pattern, such as creating a class that returns a true or false value. That advice came from a mid-level developer aspiring to be the teacher instead of continuing to be the student.
I think this might fall under "premature optimization." Granted, in the past, I've converted bools to enums when they magically got a third value, but it's so dang trivial that you should just let the bools be bools until you need them to be anything else.
Project on day one: There are only two types of discount, discount or no discount (booleans are everywhere in the code base, all public API, etc)
Project on day one-hundred: We have currently 673 discount schemes, depending on the customer type, geo location, day of week, phases of the moon...
😆
True but hopefully a refactoring occured when the business logic changed to reflect it and a new and more suitable solution was found perhaps a discount calculator class or something like that.
@@Tldr205 Or, if you (not you specifically, you in the general sense) are willing to admit that people who have gone through all this already might actually know a thing or two you don't, you could listen to what they have to say and design it without the bools to begin with, because its a small upfront cost to pass in a function as opposed to a bool is good design that can save your ass later.
I'm a huge YAGNI guy, i try not to add anything to my code that isn't needed.
But that has to be balanced against what experience tells you is the likely outcome of passing around bools.
The truth of the matter is, the bool is more likely than not to _be_ the refactor.
People LOVE to tack a bool on to the end of an existing argument list and default it's value. Absolute trash.
@@Tldr205 and it's easier to refactor this, if you didn't start with a boolean in the first place. Don't use YAGNI as an excuse to start out with a bad habit.
I've seen Singletons established with a YAGNI mindset that still haunt applications to this day and are next to impossible to weed out, because that would require too much time and development never stands still.
@@khatdubell The issue in that scenario of yours is not bad practice in terms of which specific unmanaged type they use for parameters, but lack of effort to keep the code maintained and refactor often when changes occur. These are conflated to each other dangerously often. I would be very suspicious of anyone especially a very senior developer offering absolutes, far too often the presumptions are either outdated or do not apply nearly as universally as implied.
A senior giving such advice Always does warrant examining the topic and how it does apply to you however
@@RiversJ I think it depends on who you’re dealing with.
I’ve seen people pass ints by const & “because passing by ref is faster”.
I think that is an example of what you’re talking about.
I would never take random advice from such a person.
If, however, it was someone I know writes quality code, and it doesn’t contradict things I already know, I’d be inclined to accept their advice without questioning it.
In every project I work on, I end up replacing about 50% of boolean fields and parameters with an object, struct, or enum over time as the project matures. It's not because I am trying to follow some rule but because something eventually ends up requiring me to do so to avoid obliterating code readability and maintainability.
Booleans just don't contain enough information to be useful in many situations. I'm not saying to avoid using them. I am just saying you should keep in mind that there's a 50% chance you're gonna end up needing to replace the boolean(s) with something better in the future. Don't write your code in such a way that you're gonna be fucked when you decide to replace the boolean later.
But is this not the way it should be? Implement simple logic, and refactor to more complex when the business requirements need it?
@@Tldr205It depends. If you don’t think scope changes are likely to happen in this code, keep it simple and use the Boolean. If you think this code is likely to get extended in future changes, then take the time to make it an enum (or whatever makes sense) now so you don’t have to change every call to your function as part of refactoring.
That to me is the point. Rather than “never pass a Boolean” or “passing booleans is fine”, it all comes down to understanding how big the project t is likely to get and building it from the beginning in a way that makes sense for that size project.
Make sense, I'm also do tired of all the do this and not this statements, when it all comes down to understanding context and pros and cons
All depends what the boolean is for. If it's a simple condition ("the file opened successfully" vs "the file did not open successfully") go with the boolean.
We use a lot of booleans for method arguments in our production code and one boolean is not a problem, when it has a good name.
As soon as we have more boolean, regardless of the order - we replace all booleans with an flags enum or better, separate the method into multiple ones, if possible (often this is not possible, due to duplicate code). But there is always a point, where you have to glue them together, so in the end - you don't gain that much by splitting methods.
I personally like booleans in methods or functions and as soon as i have more than one directly following the other one, it is a code-smell and i replace it with an bit-flags enum instead.
Flags are great, but I also consider them code smell.
As many times as I see them well utilised I see them as a band aid for an overly complex method that needs splitting
Each case is different, but in general passing a Boolean is a last result and to me is a sign that some rearchitecting is in order.
For the example Nich shows, I'd rather pass in an optional param "existingDiscount" if possible.
Also, I don't understand the hate for "clean code", sure some things Bob takes too far but don't throw the baby out with the bathwater because you don't like the guy.
@@DevMeloy the hate is for Clean Code(tm) not clean code. If you get me?
@@avisian8063 I've watched clean code(tm) and walked away with some good knowledge, do I subscribe to every commandment? No, but it seemed like the hate for clean code came from a dislike of Bob not necessarily his views on code.
Not being argumentative, I honestly didn't look into what "Bob did" to piss everyone off... And honestly don't care.
@@DevMeloy no, I mean Clean Code(TM) as a saleable product. Like capital A agile. It's the grifters who treat it like its commandments that are annoying (and who have followers who treat it as gospel)
With CleanCode, it's not the rules that count, but the fact that we deal with these rules, even if we don't follow them - hopefully after careful consideration.
An enum can provide additional values like GoldDiscount and SilverDiscount. But for business logic that includes orders and pricing, it would make sense to use a strategy pattern instead of conditionals. For simple/POC projects, a boolean is perfectly fine.
It would make sense to have the enum replace multiple boolean parameters (applySilverDiscount, applyGoldDiscount). Especially in your example, because these options are probably mutually exclusive.
Designing the application, so it can potentially calculate different types of discounts in the future can be good, but on the other hand you can be wasting time by overly generalizing your methods when you have a clear use case.
"Call one method in the true case, and the other method in the false case" just suggests that the if/else moves up a level to the function that calls those functions. As a mid-level engineer, my tech lead also told me that boolean arguments should be avoided, because when you read it at the call site (assuming that you either haven't been provided with a named argument, or using a language where named arguments aren't supported), then you don't know what that boolean means, without looking at the function itself. In contrast, an enum provides additional information that expresses the intent of the argument (as per the LinkedIn post)...at least, that was the rationale I was given. Neither approach bothers me.
I don't even really understand the second argument. Yes, in plain text it is maybe more readable, but most editors nowadays should have a feature to show you which values correspond to which parameters.
And perhaps you are even already passing properly named variables to the function, then it would be no issue as well.
I think it would be better to have a method that calculates the discount and passes the discount into the ProcessOrder method, which always does the discount, even if there is no discount.
It depends on the discount structure. You could have quantity break and value break discounts. You may only get the cheapest one. I worked on something that had a standard price and a discounted price if you paid a separate delivery charge for the week, it calculated which you were better off with and it charged you the least. Ultimately you may have to price everything to work out what the discount is. In that case you need a method that returns the discounted price and the full price
You should have a data structure that allows you to pass in all the data, including any voucher codes etc.
I completely agree with you. There are use cases for passing a bool, and there are use cases for calling one func for true and one for false. But either way, there is a conditional somewhere. Unless the situation lends itself to branchless code. Relegate dogma to prioritised options at best.
"Clean Code" is for people that don't care about Time or Space
And in the case of a dozen two line functions instead of two bigger ones, traceability
I agree that passing down flags to a method is a definite code smell. If you pass a flag, you probably need two different methods or encapsulate that flag (for example create an Order object and pass that down).
The advice itself isn't even that bad, but the example they show is (like you said).
Too often I see people who want a single, simple solution for all problems. Not only do I see that desire, I also see that educators need books to do their thing. And it's easier to put single, simple things in books.
That's what causes many cases like this: "Oh, I see a problem in that corner over there. Ah, I know the solution. And now that solution is the answer to everything." (And there I was thinking that it was 42.)
I sign my emails with the quote "When all you've got is a Hammer, every problem looks like a Nail" as a reminder that a single solution doesn't exist.
Instead, I have a toolbox (or rather, a toolcart) full of tools. For each problem, I need to find the right tool. Sometimes, it is to avoid a boolean. And at other times it is to use a boolean.
Teachers would do well, not to teach just a single tool, but to teach multiple tools, and how to decide which tool to use.
And I think Nick Chapsas is helping a lot with that. So, thank you for spelling it out in video's like this one.
It's when people make broad sweeping statements and overgeneralizations that they get themselves into trouble. Heard a new kid that joined our team a few years back confidently declaring relational databases were "too slow". He wanted to build everything with no-sql and document databases because that's what he was taught. It didn't matter that most the senior devs around him could write (and in many cases have written) entire systems against a relational datastore that would run circles around anything he built. Because he heard something from someone, it must be true in his head.
Sometimes you really really just need a simple boolean and you will never have to change it
I’m always amazed how so much clean code is thrown away and rewritten. 😂
I'm already grateful that you're venturing into these dark corners of LinkedIn, so I don't have to.
My best coding advice: beware of coding advice that starts with "never" or "always". Instead, look to the "why" of the advice as it will instruct you on what some good motivations can be. From there, you can better assess how a specific situation will be best fit by a particular code style.
Regarding passing Boolean parameters, I agree that they can sometimes lead to ambiguity if the code doesn't read clearly. That being said, it is sometimes a necessity, especially if the caller has no knowledge of when in the method call the conditional logic should be executed, which is likely to be the case when you're invoking the method of a different class.
You can certainly opt to create multiple methods with different responsibilities rather than introducing a Boolean parameter, but this doesn't scale well if there need to be more options in the method call. Exposing a different method call for every single permutation will litter the API, which can be a real pain when designing a class library for public use.
Generally, if a particular operation has a good number of different permutations in terms of how it's executed, I will tend to favor a parameter object to encapsulate all the various options in the call and delegate the responsibility of dispatching the work to the callee since they're more likely to be an information expert on how to execute the operation.
"Ah yes, let me introduce a new type which basically just encapsulates a bool, just so that I don't have to pass bools around" lmao who even thinks like that
Consider a better advice: "instead of passing a boolean, just create a class with `T Choose(Func left, Func right)` method which runs the correct function depending on whether you have `true` or `false`!" :D
Looks a bit like over-engeneering and a slowdown in performance. You extend the world beyond a fixed number of choices known at compile-time. If that's your goal, do it.
@@holger_p I thought the ":D" part would play as a sarcasm marker. But generally that's the eliminator pattern which is useful for DUs (which `bool` also is; it lacks any additional structure though and has a built-in eliminator `?:`) in the absence of a proper patmat support
The worst situation is where people add a boollean to aupport callers that pass the wrong parameters and the values need to be cooked five levels deep and the bool gets passed around.
I have watched this videos more than twice. And I think I simple agree with uncle bob. And I have tried to granulate this to very real life code scenario. I believe bool is simple do this or do that switch and every do this or do that can be put into a method (it can even be a private method). It is cleaner, more readable and easier to manage that passing a booling to a method
Honestly in this particular case, would be even more valid to question why a [bool applyDiscount], if a [decimal discount] would make so much more sense here. Everytime i'm working on parameters like this i stick to it's original purpose. If i'm using the word "discount" somehow, it becomes so more auto intuitive to use a float point number instead of a boolean, because the word "discound' just tells my brain to think about a number with probability for 2 decimal cases. Also, anytime i have a method with a bool value where the "if true" routine is complex and/or long, i rather to shatter into more than one method.
for clarity on scalar values use the "newtype idiom". In C# you can do "public readonly record struct ApplyDiscount(boolean apply);" and then "ProcessOrder(new OrderId(1000), new ApplyDiscount(true))" is heavily used in haskell and Rust, unfortunatelly in C# you need the "new" which makes it a bit verbose.
I think is a bit of overkill for bool if your variable/parameter have good names (shouldApplyDiscount), but for OrderId I think it gives tons of clarity (and avoid issues with passing the wrong "int"
enum traps are real and can create an even more annoying mess.
Edit: Seeing Uncle Bob hurt, I read through his books when I was being 'forced' to learn Java...
The YAGNI principle, or "You Ain’t Gonna Need It", is a software development practice that states that features should only be added when they are required. Use the damn Boolean!
Clean code depends on the eye of the beholder sometimes... Not sure I completely agree with all this. Especially after about 40 plus years.... But it does give me something to think about, Thanks Nick!!
Te quiero mucho, Nick Capas.
Sucks when you have to tell the boss you can’t answer his question because you failed to consider the need for a “not yet selected” option.
What about the feature of editors to show the label for parameters?
To avoid naming the parameter specifically, if you dont have many of them that are optional and need to choose.
Alzo Zoran Horvat have videos specifically on discounts and use type pattern matching with objects. I guess it depends on the complexity of the domain.
This. I'm so tired seeing all those boomer pieces of advice. Add prefixes here, shorten names likes that, don't use this, but use that, write comment for everyting... And their justification is "It's more readable, debugable etc". Ffs do you all write code in Notepad or something? Every modern IDE has features that help you understand what is what. Without you making code worse with their advice.
And then the same people are going to write code like this:
int32 GetCntPrdNum(int32 p_Num, FooBar p_Prd);
To then add 10 lines of comments to explain that "Cnt - Count Parameter", "Prd - Product Parameter" etc.
I've seen it, I hate it.
Sure it depends, but the example from the video cannot explain the meaning of the advice. However, commenting on Zoran's videos, there are valuable examples like replacing `IsPublished` property of `Book` class with more robust representation
In my team, I would much rather see code optimized for readability in GitHub (i.e. for PRs) than relying heavily on an IDE configuration. Other than that, I agree - start simple, and introduce the complexity gradually based on your needs.
I think you should split your comment into two distinct comments
What I often see is coding advice like "Don't do X because if you use it excessively and without reason then your code becomes bad." Like, yeah, obviously you shouldn't use things excessively without reason, but that has nothing to do with any specific programming habit, it's just a general rule.
Nick's mimics in this video are hilarious
I won't hire anyone that doesn't use booleans. I also won't hire someone that doesn't use loops. I also won't hire someone whose clean code is overly terse and contrived.
I can't believe the article didn't mention the main reason for using enums over bools as params, the fact that you can add an extra value to the enum and it's not a breaking change. You can't do that with a bool.
LinkedIn has a setting enabled by default called "Use my data for training content creation AI models". I firmly believe developers are trolling linkedin to train bad AGI.
The post's "reasons" already look AI generated. No sane programmer would write that.
For me, boolean have a problem that is not related to boolean blindness but more about memory allocation, flag enums can be easily implemented in modern languages and be really practical.
I like to use a option class, and set the properties with boolean parameters. Only if i have multiple booleans on that function, or multiple booleans that will need to be passed thru bigger functions.
And the default value will be defined inside the option class.
The one place with booleans I would agree on is C++ or C# methods that have a possibility of failing, and they return a boolean and have the actual value you care about as a reference parameter that is set, then yeah don’t do that. C++ has an optional type in its standard library and while I’m not sure about C# it’s not too hard to implement one that has similar behavior.
Alway enjoy your videos, I have my team of developers watch your channel too. Code Cop ones are my favorite.
My takeaway from listening to programming advice over the years is. If they they are telling you something that would limit your design options, it’s *probably* not good advice.
Every nail has a hammer.
This ks reminding me of my boss wanting us to make a string valued enum and the static method to read that value (which we would have to rewrite for every namespace) instead of just passing a bloody string in when calling methods which literally build strings for string valued protocols/frameworks (such as web urls, markdown, etc)
void ProcessOrder(OrderId, IDiscount);
readonly record struct OrderId(Guid Value);
interface IDiscount { void Apply(OrderId) }
class NoDiscount : IDiscount
class PercentageDiscount : IDiscount
// more discounts...
Basically big consulting firms put juniors and interns to work with little guidance and no coding standards and at this point Clean Code is basically this minimum set of standards which can help preventing this kind of crap. It isn't that great but it's something.
For instance regarding this topic you find functions with a boolean paramater where the two branches are copy pasted anyways, and it's all the same except for a couple lines that change based on the parameter. So at first glance it looks like it would be better to separate this in two functions, until you look closer and realise it's just those 2 lines that change.
If there's a chance that the logic could change in future, i.e. the discount rate will change, then an enum is on the right track but it's still wrong.
Strategy pattern would be better there.
I don't wanna split my order method in 'Orders with Discounts' and 'Order with Discounts' and then need to make a seperate payment calculation method to not have code duplication in both? This does not save me anything and does not make anything more or legible... I could just add one life with an if statement and a boolean..... I totally agree with you here.
Looks fantastic, certainly lots of flexibility over xunit and opening opportunities to make complex testing easier
when I'm adding a boolean parameter to a method, I found it useful to stop for a moment and consider if it should be an enum, or maybe two methods instead. More often than not I decide to keep the boolean, but sometimes I do choose another option.
Thank you man, for trying to stop clean code madness
That's discount option, which means there might be several kinds of discount. That makes sense if you plan to have more than one kind.
I get really sick of these rules. Firstly they should never be rules, at the most they should be guidelines.
Sometimes it's better to use a single method with a couple of bools, other times it's better to separate the methods.
But think, you could separate a function with a single bool into two functions. If you had two bools you would need 4 methods and 3 bools would mean 8 methods. Think of the naming hell, MyMethodWithClause1ButNotClause2 etc.
The point is, do what feels right at the time. I would rather write a function, test it and check it in rather than wasting another 15 minutes debating whether my approach is right.
This time I don't agree with you, example being 2:36 -- and this is why I use only wrapped ids -- NumberId, GenderId, WordId, etc. There are no "raw" ints used as ids in my code for a reason. Of course stating "don't use bool" is somewhat too strict, but I am all in for "limit their usage". The example provided is not good because of the reason you mentioned -- this is basically one fragile code (because of ambiguous type) exchanged with another fragile code (because the type is now open but it is handled like boolean). But the surface of the method (signature) is way stronger in the second case -- it is "readable" vs "bomb-proof", there is no chance in the second case to mistakenly pass for example "IsForeignerCheckbox.Value" instead of "DiscountEligible.Value". So in short, I fully support the idea of using enums (but not in the sense as presented in the lecture included), it is counter measure against human errors, being tired, not fully understand what goes where, typos (consider also that today we usually type 2-3 characters, and the rest is added by IDE).
Ok… I can’t believe me, the great non-documenter is going to say this… isn’t this why you document your methods?
Good tooling can also make things like this easier to reason through. Visual Studio has an option for showing parameter names with inline constants and such, as an example.
Also, have to say the clickbait title totally got me on this one 😅
I used to think that booleans only occupied one bit in memory; it doesn't. It takes an entire byte because the smallest amount of memory you can load into a register is a byte. BUT that's not all. Many times, booleans take up many bytes because of how memory has to be aligned, and padding is added to make it aligned along address boundaries. But that's still not all, because that extra space can go over the cpu cache pipeline, degrading performance and causes cache misses. Here's my take, if you have a lot of booleans, then use a [Flags] enum, which can allow you to pack 31 booleans in 4 bytes if the enum is a default of int32 (or 32 if you use an unsigned int), 8 booleans if it's a byte, and 64 bools if it's an unsigned long.
A Boolean is simply a bit of data that the called code uses to perform its function. So documentation is mandatory if you're writing an API and expect those writing code to call it know what to pass in and why. The values expected for the method to receive need to clearly defined and what the values mean, and how those values can affect what happens in code. Enums don't work well for public API's, because they get converted to integers, which mean they need to be documented as well. What does 3 mean? The developers on both sides of the calling/called need to be clear with the intent of each of the parameters being passed. What the data types are is insignificant if the knowledge behind the parameters is understood.
You could instead create a type to encapsulate all necessary state. This is really a non-issue for one boolean, but when you have several, you get a combinatoric explosion and good luck providing unique names to have a handler method for each code path. Or you could just...hover so your IDE will tell you what each parameter is or run an IDE feature/extension which shows a label for every parameter.
I wanna more clean-code-triggered-Nick :D You should make separate series going through this book!
Nah: if it's always gonna be binary, it's a boolean! Enum here breaks the less code is better code golden rule whilst being equally maintainable though not as extensible, BUT, if it's always going to be binary, then it's a boolean, IMO!
> Enum here breaks the less code is better code golden rule
This is not a "golden rule", it is a general and specific rule applied to very specific contexts.
You cannot approach domain objects or newtypes if you follow this to the maximum extent, you would also not use any statically typed language in that same matter (as dynamic languages objectivelly need to achieve the "same" things). If something is binary it does not mean it is a boolean, a boolean has a pretty objective description: to be true or to be false, it is reasonable to use when you need to describe something that can be either true or false, but not that has 2 possible states, this way of thinking fastly turns into extreme primitive obsession.
If its always binary use a byte 😜
Just couldn't agree more. Exactly what I think of these short sighted dogmas. And this is one of the most aggressive ones.
The only justification I can think of is that if you have 2+ boolean parameters, then make them separate enums so you don't mix them up when calling a method.
Nick, I agree with your comments. There is one issue you glossed over. If the parameter will usually be a constant, splitting into two methods is probably good. If it will usually be computed, it makes more sense to be a parameter.
I think both cases are valid, the boolean one is shorter, but allows any boolean to be passed in, even if it represents something else. Also a boolean cannot easily be extended, perhaps if more discount options arise. If the advice is to replace al places where we pass in a boolean, it is probably not worth it, and miht make the code needlessly longer.
Just use a multiplier in this case, if it's 1 no discount, any value below 1 it's a discount, no bool or if clause needed.
return falsen't;
Remember folks: LLMs are being trained on these pieces of content.
I'm a c++ developer and I'm still learning c#, but in c++ I think you should avoid booleans as parameters. If you read Code and see a function call you do not immediately see what true means. But if you use a (scoped) enum with yes and no you can see whats happening more easily.
Also it helps creating stronger typing.
It made sense until enum with yes and no
The advice could be useful if different reasons were given, as it stands, not so much.
I have used this in two situations:
- Its really really obvious that new options will arrive soon, starting this way can prevent expensive refactors.
- If you want to persist that indication in the database, an numeric enum can help keep track of things
i think the main issue in LinkedIn is that there are no dislikes
I think, the advice comes from supporting code where there are no named arguments. Invocations like "MethodName(true, false, false);" look really convoluted. But when you have something like MethodName(applyLogic1: true, useMode2: false, isOption3Enabled: false) it becomes much more clear what's going on here and enums become quite redundant. This also applies to string literals and other scalar types. Therefore, if you pass literals of any type to a method, it would be more preferable to use named parameters for them. Or instead, just use named constants.
It's ok until another developer comes along and doesn't follow that convention
@@mattbristo6933 If the function is supposed to be used throughout the codebase by multiple developers, then surely, we can design it to impose more explicit code style. But if the function is purposed for a more limited scope, like private method, then IMO we may have booleans and generally more relaxed definition.
@@zachemny bit in the example it is public otherwise I agree
The discount is a transformation of the price, so it should be passed in as a function. If there’s no discount, pass in the identity function. (All of this assumes you have to have a method with a very abstract purpose like “processing.”)
The one thing I would say is that doesn't at all relate to "Boolean blindness" or any of these silly reasons lol is that the enum can be extended over time to support additional cases. So often in our API design sessions we'll change a configuration type bool column on an entity to an enum like FooMode or BarType. Bool can pigeon hole behaviour toggling very quickly.
Enum item names are used throughout the entire application. For a boolean paramenter you "invent" a new parameter name on each method you are passing it into. And here you could make mistakes. Especially on refactoring, there is no automated "rename all" for case you ever change the meaning of the original value.
It's simply type safety, not to call a function(bool isenabled) with function(IsFrozen). With bool the compile would allow it, with two different enums, he wouldn't.
I see this in architects all the time especially if they are not writing code daily. They will rip apart your PR without understanding the added work they are adding now and in the future. When you had a dozen sun methods to control its difficult to know their dependencies. Someone coming into your code a year later will need to spend time understanding the order and where in your code that three line method is effecting.
I am considering a construction like this: Orders.Select(1000).ApplyDiscount().Process; or Orders.Select(1000).Process; if you don't want a discount.
Now, Orders would be an object factory. Select creates an object with two fields: The integer OrderID and the boolean ApplyDiscount. The latter is false by default. Then the ApplyDiscount() method will set it to true. The Process() method will then do the order processing...
Why this method chaining solution? Well, I noticed that ProcessOrder() itself returns void. And it needs an order ID to select some order to do something with it. And that's weird, as this probably means it has to get that order object from a database or some other resource and then process it. Whatever this object is doing, this processing is doing things that should not concern it! But you can create an order-processing class that has several order-related fields and various methods that allow method chaining. The last method would be the process() method that would use all information that has been provided by the other methods.
Would this affect performance? A bit. But it also makes the code more readable.
By architecture it adds some type-safety. As well as you could add "7 meter + 5 miles = 12 'nonsense'. You could do the same thing on the logical level.
You do not "or" combine things, that aren't combinable, you add kind of a unit to the value.
This honestly feels like code advice from Kotaku
This comes down to logical organization of a code. You make a function to combine input parameters and spit out a result. If the result depends not only by some values but also some flags you want to have all that processing done inside that function thus need to have some bools passed to it.
That enum looks stupid for a bool. For a bigger group of possible states it makes sense but for just two states no.
I'm with you on finding this advice totally stupid. And there are reasons for that:
- Applying method overloads (just to remove a boolean from a signature) simply moves the branching logic from the callee to the caller (so no benefits)
- As long as the boolean really depicts and drives a single, not entagled, true/false condition those are perfectly fine. (example : ProcessOrder(bool applyDiscount, bool isFrequentBuyer) might be confusing and dangerous to maintain)
- In the example provided is WAAAAYYY more dangerous to have the orderid passed as an int ... as int can be anything. How we could enforce at compile time the function is not called using, say, the day of the week as an order id ?
- etc etc
One problem with Boolean parameters is lots of them for lots of options. When (not if as it’s inevitable) you add another option, the parameter signature changes, breaking existing client code. As I use Java 8, which doesn’t have calling with named parameters, client code has a bunch of trues and falses, so is hard to read. Using a vararg enum means client code is a list of enum values and more enum values can be added for more options.
There is also the problem that the enum is not guaranteed to have only two values. In fact it could be any number that can be represented by the underlying type
I think Nick is absolutely correct here. Overall, named parameters are extremely powerful tools in the context of creating readable code. There are other techniques as well, but indeed, just replacing boolean with an enum is not solving anything.
In context of Uncle Bob, I think it is interpretation problem. I don't think he meant that boolean should not be passed to a method ever but that often it is better idea not to. On github there is a massive number of examples of wrong methods design.
The real problem is absolutely different. People hear an advise and bilindly apply it without understanding the context or purpose.
I agree the bool has a finite list where the enum has an infinite list which potentially creates cognitive problems. The approach should be looked at why do we need a bool parameter and determine are we doing different things or are we doing the same thing with a twist. The other part is does this code need to know about that decision? Such as should I have cases where the discount is calculated differently - Flat, Percentage, based on when its paid such as a 10% discount if paid in 10 days Net on 30? This may indicate I've to simple a concept on Amount Due or on Discount itself.
I think I'll write an article about this. For now I'll summarize that there are no dogmas in technology. And I've already talked about how "clean code" is subjective and relative. I could pass the amount to be discounted to that function, and thus remove the responsibility of the function to look up what that amount is. What I'm trying to say is that using bool as a parameter, or using an enum, or different methods, will be fine if it's justified and if it works for you in your context. In the example of LinkedIn it's not a good justification, nor is what Bob says a justification for not using booleans as parameters.
There are definitely pros and cons to using booleans. Sometimes they're clear and simple, however over-usage can create complexity. I guess they are... "a bit on and off"
"Booleans are bad" is a very binary statement
I've just recently started picking up some basic coding. I'm taking some self-study lessons in JS, leading into AppScript. Before that, I used to primarily do "in-sheet" coding for spreadsheets. And, before that, the last coding experience I had was a class on C++ almost 25 years ago in HS. So I'm not exactly _devoid_ of coding knowledge, but I'm nowhere near fully-versed in modern coding standards. But even just looking at this, it seems like *both* approaches are a bit crazy.
Using an Enum _just_ to re-invent the Boolean is flat-out dumb, no argument there. It's basically _half_ a Boolean; you only care when it's _true_ and do *nothing* if it isn't true, so all you're doing is turning the if/else into "if and only if; then".
But there _is_ kind of a valid point to be made regarding the If/Else. If you have two entirely dissimilar branches, it probably *should* be two separate functions. And if they overlap, instead of passing a Boolean parameter, why not just pass the Discount value *directly?* Or even use a Truthy value?
So, for example, the function could take a number either from 0-100 for a percent discount as a parameter, or 0-1000 for a permille discount, or even just from 1-0 as a cost multiplier. Then just process the payment with the cost adjusted by the provided discount. No need to even _check_ what it is; if the discount is 0% or the multiplier is 1x, that's just full-price. I'd only bother with creating fixed values through an Enum if there were specific discounts I wanted to _enumerate._ For instance, memberDiscount would give 10% discount, employeeDiscount will give 15%, and managerDiscount gives 20%; and a "None" case as a default. Seems like that would be a more sensible use, to create a list of valid discount values.
That rocket emoji is a dead giveaway for Bing’s GPT implementation
A Boolean is also a known type. When u use an enum instead of a Boolean because someone thinks it’s against the rules, your forcing everyone to find the definition of the enum to identify the possible values or the right one for your condition. It seems like a lot of unnecessary noise.
I wish people would stop doing dumb things in C#. But if they did, I wouldn't be able to charge for fixing their screwups :)
It's not dumb. Sometimes it's old, things that were not possible 20 years ago. Or it's quick and dirty, they had to meet a deadline within 4 hours, and then you minimize typing.
That's a topic of renovation in a chill moment.
@@holger_p talking about the bools and small mistakes that shouldn't be, regardless of .net version.
Coding C# for 20 years, I've always walked away from enums disappointed.