Boolean Is Not Your Friend

  Рет қаралды 44,786

Zoran Horvat

Zoran Horvat

Күн бұрын

Пікірлер: 372
@pl4gueis
@pl4gueis 3 ай бұрын
Best video to date. Loved how it clearly showed step by step how a code base degregates with "just one more change request" and how to propertly fix it. I hate these flag bools with a passion.
@rethardotv5874
@rethardotv5874 3 ай бұрын
That’s the reason why you make changes to the architecture if needed. If you just append features you get an unmaintainable mess within less than a year.
@pl4gueis
@pl4gueis 3 ай бұрын
@@rethardotv5874 I'd rather have a solid foundation and make it right the first time. There is no need for bools. Imagine if architects made buildings how we make software: "Just start building. We can always change the architecture later" Sure some flexibility in software development is required but most just go all in on that and don't plan anything at all anymore.
@rethardotv5874
@rethardotv5874 3 ай бұрын
@@pl4gueis that’s not what I meant. When requirements change the architecture need to change to avoid such nonsense as adding bools.
@pl4gueis
@pl4gueis 3 ай бұрын
@@rethardotv5874 Ah sorry I misunderstood. Yeah I agree I wouldn't call it 'architecture' in that case. I'd call it Domain Models should change and because we remember primitive Obsession we shouldn't use simple non expressive types like bool.
@cj82-h1y
@cj82-h1y 3 ай бұрын
The boolean is not my friend? Well, in all fairness, it's "a bit on again, off again"
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@cj82-h1y And when you stumble upon the rock, it's the gravity that made you fall. In all fairness.
@KazisCollection
@KazisCollection 2 ай бұрын
Amazing pun
@TPInspector
@TPInspector 3 ай бұрын
Basically, if you have a class with an attribute that represents multiple states, you can use subclasses to represent each of these states. This approach makes the code cleaner and more organized. It also makes it easier to handle changes, which is important because clients often have additional requirements or changes in the future. Thanks Zoran
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@TPInspector Only as long as there is no second level of inheritance. That is why I pay so much attention to make each class a composition of single-level hierarchies. Soon enough, there will be discriminated unions in C#, implemented as a single-level inheritance. They will just click with any design based on these principles.
@TPInspector
@TPInspector 3 ай бұрын
@@zoran-horvat Oh yeah. I did not even think about that. Thank you :)
@fswerneck
@fswerneck 3 ай бұрын
@@zoran-horvat So cool! Discriminated unions is something every language should have!
@RandomGeometryDashStuff
@RandomGeometryDashStuff 3 ай бұрын
hard not to make root class represent invalid state
@OatmealTheCrazy
@OatmealTheCrazy 3 ай бұрын
​@@fswerneck Personally, I'm against apartheid
@nickbarton3191
@nickbarton3191 3 ай бұрын
How I giggled at the first law of customer requirements, that they never stop. The second law is like unto it, they want it yesterday. Great job Zoran.
@slipperynickels
@slipperynickels 2 ай бұрын
third law is turns out they didn’t need it and it was just a whim from a non-technical manager.
@holger_p
@holger_p Ай бұрын
It's more like, they are unable to say what they want.
@nickbarton3191
@nickbarton3191 Ай бұрын
@@holger_p Or they explain it in terms of an implementation without describing the problem they need solved.
@neeeeeck9005
@neeeeeck9005 3 ай бұрын
This is what differentiates good programmers, if programmer doesn't understand or doesn't want to understand a hollistic picture of the product and requirements they're developing, they would not come up with this solution. This solution shows understanding of not just programming, but analysis and thorough understanding off business logic. Always understand what you are coding, and why you are coding it, before you start coding it.
@Tekner436
@Tekner436 3 ай бұрын
going from a team that designed the database first and wrote code around it, starting to use ddd is a huge breath of fresh air
@collynchristopherbrenner3245
@collynchristopherbrenner3245 3 ай бұрын
@@Tekner436 This story is all too common. The DB becomes the culture instead of code being the culture. Code is more flexible than RDBs by design - it's a matter making sure you use the code effectively and each tool for what it was meant to do.
@OtakuNoShitpost
@OtakuNoShitpost 2 ай бұрын
I don't know that "Represent that with another class which wraps couple of trivial properties" is a matter of not understanding the requirements or not listening to the customer...
@000dr0g
@000dr0g 3 ай бұрын
Marvellous video. As an F# user, I've been drifting away from using explicit class inheritance, but it's very inspiring to see it well done. Starting with a bad design is a great plot device.
@fswerneck
@fswerneck 3 ай бұрын
Definitely the right way of writing code. I was butting heads these days exactly because of this at work. Coworkers who are closer to management got to include code with potentially invalid states in it, and a boolean. Like: thing.is_partnership: bool, thing.partnership_description: str | null, and thing.partnership_enddate: datetime | null. During code review I pointed how the boolean is unnecessary, and that it would be better for those fields to be tightly coupled into a new type, Partnership, that could itself be nullable in this context, as in, thing.partnership: Partnership | null. If it's null, there's no partnership. If it isn't, then for certain there will be a description and an end date. They ignored my comments and merged anyway.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@fswerneck Sounds similar to my demo example.
@fswerneck
@fswerneck 3 ай бұрын
@@zoran-horvat Shockingly similar! That tells us how common this is.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@fswerneck Oh, there is no mystery there! I made this video because I saw that a million times. And they all look alike.
@NullzeRT
@NullzeRT 3 ай бұрын
First thing I thought of is a Rust's enum, which is algebraic type, meaning that each of it's variants can contain it's own data. And it turned out was the correct answer with analogous construction in C#. In Rust this solution comes to mind sooner, since its a very common practice and one of the core features of the language.
@zoran-horvat
@zoran-horvat 3 ай бұрын
Rust is doing that very efficiently, both in terms of space-time performance, and in terms of coding effort.
@CraigLuna
@CraigLuna 3 ай бұрын
The way Zoran implemented this is how I simulate Rust ADT in C#. Perhaps the intro of unions will also allow use to tighten up memory as well
@StefanH
@StefanH 3 ай бұрын
An even slightly flawed model can have massive implications and introduce pain points. Great showcase of how data models should be designed to make invalid statd impossible to represent
@batek34
@batek34 3 ай бұрын
I really can't follow a single video, it starts in the middle of an already started project, the narration is abstract and poem-like, the explanation is confusing and all over the place. He seems like a good developer, not a good teacher, the whole point of the video was if you have a class with an attribute that represents multiple states, you can use subclasses to represent each of these states, and i got that from a comment, HE never said that, he was all like "In the realm where code does weave, A tale of logic, pure and brief, There lies a class, a single form, With attributes that should transform". Personally I think Milan Jovanovic is the best .net online teacher at the moment, him and Mohamad Lawand.
@Ownage4lif31
@Ownage4lif31 2 ай бұрын
Yea same I was confused because of the title. This seems to be mostly for people who already know what he's on about. Just don't know why his title is clickbaity.
@Gremirz
@Gremirz 3 ай бұрын
This OOP approach is just beautiful. You good sir just sold me your course.
@yimyim117
@yimyim117 3 ай бұрын
Good demonstration of using inheritance to abstract away binary states. Although a data driven design approach would also yield high simplification. Why store the information of being published at all? You can manage these states as containers, e.g. a list of published books, a list of unpublished ones etc. But of course it depends on the context.
@Internetzspacezshipz
@Internetzspacezshipz 3 ай бұрын
Every problem and solution always comes down to context. But you can always choose the context for the next problem by solving the current problem in a predictive manner…
@chlojolo
@chlojolo 3 ай бұрын
I am grateful that the next time I have to explain to someone that bool is not their friend, I will now have the option to send this video. With any luck, they may even heed it.
@mkwpaul
@mkwpaul 3 ай бұрын
Great video, superbly explained and illustrated. I just wanna point out that this isn't just good practice when doing ddd or OO but any programming style be it functional, purely imperative, data-oriented, or in this case DDD/OO. Properly modeling your data, and making invalid states unrepresentable on the type level, is the biggest lesson to learn here. And this modeling generally requires much more fine-grained types than many developers are used to. The "IsPublishedBefore" check is implemented here as a virtual method on the base type, but could just as easily be a static extension method with a swtich expression instead in a functional or imperative data-oriented style. Of course there are always exceptions, where booleans are truly domain >data< and not a misguided way of implementing a more complex piece of information.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@mkwpaul I agree with your analysis.
@markky212
@markky212 3 ай бұрын
Please don't stop your mission Ser!
@zoran-horvat
@zoran-horvat 3 ай бұрын
I'm only warming up!
@yeti25934
@yeti25934 3 ай бұрын
I saw many issues with the code here, few of which were really caused by the boolean. Also, that "a boolean never walks alone" line was cute, but completely untrue. Please refrain from spreading catchy lies.
@aflous
@aflous 3 ай бұрын
You missed the point here. Booleans as a primitive type are ok to use and abuse when modeling a simple property or attribute. The phrase he used applies when using a boolean in domain-driven design, which stems from how you choose to express a certain business-related state.
@roll-outcommanders6520
@roll-outcommanders6520 3 ай бұрын
I find your videos tantalizing, provocative and very informative. Although I have come to the end of my software engineering career ("career" use as verb here) I am sure I will still take interest in what you have to say although I will never get to engineer any of your insights. Thank you Zoran and keep up the good work.
@juliogomez3790
@juliogomez3790 3 ай бұрын
You are a true genius of clean design! I've learned a lot with this video and I am excited to continue learning stuff with your videos. Thanks for sharing your knowledge with the world!
@josebarria3233
@josebarria3233 3 ай бұрын
Good video. Note that this applies not only to domain models.but also to game design.. instead of representing states with a bool, just take the time to make it more complete
@tryoxiss
@tryoxiss 3 ай бұрын
I know this is an example in Java (OH, its C#, they are so simillar I missed it), so this solution wont work here, but if you are using a language with sum types (Rust, functional-programming), you can use an enum: enum { Published(Date), Scheduled(Date), Unpublished }
@zoran-horvat
@zoran-horvat 3 ай бұрын
You are correct. C# currently uses record types to the same end, so your proposal is essentially the same design but a slightly different syntax.
@britbuttmcbooty9221
@britbuttmcbooty9221 3 ай бұрын
C# has enums I just used them a couple of weeks ago for a final
@harrytsang1501
@harrytsang1501 3 ай бұрын
In C++ we have union class, but the way rust or scala handles it with match case is so much nicer
@Vennotius
@Vennotius 3 ай бұрын
I will now keep an eye out to discover this boolean law in my designs. If I see it for myself, it will click. For now I take note.
@5cover
@5cover 3 ай бұрын
So satisfying to see that code shrink and simplify!
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@5cover Many programmers think that simple state makes simple code. They don't see how complex the methods must be to operate on that.
@baranacikgoz
@baranacikgoz 3 ай бұрын
That's why I think one must master the strategy pattern before write code. Excellent video.
@MaxPicAxe
@MaxPicAxe 3 ай бұрын
Yes, this is where I like to use enums with associated data. In this case we wouldn't necessarily wrap the whole book in the various possibilities, but rather just the section of data that depends on it, so in this case just the Date would get wrapped in the various possibilities.
@MaxPicAxe
@MaxPicAxe 3 ай бұрын
Edit: seems like that's what you did in the end
@zoran-horvat
@zoran-horvat 3 ай бұрын
Yes. C# uses record types for that purpose, and they are really efficient in every respect, including coding time.
@s0psAA
@s0psAA 3 ай бұрын
How would you store this Publication record in the databse, regarless of what actual instance it holds, serialize it to a json and store it in a single column?
@zoran-horvat
@zoran-horvat 3 ай бұрын
When there is a 1-1 relationship to the containing entity, then , you can store any of the possible values in that entity's table using strategy like table per hierarchy. That might create several strongly-typed fields in the table. There is no need for JSON, though it is a possibility as well, if you wish it that way.
@nanny07
@nanny07 2 ай бұрын
@@zoran-horvat IMHO, this should be a dedicated video because it's something that it's hard to imagine. It will be interesting to see the difference between a RDBMS database and a NoSQL one
@conradrobinson7941
@conradrobinson7941 3 ай бұрын
Hi mr Zoran great video but mainly I really really like your voice, tone, and pace of speaking. I will be watching all the others now.
@gonzo191
@gonzo191 3 ай бұрын
I see what you did there. A response to the confusion from the junior programmer video's book example.
@zoran-horvat
@zoran-horvat 3 ай бұрын
Yes, this is the follow-up video. I plan a few more, to clear other confusions that happened there. P.S. It was the seniors who were confused. Funny enough, in quite a few comments there, people who declared as juniors were explaining their mistake, but seniors wouldn't listen.
@unexpectedkAs
@unexpectedkAs 3 ай бұрын
Really good video, really good examples and step by step presentation. Will be sharing it a lot!
@umdi3337
@umdi3337 3 ай бұрын
One of the most brilliant videos I have watched recently. Next one should be about how to persist all these in a relational database 🙂
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@umdi3337 Actually, persistence is still not an issue at this level of complexity.
@MarcoAntonioRosadaSilva-h9c
@MarcoAntonioRosadaSilva-h9c 14 күн бұрын
I don't know how to explain this, but watching you code feels like watching someone paint.
@zoran-horvat
@zoran-horvat 14 күн бұрын
@@MarcoAntonioRosadaSilva-h9c I try to make coding enjoyable.
@marcotroster8247
@marcotroster8247 3 ай бұрын
Well done. But not everybody has the luxury to refactor the domain model like that. Most of us work at legacy systems and try to bring the codebase into a state like before your refactoring and would be happy with that little boolean flaw because no one really pays for that refactoring you showed. It's kind of unfortunate.
@DxCKnew
@DxCKnew 3 ай бұрын
In the end, you still need to map all these objects into a database. While possible with some ORMs, it is somewhat a challange and complicate things by iteslf because databases are naturally not designed with OOP in mind, imagine a big solution with tons of these, so I generally don't like to mix inheritance and database objects.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@DxCKnew Persisting this object model with an ORM is straightforward and it is managed entirely in the ORM configuration. Persisting it with document storage is more-or-less a single line of code. On the other hand, implementing domain rules, object interactions, business validations - that will quickly grow to hundreds of types and thousands of lines of code. I would really appreciate it if the programmers one day stopped asking "how do I save this" all day round and started asking "how do I implement 470 business requirements the customer has filed since the last year".
@dusanknezevic9072
@dusanknezevic9072 3 ай бұрын
Using boolean as a flag shows a problem with modeling. Modelling concepts with simple boolean flags leads to delegating responsibility to caller and worse yet, to cartesian explosion of possible states. Some of these are invalid and shouldn't even possibly be represented in code and at runtime. It's just sensible to use type system to capture concepts and states whether its OOP language or FP or logic paradigm etc.
@holger_p
@holger_p Ай бұрын
I don't see any difference, to use an enum, as a flag. a boolean actually is also an enum.
@Art1x_y
@Art1x_y 3 ай бұрын
If it comes to storage in the database, then everything can be the other way around. Then filtering (search) by Boolean variable will be much faster. And even just non-nullable type filtering is also always faster than nullable, so the decision always depends on the context of its use. There is no ideal solution, there are solutions suitable for specific tasks :)
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@Art1x_y You have a point there, but I must add to it. Even in storage, I would keep the timestamp if the event rather than a flag whether the event happened or not. That would help support diverse queries at virtually no additional cost.
@7th_CAV_Trooper
@7th_CAV_Trooper 3 ай бұрын
The domain model and the data model have little to do with each other. Of course you're going to transform the data before laying it to rest.
@NGC-rr6vo
@NGC-rr6vo 3 ай бұрын
damn, thats eye-opening
@perplexedon9834
@perplexedon9834 3 ай бұрын
This is where enums and options are so powerful. Rust isnt the only language, but its the most famous for it. let released= Option means that released could hold either "None" or a ReleaseDate. This forces every function that interacts with release to consider whether it could have failed to be release. None is not null, it is a true value thay can be handled. This sounds like a chore, but usually just means a single extra "?" in the function to early return and branch the logic if its None. Always cleaner than exception handling. If you want multiple possible states, its as easy as defining an enum that could be one of a set of states, some of which can hold data: enum ReleaseDate { Unreleased, Range(Date, Date), Specific(Date) }
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@perplexedon9834 Yes, enums with values in Rust and Java is what C# currently implements as records derived from an empty abstract record. It looks like C# will soon attain discriminated unions that will simplify the syntax a bit.
@yuryschkatula9026
@yuryschkatula9026 3 ай бұрын
It's not about the bool itself, the story is about introducing present-time properties into models that intend a period of time. The same thing about "Age" property, it is not a bool one for sure. Can you spot the domain issue nevertheless? Does it mean numeric types are not your friends?
@zoran-horvat
@zoran-horvat 3 ай бұрын
Primitive types are not your friends, to be precise. You must contain it in a meaningful type that is responsible for them, like an older cousin. A devastating side-effect if not doing so is insatiable growth in the number of fields per class. This Book in my demo would have a 15-argument constructor already, and I have only just started developing the application!
@2Fast4Youtube
@2Fast4Youtube 3 ай бұрын
So is pretty much: If you know you will have more than 2 states, don't use a bool to represent it
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@2Fast4KZbin It's more than just that. Add: If you know there will be behavior on that bool, make it a standalone type. And a few more ifs like that.
@fallegapyro
@fallegapyro 3 ай бұрын
Everyday, something new is "not my friend"! Hell nah! Strings, booleans, singletons and others are my friends!
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@fallegapyro How do you contain code duplication and code inflation, then? Looks like you are stuck with the data model, but somehow ignore behavior.
@NikorouKitsunerou
@NikorouKitsunerou 3 ай бұрын
This might be a point against "code first" databases. Some fields deserve their own tables.
@zoran-horvat
@zoran-horvat 3 ай бұрын
I don't think it is big enough to consider it relevant.
@Rick104547
@Rick104547 3 ай бұрын
What is your preferred approach when introducing persistence (with EF core for instance) with a rich domain model? I find that a rich domain model and persistence don't always like each other. There are some ways around it with custom converters etc but they all seem to have downsides by not fully supporting all operations for instance. Also the shape of a domain model might be completely different from how its persisted in a database. Iam leaning towards using a separate persistence model with types that are friendly to persist (which usually are primitives). How do you deal with this? Do you use different approaches depending on the situation?
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@Rick104547 I do all the EF Core setup in the infrastructure layer, i.e. not in the domain, say via attributes. in that way the persistence mappings do not pollute the rest of the code base. EF Core is progressively becoming quite powerful, with the resulting relational schema approaching the best one would do in an entirely manual design. There is one notable detail regarding EF that particularly annoys me: Many-to-many relationships. I insist on one-to-many in models, but EF insists on having its many-to-many part of the domain model. Then I must hide it in private fields and only expose that one-to-many relationship in the model as I wanted. The example in this demo is books and authors, which naturally form Many-to-many relationships in the database, but the book only observes many authors of its own, i.e. one-to-many.
@marko5734
@marko5734 3 ай бұрын
You can use ComplexType attribute for value objects
@adambickford8720
@adambickford8720 3 ай бұрын
That's a big downside to this approach; you'll end up writing many 'mappers'.
@Rick104547
@Rick104547 3 ай бұрын
​@@adambickford8720 that's true which is why I don't always do this. But then the 'domain' model ends up being a compromise as it also needs to work well with persistence. It's getting better with recent updates though.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@adambickford8720 Well, no. Why would not using an ORM be a big downside? Will that not make functional design less favorable, where in fact there is plenty of evidence to the contrary?
@gregorymorse8423
@gregorymorse8423 3 ай бұрын
There are more cases where Boolean flags are correct for state than these narrow areas of bad design with redundant ambiguous uses. A bit over dramatic and over selling. Try giving this talk at a hardware company or a BIOS or kernel driver provider. Their eyes will roll so far back in their heads it might cause permanent damage. So just noting this is true on very high level data or abstraction models. In low level code, on the other hand, this it is generally optimal to use certain contextual Boolean flags.
@zoran-horvat
@zoran-horvat 3 ай бұрын
Why would I talk the same to a BIOS company as to business application developers? Do I look like an idiot?
@gregorymorse8423
@gregorymorse8423 3 ай бұрын
@zoran-horvat lol I only meant the title was a little bit click bait without context. Granted I suppose it is considered fair game to do that. I assume that was a rhetorical question :)
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@gregorymorse8423 It cannot be a clickbait if the title only speaks of Boolean in a negative sense and the video only covers Boolean in a negative sense. I don't do clickbaits.
@user-tk2jy8xr8b
@user-tk2jy8xr8b 3 ай бұрын
Sometimes bools go alone. For example, a client setting controlling whether an IRR result should be annualized or not. There is no functional dependency with any other part of the application state, it only affects the output number. Feature toggles also go alone. Those are hardly domain models though. Otherwise, yes, bools usually have some sort of a functional dependency from another value, like in Nullable implementation. Not only bools, any type, potentially. Maybe such dependency eradication should be called "model normalization", just as it is in DBs?
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@user-tk2jy8xr8b I've done so many of those and I'll tell you're missing the world of opportunities if you only took a bool as the model. Consider a feature toggle that is the timestamp *when* the feature is toggled. Or a record of finalizing a transaction, which incorporates a timestamp, security information, external system reference, and a few other fields. A bool wouldn't walk alone in those applications either. The whole range of everyday requirements will require a redesign, you see. Some people fall victim to believing I am just guessing. They know nothing of a quarter of a century I spent doing business applications. I *do* know how incapable boolean models are.
@user-tk2jy8xr8b
@user-tk2jy8xr8b 3 ай бұрын
@@zoran-horvat what if I model dynamic computations over booleans (which I do) so that boolean values directly represent the domain? Sometimes a bool is just a bool.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@user-tk2jy8xr8b That is what I said in the video the other way around: a bool is a good answer to a question.
@yurisich
@yurisich 3 ай бұрын
Bools export work that's meant for compilers onto people. This is why feature flags work, it's a rare example of the friction they introduce serving an intended purpose.
@zoran-horvat
@zoran-horvat 3 ай бұрын
Think about a feature flag that has a time when it is toggled. Then a feature flag that has an interval, such as the one we use in A/B testing. I've never implemented feature toggles as a Boolean flag, but always as a method that returns boo and encapsulates logic. It feels much better.
@v0id_d3m0n
@v0id_d3m0n 2 ай бұрын
Wow - it's beautiful!
@ThugLifeModafocah
@ThugLifeModafocah 3 ай бұрын
I did not get one thing. Why were you using the new Published as param to Release if one another possibility would be that the publishing was just Planned? Wouldn't requires that the selection of what implementation of PublicationInfo to be dynamic?
@Foxsterdota
@Foxsterdota 3 ай бұрын
Great stuff! Out of curiousity, is there a particular reason why you made PublicationInfo an abstract record (especially one that does not contain any actual implementation like the one in this example) rather than an interface (like IEdition here) that the concrete classes could then implement? Perhaps it's mostly, if not entirely, a question of 'actually being' vs. 'being able to', if that makes sense...? As in the PublicationInfo is/is not published vs. an IEdition being able (an action) to be advanced to a next edition.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@Foxsterdota It's only my feeling that it makes them look more like a discriminated union, that is all. If the current proposal for discriminated unions passes, they will look like a class out of the box.
@mohammadtoficmohammad3594
@mohammadtoficmohammad3594 3 ай бұрын
Thank you good idea please keep going but please we hope you can present your ideas in better way, if people follow this way or another that does not mean they are doing mistake or they are juniors , but there are many aspects must taken into account
@fennecbesixdouze1794
@fennecbesixdouze1794 3 ай бұрын
You could have fixed the bug by simply introducing runtime validation into the constructor to ensure that if isPublished is passed as true, then a publication date must be provided. You could have a single concrete IsPublishedBefore method on the Release, which simply would return false if isPublished is false, and do the date logic otherwise. Your Program.cs code would look exactly the same except it would call isPublishedBefore on the Release, and the runtime type inspection would be replaced by checking the boolean isPublished. If you're worried that the consumer will sidestep your IsPublishedBefore method and still inspect the isPublished flag and hand-roll their own query depending on it, simply make isPublished private but still call for the boolean in the constructor with the same runtime validation. The only difference is that the constraint would be imposed at runtime rather than in the type system. Which is a fine trade-off to avoid writing all the extra code to introduce an additional abstract class with all of these overrides
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@fennecbesixdouze1794 The suggested method fails at runtime. I don't like having code that fails. I prefer code that works with no failure.
@twenty-fifth420
@twenty-fifth420 3 ай бұрын
I dont use Booleans, I just use 0 and 1 and I make sure the compiler doesn’t forget it. 🔫🔫
@AK-vx4dy
@AK-vx4dy 2 ай бұрын
Great video about object modeling but.... Do you have video with concise and sane method of mapping such type to database for example two database fields in one table ?
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@AK-vx4dy There are a couple of videos on other topics where this model was indeed persisted, using EF Core. I am currently working on another demo which might end up demonstrating EF Core persistence if this model step by step. As of now, it is important to know that EF Core supports all the elements shown in this video.
@AK-vx4dy
@AK-vx4dy 2 ай бұрын
@@zoran-horvat Yes i saw but I'm interested especially in "flattening" such simple subobject to multiple files possibly in the same table (I know you may have other ideas but many times I work with existing already databases which I can't change or like in this case it seems artifical to split to many tables) without too much boilerplate.
@NGC-rr6vo
@NGC-rr6vo 3 ай бұрын
hi, what type of content do you have on patreon? may be there is table of content somewhere?
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@NGC-rr6vo Basic membership gives you access to the source code from the videos. Advanced membership adds access to the new video course on object-oriented programming and design I am working on right now.
@Merssedes
@Merssedes 2 ай бұрын
After watching this video, I've one question: why so many classes?..
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@Merssedes Because there is a dozen methods operating on each. If you put it all into 2-3 classes only, each would be longer than 1,000 lines single-handedly and contain tons of mutually unrelated code. Think for yourself. Could you define a production-grade bookstore model with less than 10,000 lines of code?
@Merssedes
@Merssedes 2 ай бұрын
@@zoran-horvat What model are we talking about? Because my knowledge of C# does not contain such term.
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@Merssedes Domain model. It has nothing to do with C# in particular. Every domain application has it. It consists of domain classes and other types and methods/functions defined on them.
@Merssedes
@Merssedes 2 ай бұрын
@@zoran-horvat So all it means is that I just missing context for the video. Because i've never heard term "domain model" before.
@Karloffspring
@Karloffspring 2 ай бұрын
My biggest take-home was: use your language's type system to make impossible states impossible to represent. I loved the observation that a boolean variable never walks alone. (I'm not a C# programmer, but this vid was useful to me anyway.)
@DavidSmith-ef4eh
@DavidSmith-ef4eh 3 ай бұрын
Let me play devil's advocate again. I can see how that makes your code cleaner. But you still have to persist it in the database and some frontend api needs to call it using rest/graphql. A boolean toggle is so much simpler for them, or at least an enum...
@DavidSmith-ef4eh
@DavidSmith-ef4eh 3 ай бұрын
I mean, you could still have objects nested within objects. But you'd persist it as 1-to-1 child relation in db? I can see that working..
@pl4gueis
@pl4gueis 3 ай бұрын
I'd argue that for the consumer a boolean toggle is so much simpler..yeah to introduce bugs. What is the correct state combination for a book that is scheduled to release next year and the 'IsPublished' flag? Will the bool be true or false? I mean right now the book is not published but next year it will be published.
@DavidSmith-ef4eh
@DavidSmith-ef4eh 3 ай бұрын
@@pl4gueis in that case I'd use an enum + release date. Or just the release date, without any booleans. The good thing about a boolean, it can be a 1 byte db column. But there is no perfect solution, obviously.
@pl4gueis
@pl4gueis 3 ай бұрын
@@DavidSmith-ef4eh What stops you from saving it as a bool in the database? The way you model your domain is how the business works. It does not have to reflect that 1:1 in the persistance. You can just map it later.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@DavidSmith-ef4eh Keep in mind that the model also contains other polymorphic components, such as edition. The release date will quickly become more than just a date (the day is often unknown, only month and year are known or recorded; on dome old books it's only the year that is printed on it). Soon enough, the book object's state would grow to 20 fields or so, and constructors/factory methods would sustain combinatorial explosion, to the point where it becomes impossible to manage them. Going down that path would lead to having dozens of factory methods just to construct each combination of states. Polymorphic components solve all these problems at the outset. The trick is in keeping all mini-hierarchies only one level deep.
@NikolozLatsabidze-t6h
@NikolozLatsabidze-t6h 3 ай бұрын
Why you did not create a method inside Publication interface which is returning this 2 integers, logic is coupled to the Publication class, since publication state defines which integers should be returned in certain cases, in addition you can also remove this if else blocks and type checking for published instance
@MrCumberlander1
@MrCumberlander1 3 ай бұрын
Rust enums would be perfect for this purpose.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@MrCumberlander1 Rust enums are identical to C# records I used in the video. Same thing, different language.
@RoboDragonJediKnight
@RoboDragonJediKnight 2 ай бұрын
Alternatively, could be solved with some finite state machine patterns. A good section in "The Pragmatic Programmer" on this pattern.
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@RoboDragonJediKnight The goal of the finite state machine is in mappings that are closed to the machine. Here we have the problem of mapping the states of the supposed machine into the values that are not its states. The range of the functions we need is not the set of machine states.
@trustytrojan
@trustytrojan 3 ай бұрын
why add a boolean when you can simply check whether the PublicationDate has passed?
@zoran-horvat
@zoran-horvat 3 ай бұрын
Because it can pass without the book actually being published. Life is sometimes like that.
@sorakatadzuma8044
@sorakatadzuma8044 3 ай бұрын
So, I agree with your ideas and the changes that you propose to make code cleaner. However, I am curious how this would work in a language that doesn't support polymorphism or your project constraints don't allow for polymorphism? I'm considering things where Data Oriented Design/Programming is preferred or required, and languages like C or Rust is used.
@zoran-horvat
@zoran-horvat 3 ай бұрын
Rust supports this solution via enumes with values, and so does Java. In C# it is records right now, but as it seems they will soon evolve into proper discriminated unions. It is actually the discriminated union I am using in this demo, not inheritance in the traditional sense.
@HkanAktas
@HkanAktas 3 ай бұрын
I don’t understand why you wouldn’t get rid of the Boolean completely. Tell me if I’m missing something but the Boolean is not bringing any new information the date didn’t implicitly contain. Future date => planned book Past date => published book No date => unplanned book I also understand that you’re bringing context to an otherwise poorly defined flag, but the example is teaching another bad practice, namely unnecessary data denormalization.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@HkanAktas What is the state of the book that was planned for a certain date after that date passes but the book didn't get out?
@Mystic998
@Mystic998 3 ай бұрын
@@zoran-horvat It's an invalid state. But then so are lots of states that are technically allowed but not accounted for in the code (publication date = 20134-01-01, title = 'esfjesfjesdjf', etc.) The extra state information gives you traceability into what's probably a common occurrence (book is delayed), but that extra state has to be updated when the book is published either on time or later, so your data will somewhat regularly be in an incorrect state. Using just the publication date as an indicator means that as long as books publish on time most of the time, most of your data will be in a correct state without you doing anything. But then you have to reconcile your supposed publications with your actual publications and fix the dates. If I were doing it, I'd use the date to check if something is published for reporting purposes but also use the (not) Published state information to trace books that are potentially delayed/in need of correction. However, all or none of these are valid choices for dealing with the problem depending on the situation.
@HkanAktas
@HkanAktas 3 ай бұрын
@@zoran-horvat if that's a possible case, the code is not able to represent all possible realities. All that code in the end of the video is telling me is that a book can either be published or not published. Today is Aug 16, and if I see a record in the DB for publish date of Aug 10 but the `IsPublished` flag is false, I have to *assume* that it must have been planned. The code is not telling me that. If this planned-but-not-published state is possible, I would still get rid of the boolean flag, and add an enum state instead. Pseudo code: ``` enum ReleaseState { NotPublished, Planned, Published, } ``` With the enum, I can now distill the information that if a book's release state is `Planned`, the release date is a planned date. And if the date is in the past but the state is still `Planned`, I can know that it was planned for then, but it didn't became the reality. The code no longer depends on an *implicit* plan-to-publish state, and the devs no longer need to put extra cognitive energy to deduct that.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@fusedqyou So, it is date and date vs date and bool, with all other issues remaining? I thought you would resolve the issues in code, not trade one scalar type for another.
@HkanAktas
@HkanAktas 3 ай бұрын
@@zoran-horvat Ugh, I wrote a detailed answer the same day, but either my internet failed me or KZbin decided it's not worth to be displayed to the world. In summary, if the state you mentioned is possible, wouldn't it be better to use a state enum instead of a boolean? Boolean flag is technically a state enum with two implicit states, but IMO it's clear that the product has a third state in reality. The "planned but didn't go out" state. I would rather represent that with: PublishState.Planned & a past date, instead of a confusing IsPublished = false & a past date.
@AndersBaumann
@AndersBaumann 3 ай бұрын
This refactoring looks jolly good until it is time to persist the Release class and PublicationInfo record with EF core or NHibernate.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@AndersBaumann Actually, it is quite straightforward with EF Core.
@AndersBaumann
@AndersBaumann 3 ай бұрын
@@zoran-horvat May we see that?
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@AndersBaumann There are a couple older videos with full persistence of this model, and there will be a few more to come.
@AndersBaumann
@AndersBaumann 3 ай бұрын
@@zoran-horvat What are the names of the older videos so I can find them?
@zsoltesse987
@zsoltesse987 3 ай бұрын
@zoran-horvat this is such a valuable lesson! It really is. Also an interesting one. But watching your video makes me feel bad. Not (only) because I'm ashamed that I'm not doing things right yet, but because I'm struggling to follow you, even though most of the time you're not speaking fast at all. I watch your videos at 0.9x speed, but I still have to watch some parts several times. I think there are several "problems": 1) a) The way (animation?) you add new code is usually too fast. Also, you're talking about something else at the same time (I mean, you're not reading the code you're showing). 1) b) Your code is not in the usual style. Which is fine, because you often show it to help us learn good coding styles. Just be aware that many of the viewers will find it a bit hard to read, they will need time. 2) Your rhythm is a bit too fluctuating. I know it can be a rhetorical tool, but sometimes it makes it "challenging" to follow you because it's so unpredictable. 70% of the time I could watch your videos at 1.25 or 1.5 if it weren't for the visual part, but 30% of the time is almost too fast (especially for non-native English speakers). Please consider changing your presentation style a bit.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@zsoltesse987 I'm changing my style all the time. The points you make are valuable, thank you.
@shadeblackwolf1508
@shadeblackwolf1508 3 ай бұрын
I think booleans can work in the model, but in this case, is_released is a derivative of the release date, is it not? we can add an isReleased method by checking if the releasse date is a forecast or a date in the past, which can be implemented by comparing it to now. And when you bring in the nullable, it's starting to smell like Release needs to become polymorphic. To talk an example from my own experience, as part of a customer object we model a list of services for that customer. The services have a 3-value semantic. They can be present and active, present and inactive, or not present. Present and active means the customer can use the service. Present and inactive means that the customer is eligable for the service, but they do not have it, and absent means the customer is ineligable for the service. Currently this is modeled as a list of Service objects, that each contain a boolean state indicating if that service is enabled, and it's been servicing us well. The main upside this has, is that the services themselves are data, and as such can be added and modified semantically without development impact
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@shadeblackwolf1508 On most occasions, a bool is a good answer to a question, but an incomplete representation of the state.
@ferinzz
@ferinzz 3 ай бұрын
I think that for his example to apply to your system the bool would be representing some other value. For example if active was tied to a contract with an end date. In that case you could/ should derive the bool from the presence of that end date as well as whether it has been passed in time or not. instead of declaring a bool and also having a contact end date.
@ParkourGrip
@ParkourGrip 3 ай бұрын
Ok. Now I'm curious on how you would store this model with a tagged union in a relational database. Would the relational model have a bunch of nullable attributes with custom constraints that ensure that only the fields of 1 and only 1 tagged union variant is not null? Or would you have multiple tables, one for each tagged union variant, with 1:1 relationships to the "main" table?
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@ParkourGrip You can store them in the table with the containing entity and a discriminator. It works very well. On the other hand, "a bunch of nullable attributes" already speaks about the feature's complexity we are dealing with here. It's not bool. It is several possibilities (more than two for sure), each represented with a different set of attributes.
@rotamrofsnart
@rotamrofsnart 3 ай бұрын
Is there any literature or article about this? I found it a bit hard to grasp and I would like to learn it at my own pace.
@zoran-horvat
@zoran-horvat 3 ай бұрын
There are many books that would help you: Martin Fowler, Eric Evans, Bertrand Meyer, etc.
@SteinGauslaaStrindhaug
@SteinGauslaaStrindhaug 2 ай бұрын
As a fronted programmer I tend to always assume every field can be null or wrong type because the data comes from a server I don't control or a browser API I cannot control or worse from an incompetent user. So I always write code to handle nonsensical combinations of data. Assuming the data to be correct because you designed your data model to only allow sensible configurations is a luxury you only have if you're not having a database, a network connection, or user inputs outside your control. Also unless a boolean is the completely wrong type to use; I'd say that any combination of bools and nulls are possible and to be handled even if they are unlikely. E.g. a book can be published but lacking a (known) publication date. So having isPublished == true and publication date==null is something you should handle.
@zoran-horvat
@zoran-horvat 2 ай бұрын
You are speaking of data sanitization, which belongs to the system's surface, not interior. Once the data are in, they must have passed validation already, so there is no need to repeat any checks again. In strongly-typed languages that check is already performed on the assignment for you, so you design sanitization via the design of types. In persistence, schema validation performs sanitization for you before materializing the object graph, leaving nothing to you to repeat. Even in weakly typed languages, separating sanitization from domain code is a generally preferred design. Skipping that step would inflate your code by a factor of 10 with no benefits whatsoever.
@SteinGauslaaStrindhaug
@SteinGauslaaStrindhaug 2 ай бұрын
By bool being the wrong type I mean when you realise you need multiple bools that are almost always evaluate together, like isShipped isReceived isCompleted etc. Where most combinations are invalid you probably want an enum instead. Only use bool if there only 2 states and they are always possible to be set in either state independent of other fields. IsArchived is fine as a Boolean since you can decide to archive something at any point in the process. But anything modelling a sequence of steps is probably not a set of booleans.
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@SteinGauslaaStrindhaug Where do you implement a domain operation when the state is represented by a primitive type?
@impero101
@impero101 3 ай бұрын
Could you make a part 2 where you demonstrate persisting a model such as this one? Both using an ORM with a relational database, and with a document database.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@impero101 I will be making a few other videos that demonstrate that as well. But note that there are probably a dozen of videos already on my channel that demonstrate other aspects of the bookstore application, which use EF Core in persistence.
@timseguine2
@timseguine2 3 ай бұрын
I like your fixed model for the most part and I agree with the main point of the video. BUT, I tend to be very "vibes" driven when it comes to programming so I often "know" something is "wrong" before I can explain why. And my experience has shown me it is a good idea to follow that feeling. So I actually had to think for a while why I felt your final model was (for me) ultimately unsatisfying. First the feeling: adding "isA" checks outside of the context of a statically checked variant type always feels gross to me. I know C# doesn't really have those yet, and that seems to be your intended model here even though the syntax doesn't really allow that. But even so, I object to it on semantic grounds. My objection is subjective and minor (in this case I am not sure it is relevant), but the issue comes if for some reason we get a requirement that needs a new type of PublicationInfo. Even then it could only bite you if that new type was also considered to have IsPublished == true. But if we follow the principle that things which have changed in the past are more likely to change in the future, then it seems reasonable to want to future proof this kind of extension. I'd tend to prefer from a type theory perspective that EITHER the set of concrete classes is limited explicitly at compile time OR that the classes are built with the possibility of another type being introduced in mind (the type hierarchy usually isn't closed even if all the concrete classes are sealed). And in this case that requires IMO that the behavior of PublicationInfo is fully specified by its available methods. Concretely that brings me to what I would have written when you said what the new requirement was: First off I agree, the requirement seems to be screaming for a new type that encapsulates the two connected pieces of information. But in my opinion, the bool isn't so far off from what we really want. From a business logic perspective we want an IsPublished query. And it is the responsibility of the PublicationInfo type to provide that query (in this case without a backing field). so the other types would have: public bool IsPublished => false; and Published would have: public bool IsPublished => true; I could see an argument against it in this case that it is overengineered. But I get the impression that we are discussing a general principle and not this specific application. In any case I recognize there is a tradeoff. And the reason for me is that my suggestion seems less likely to break under maintenance.
@palapapa0201
@palapapa0201 3 ай бұрын
5:13 Why would need to add another bool to bring the total possible states to 8? I get that the point you are trying to say is that with a bool and a nullable, there are 4 possible states, but one of them is impossible but representable, which makes it bug-prone.
@Mothuzad
@Mothuzad 3 ай бұрын
> Oh, some clever rage bait? > It's actually valid and clever.... > Got it. It's a "make invalid state unrepresentable" video. But instead of Rust or Gleam, we're doing it with C#. > Subscribes
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@Mothuzad Did you see any rage in the comments? I see none. P.S. I may have not understood all too well.
@lukkkasz323
@lukkkasz323 3 ай бұрын
The Junior programming video was confusing to me, but this one made much more sense. I'm below junior level, my initial solution to this was to wrap both of these into into a Publication class, and also to replace the state with a method or even a simple property that just checks if the book is published, but I'm not sure if that was allowed here, because I don't know the exact requirements. Basically something like this: publication.IsPublished() => publicationDate < Date.Now(); Did I understand the video well (at least somewhat)?
@zoran-horvat
@zoran-horvat 3 ай бұрын
A touch of senior thinking: make the method receive the time when it evaluates. Otherwise, I agree with you. A bool is only an answer to a question, not the entire state.
@sf-petru
@sf-petru 2 ай бұрын
this is a nightmare to change when the logic is huge and the customer wants big changes
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@sf-petru Do you have an example of a "big change" that would cause troubles? I see irrational fear behind your words.
@sf-petru
@sf-petru 2 ай бұрын
​ @zoran-horvat first thing, I love your videos... but: this is a very simplistic example. In real-world projects, as the code grows, you’ll need extra parameters, logic will evolve, the logic will change and you have lots of classes that override a functionality that is supposed to change. You’ll end up having to change a lot, often without knowing what might break
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@sf-petru This is not a simplistic example - there are 20 types in it. Now, how would it help if I shrank it back to three classes? I don't want to repeat the video here. I believe I have already explained where that leads.
@sf-petru
@sf-petru 2 ай бұрын
@@zoran-horvat Apologies if I wasn’t clear earlier. What I meant is that the IsPublish methods are overly simplistic. With 15 years of experience in .NET, I’ve repeatedly encountered similar issues in large projects, especially those where specifications change frequently, and even the language versions and recommended patterns evolve. I really enjoy your videos and have learned a great deal from them. Your content is insightful, and I appreciate the value it brings, but I'm not always in the same boat.
@sf-petru
@sf-petru 2 ай бұрын
@@zoran-horvat Sorry, I wasn’t clear earlier. What I meant is that the IsPublish methods are overly simplistic. With 15 years of experience in .NET, I’ve repeatedly encountered similar issues in large projects, especially those where specifications change frequently, and even the language versions and recommended patterns evolve. I really enjoy your videos and have learned a great deal from them. I love your content, and I appreciate the value it brings, but I'm not always in the same boat
@colinmaharaj
@colinmaharaj 2 ай бұрын
I'm a C guy, there are bool and BOOL, I use both.
@johanavril1691
@johanavril1691 3 ай бұрын
Today I learn that c# doesnt have tagged unions
@zoran-horvat
@zoran-horvat 3 ай бұрын
But it has record types with a tag (sic!).
@templeofdelusion
@templeofdelusion 3 ай бұрын
The only problem I see here is that the bool is a field as opposed a method that checks if book was published... Surely you cannot be past the provided date AND have the book still be unpublished??
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@templeofdelusion That is one of the issues with bool. The same issue does not exist when there is an object that specifies the effects of the event that happened. For example, you can ask for the status of the published book at a time prior to its publication date, and it would not be published!
@RandomGeometryDashStuff
@RandomGeometryDashStuff 3 ай бұрын
10:16 Planned(date in the past)
@rahulmathew8713
@rahulmathew8713 3 ай бұрын
Why are u mixing business layer method and DTO properties in the same class Release. Isnt violation of SOC
@zoran-horvat
@zoran-horvat 3 ай бұрын
What do you mean by DTO properties?
@rahulmathew8713
@rahulmathew8713 3 ай бұрын
@@zoran-horvat the Release class we can see properties and methods
@alex38235
@alex38235 3 ай бұрын
This basically acomplishes the same thing as the typestate pattern right?
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@alex38235 Yes, it looks very similar. That is the coding pattern known in all languages and coding styles, primarily for its safety and clarity.
@felixnotthecat4249
@felixnotthecat4249 Ай бұрын
how the record Published would be represented in the Database. How would I map this to EF?
@zoran-horvat
@zoran-horvat Ай бұрын
@@felixnotthecat4249 You can include them in an entity as a complex property. There are a few later videos where I used that technique.
@felixnotthecat4249
@felixnotthecat4249 28 күн бұрын
@@zoran-horvat Thanks for answering.
@jakezepeda1267
@jakezepeda1267 3 ай бұрын
I hope this isn't a stupid question. What if instead you just omit the bool and use a nullable DateTime and compare that with current date to get the status? null = not planned > current = Planned
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@jakezepeda1267 Actually, a planned date may eventually become past without the book being published. But the problem is in other aspects of using primitive types, and especially nullable types: the caller must implement domain logic, that logic will repeat endlessly across the code base, and the code base will be inflated by a factor of 2-3 because of this decision. For all these reasons, I consider the winning solution one that wraps those values and associated logic into a dedicated type.
@makemeafirewall
@makemeafirewall 3 ай бұрын
This is great, please explain how to use this with ef core, how should we use the polimorphic publishing state in the DB?
@zoran-horvat
@zoran-horvat 3 ай бұрын
Polymorphic state is not an issue, because EF Core supports that out of the box. A greater issue is when you develop an immutable model. That is where EF Core falls short. You must either add a few utilities to the DbContext to support immutable models, or use something else.
@CrapE_DM
@CrapE_DM 3 ай бұрын
Not how I assumed that would go, since I made an assumption that apparently you didn't want to make. I assumed that if the date was in the past, it's published, otherwise not. I understand that a planned release date can come and go without it actually releasing and the data being updated, but that's still kind of the case, where a Planned release may come (this time it actually gets released) and the data doesn't get updated to Released. But, if you'd prefer to live with the former issue over the latter, this is definitely a better solution. If you prefer the latter, it gets even simpler, since you can just stick with an optional release date.
@zoran-horvat
@zoran-horvat 3 ай бұрын
Have you considered tentative analyses? Like querying the library at the end of this year, for the purpose of projecting the revenues? How about doing it backwards, querying the data in the past, so to assess the predicting algorithm on the future that is already past? After a quarter of a century working on enterprise software, I'll tell you there is no business that doesn't ask for auditing and analysis before end. Will you go with "simple" designs for years only to respond with "but we can't implement that"? I witnessed that too many times.
@aredrih6723
@aredrih6723 3 ай бұрын
I've made the same assumption as CrapE_DM and I'm really unsure where the disagreement lies. Is it about the fact that an announced book will automatically release if you don't manually update it ? (And lead to misinterpreting the data) If the goal is to allow for retrospectives, then yeah, the date alone won't be enough but both models rely on overriding outdated data to get the up to date state and aren't a great fit for retrospective (you'd lose information on which book were planned to release). IMHO, event sourcing is the gold standard for retrospective and having a talk about data model either suggest you're dealing with aggregates derived from an event stream (and save the stream for eventual replay) or care little about retrospective. Personally, I would take the contrast between announced and released dates as a justification to have 2 date fields rather than reusing one with a boolean flag. (You could even make the case to add a third date field for the date the book enters the library offering)
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@aredrih6723 The splitting into two date fields happens naturally when a discriminated union is applied. That opens for another improvement, depending on requirements, as one of the dates might have a different precision than the other, so essentially their underlying types would end up being different.
@JohnSmith-op7ls
@JohnSmith-op7ls 3 ай бұрын
This is good but I think your examples would be better if they presented any domain specific knowledge or example specific business logic up front. You’re trying to demonstrate why a certain code design is better or worse, if we have to know that a book’s published date doesn’t inherently tell you if it’s been published or not simply by checking if the date is in the past or future, or even null or some default which takes the place of null, then you’re trying to learn and understand some business logic while trying to understand why one solution is better than another. I understand that an example might require some complexity in the scenario to illustrate the point, but trying to explain the code and business logic incrementally, going back and forth between the two, is more complicated than presenting the full problem, then going over the solution. This also comes into play when you ask the audience early on to look at the given code and try to spot the problem. We haven’t been told yet what exactly the code should be doing. So unless we understand how the book publishing business operates, we probably aren’t going to spot issues which require such insight. Also, showing completed code blocks, then describing them is a lot easier to follow than watching characters being incrementally typed, while you’re talking. We read in words and blocks of words, not letter by letter. And our focus doesn’t truly multitask. Presenting information character by character while also trying to process speech makes both a lot harder. Letting us first read the information, then listening to explanation follows the way our focus operates, sequentially. Great topics, just wanted to provide some ideas from a former teacher of 7 years.
@zoran-horvat
@zoran-horvat 3 ай бұрын
There is the problem with your proposition when it comes to convincing KZbin visitors to watch the video. However monstrous, the talk must put the cards on the table within the first 20 seconds or they're gone. A video that is structured as an engineer would do, is a certain disaster on KZbin. The algorithm would bury it within an hour. Now about the details. One caught my attention, so I will ask a question now: What is the state of a book with the planned date set, when that date is in the past?
@adambickford8720
@adambickford8720 3 ай бұрын
I agree, it feels very bait-and-switch. A boolean was perfectly fine until we increased the complexity of the problem. I subscribe to the exact opposite world view: premature abstraction is far worse than an informed refactoring.
@JohnSmith-op7ls
@JohnSmith-op7ls 3 ай бұрын
@@zoran-horvat Yes, you have to hook the audience quickly, but once you’ve done that, I think it’s better to provide the context of what’s being solved, then demonstrate how to solve it, then maybe cover problematic ways people try to solve it and why they cause issues. If required information for us to solve the problem on our own is given out incrementally, mixed in with incremental explanation of the solution, we can’t really solve it. It’s like trying to solve a maze while it’s still being drawn. Lead with an enticing hook but then take a more measured approach. I don’t think this will hurt you on YT. Once you get that first 30:seconds, YT counts the view and you can relax the pace. To answer your question: It depends on the business logic. Is a published date only set when the book, is actually published? If it’s set before publishing, can we just change it if the actual date ends up being different, or do we need record what we assumed the date would be even if it was incorrect. If so, two fields would be best, the planned date and actual date. If we don’t care about our initial estimate, just update the date if it changes. If we don’t need to know when it might publish, only when it actually did, leave it a default min date time stamp or whatever your default is (or null). Having two dates provides the most flexibility if in the future you want to see how accurate publish date estimates were and flag projects that were way off. But you can’t get into building out things first hypothetical requirements that never happen, wasting time and increasing technical debt. Or am I misunderstanding the question?
@JohnSmith-op7ls
@JohnSmith-op7ls 3 ай бұрын
@@adambickford8720 I agree, if accommodating future flexibility is free or requires very little effort, wait until you know you need something. And I mean when dealing with pure hypotheticals, not when the boss says there’s a 75% chance we’ll need to do X in 3-4 years. Odds are even if you know vaguely that you’ll need to accommodate something non-trivial later, whatever you do now will need to be heavily anyhow. Best to build to real requirements later than to guess about them now. Otherwise, where you stop with the guesses, and do you really have time now to accommodate them, much less maintain them if they don’t get used? Probably not.
@adambickford8720
@adambickford8720 3 ай бұрын
@@pl4gueis But it wasn't 'proper' until the goalposts were moved. Overengineering is solving problems you 'might' have but don't "yet". Go with the smallest, simplest thing that works. Once you have new *actual* requirements you evolve the design. You may very well end up at a very similar place, but don't pay for it until you need it and know its the proper abstraction. Digging through factories and type hierarchies to find the 1 actual implementation is not 'more proper'. ymmv
@holger_p
@holger_p Ай бұрын
Well, I get "don't store something, that can be computed from other properties", that's normal. But I do not see the smallest difference, to using an integer and testing it for 0 and 1 or maybe a third state. Sometimes an enum is better to read, if we are unsure what "true" and "false" actually mean, it shifts the information from the variable name to the value, which is more robust.
@zoran-horvat
@zoran-horvat Ай бұрын
@@holger_p The point of the video is in something else, not just in data representation. The problem becomes visible when I ask you where the behavior is implemented in that design.
@holger_p
@holger_p Ай бұрын
@@zoran-horvat I don't see anything, that "bool IsPublished => PublishedDate != null" wouldn't solve. And I don't see any difference to using "int IsPublished" and testing "if (IsPublished==1) What you describe is not at all connected to the datatype "bool". Nothing in your video changes, if you replace bool with int or enum. An enum Flag {no, yes} definitly shows the exact same behaviour. I think you want to point out a design problem, but people are confused with a title like "DataType boolean is bad"... without any narrowing context. .NET is full of boolean properties and it cannot be generally bad design, otherwise somebody had already changed it.
@zoran-horvat
@zoran-horvat Ай бұрын
@@holger_p It is connected to bool because it soon turned out that there are more than two representations. Aldo, it had to do with bool in a sense that true value comes together with another piece of data and the false value with yet another piece of data. However, the most dramatic deficiency of a bool is in implementing behavior that depends on it, so I will have to repeat the question: Where is that behavior located?
@holger_p
@holger_p Ай бұрын
@@zoran-horvat Obviously we live in different worlds. Each "if" requires a bool. World cannot live without bool. You make your video like a quiz show. "Do you see it, Do you see it, Where is it". Over time this style of teaching can become annoying. It makes people feel like an idiot, if they don't see anything. Maybe you refer to code everybody knows. What is wrong with "Rectangle.IsEmpty". Why is this bad design ? Why this is "not my friend". if (rectangle.IsEmpty) if (rectangle.Width==0 && rectangle.Height==0), very same thing, just longer, no difference in design. You want to tell us you write bool-free applications ? That's weird. But that's what the video title suggests. I think there are a lot of side-conditions, under which you want to avoid bools, but you don't speak it out loud.
@zoran-horvat
@zoran-horvat Ай бұрын
@@holger_p Wrong answer. Think again: If there is a state represented with a discrete type, such as a bool, then where is the behavior implemented? Stop answering some other questions and focus on this one, because I see a blind spot in you regarding that particular issue.
@TheDiymovies
@TheDiymovies 3 ай бұрын
Only if I could come to these realizations on my own as easy as you are able to explain them! Excellent video, gives me much to think about!
@matheosmattsson2811
@matheosmattsson2811 3 ай бұрын
Good video! Quite dumb/irrelevant question but here it goes: I have noticed "Modern day C#" code uses more and more type checking using the "is" operator, as you do at the very end. I remember from my days in school, using Java (1.8), that the teacher's strongly discouraged type checking (in Java at least) as it was said to have performance drawbacks as opposed to, I don't know, using a boolean in the parent class to indicate one of two concrete implementations (ironic jokeful example). Is this kind of thinking outdated or was it a myth all along, or could it be that it was something Java specific and has never been a big deal in C#? This springs to mind everytime I typecheck nowdays in C# :P
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@matheosmattsson2811 Type checking is quite fast nowadays. It was reimplemented early in the C#'s history and you don't have to fear it in C#. However, type checking is not common in OOP - it is a design flaw more often than not. Type expressions are a native part of functional programming, where it is one of the principal ways of implementing polymorphism. You will note that I am applying type checking to record types only, and not to regular classes in the demo.
@matheosmattsson2811
@matheosmattsson2811 3 ай бұрын
@@zoran-horvat Thanks for the answer! How come you only type check for record types? is not the ´record´ type == ´record class` --> syntactic sugar for creating an immutable class with automatic handy implementations for Equals, ToString etc? Why would the "manual" class be discouraged in terms of type checking, if I may ask?
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@matheosmattsson2811 That is how they are implemented, but much more is in the intended use. The record is not supposed to contain behavior, only publicly accessible immutable components. From the design point of view, a record is a functional type.
@matheosmattsson2811
@matheosmattsson2811 3 ай бұрын
@@zoran-horvat Okay, right got it. Just asking if there was some "hidden" performance related difference which I was missing. Thanks for the clarification
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@matheosmattsson2811 My blind guess is that the cost is very small compared to common tagged unions. Performance benefit of tagged unions is that the tag is compile-time constant, and so the compiler can construct an ideal calculated jump. Classes in C#, on the other hand, have a two-byte type identifier. While it is still just a number, it is not known in advance and so a more costly branching is required. All that unless there is some wild optimization in C#, the existence of which I wouldn't dismiss lightly.
@AlFasGD
@AlFasGD 3 ай бұрын
I hope the next one is about tuples not being your friends
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@AlFasGD Tuples are your friend. Enums are not.
@adambickford8720
@adambickford8720 3 ай бұрын
@@zoran-horvat What's funny is your `PublicationInfo` would be an enum in java.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@adambickford8720 Actually, Java has extended enums to become discriminated unions. C# has records for the same purpose, and will soon attain proper discriminated unions with much cleaner syntax. Rust supports very clean enums with state that look close to the discriminated union syntax proposal in C#. Either way, all those are the same design. The only design that wouldn't work is the C-style enum.
@adambickford8720
@adambickford8720 3 ай бұрын
@@zoran-horvat Java has recently added sealed classes for that purpose. afaik enums are the same as they ever were
@tk36_real
@tk36_real 3 ай бұрын
PublicationDate in the future or null == planned PublicationDate in the past == published way easier…
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@tk36_real Planned publication date in the past is still inly a planned publication date. The book will not get published by waiting for some date to pass. It doesn't work that way. Testing for null in dozens and hundreds of places in the calling end is a big red flag for the entire design. It goes the same with testing a bool for true, comparing a date to the current day, and myriad other procedural artefacts. The calling end is not the place to implement domain logic.
@tk36_real
@tk36_real 3 ай бұрын
@@zoran-horvat a planned publication in the past that didn't go through but is still in the dataset is a problem of validation! and if you think all programmers are too incompetent to handle null being a meaningful value you could a) put a "maximum date" (semantically means indeterminate in the future) or b) provide ONE utility function to wrap it into a polymorphic type like in your video!? you're just making excuses to overcomplicate the API while making the actual date inaccessible…
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@tk36_real What do you mean a problem with validation? You cannot alter the data just because you don't like them. We plan it for tomorrow. Two days later the data says we planned it for yesterday. It is a fact, and the database says it.
@tk36_real
@tk36_real 3 ай бұрын
@@zoran-horvat you cannot plan to release something in the past! if the data is there already you SHOULD alter it to "unspecified future release" and if you need the history put the old planned release date into a database or something
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@tk36_real Is it possible that your thought process is so broken? Am I right to suppose you are a senior programmer?
@DovydasGrigaitis
@DovydasGrigaitis 3 ай бұрын
This channel is pure gold!
@Ratstail91
@Ratstail91 3 ай бұрын
Ha! This reminds me of the cuckoo in a creature-collecting game I made. Because cuckoos in nature are brood parasites, I thought it would be clever to have their eggs obtained in a different fashion... from that point on, suddenly I had to double check in about a dozen places to see if I was dealing with a real egg, or a cuckoo egg in disguise. Whoops.
@zoran-horvat
@zoran-horvat 3 ай бұрын
That is the most fragrant point which herds of senior programmers somehow fail to see: the same if branching over and over again, plaguing the entire code base, and for what purpose?
@Ratstail91
@Ratstail91 3 ай бұрын
@@zoran-horvat It as only about a dozen files that I had to double check lol. One instance in over 4 years isn't bad.
@dangdudedan8756
@dangdudedan8756 2 ай бұрын
virgin boolean vs integer you only change to 0 and 1
@andreapuerto8967
@andreapuerto8967 3 ай бұрын
Yes he is, I even invited him to my wedding.
@stan_yolo
@stan_yolo 3 ай бұрын
So many pain and cringe. Just use enum.
@zoran-horvat
@zoran-horvat 3 ай бұрын
In C# - no. If you thought Rust-style enums, then I already did. So, what's your choice?
@stan_yolo
@stan_yolo 3 ай бұрын
​@@zoran-horvat may be I was upset when I write at that comment. Topic of this video is important, but all this was said by F# guys in 2015. From the "informatics" side - all struggles is because you want sum-type, but encoded it by product-type. Old languages hides from programmer the fact that type is a proof by itself. IMO there should be no samples with awful code with defaults, branching on ambiguous semantics etc. Reasoning should be simple: date and flag is highly dependent/coupled, then is must be one type/entity. Whoever wanted to understand - understood. Whoever wanted to learn programming - learnt Rust/F#/Haskel/math/whatever
@LeonardoVargasL
@LeonardoVargasL 3 ай бұрын
No way!!! This is gold! This is poetry! I have no words to describe a magnificent lesson learned in this video. I can see now how to handle any crazy new requirement from a customer/product manager.
@johnwostenberg840
@johnwostenberg840 3 ай бұрын
Even better yet, use a language with discriminated unions and spell out the various possible states. “Make illegal state unrepresentable”
@chrisjones9132
@chrisjones9132 3 ай бұрын
I think I walked into the wrong video. I’m a C and asm programmer.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@chrisjones9132 Why do you think this is not applicable there? You would still use structs in C and keep related data together in assembler as well. On a related note, I remember doing dynamic dispatch in assembler with no trouble, whereas C was the testing ground for it in the early days of C++. It is wrong to attribute these techniques to a programming language, in my opinion.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@edwardfanboy Don't mix the concept with its implementation. You *will* use a hand-made v-table in assembler because of its conceptual value and utility, despite the potential to err. You can make a myriad of other errors, too, which are detected easily by higher compilers and impossible to make in many other languages. But that potential will still not retire the assembler. It remains useful in its usual niche.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@edwardfanboy My point is that if you are working in a language where sum types are not supported natively, you will still not choose to repeat dozens and hundreds of identical branching blocks on the calling end, allow combinatorial explosion of operations, inflate your program by a factor of two or three, and then blame the language for that. I've been working in assembler and in C for years, so I know very well what I am saying.
@facundoayosa8052
@facundoayosa8052 3 ай бұрын
I love your videos, I feel like they ooze in profound knowledge. The thing is I'm too junior to understand 99% of it. I'm fluent in Javascript and barely touch Typescript so you'd understand why. I'll subscribe and read some books until these videos make sense.
@Leftysrev3nge
@Leftysrev3nge 3 ай бұрын
We out here booing Boolean
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@Leftysrev3nge Nope.
@kawashirov
@kawashirov 2 ай бұрын
I simply would do public PublicationDate? ProposedPublicationDate; public PublicationDate? ActualPublicationDate; lol
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@kawashirov lol show me the calling end.
@naughtiousmaximus7853
@naughtiousmaximus7853 2 ай бұрын
​@@zoran-horvatWouldn't frontend have input field where date for planned publication would be entered and after that some kind of confirmation buttons (PUBLISH/UNPUBLISH for example, just to ilustrate the point) that confirms if book was published when it was planned or not? A POST request for each button can contain book id and if the book was published (PUBLISHED) or not (UNPUBLISHED). Database can for example just have a Id column, Date column and enum with (PUBLISHED/UNPUBLISHED) related to it (BookId: 2167, PlannedPublicationDate: 26.8.2024, PublicationStatus: UNPUBLISHED).
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@naughtiousmaximus7853 You have mentioned all parts of the software except the backend. Does the backend exist and what is its role?
@naughtiousmaximus7853
@naughtiousmaximus7853 2 ай бұрын
@@zoran-horvat Maybe in this case backend should just be a layer for data validation and updating? Using something like FluentValidation. Database in this case seems like excellent way to do decision making for this scenario. Basic flow would be frontend -> api endpoint -> validation of data -> service that has dbcontext or repository in constructor -> method that does CRUD is called based on received data -> save changes to the database.
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@naughtiousmaximus7853 So you have built yourself a CRUD service. No business rules, no legislation, no nothing. Could be a good school project.
@johndowson1852
@johndowson1852 3 ай бұрын
Look at what they need to emulate a fraction of a proper algebraic type.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@johndowson1852 What? It's one line per variant in C# and one line per variant in F#, as well as one line per variant in Rust. You are exaggerating, and that might be on purpose.
@johndowson1852
@johndowson1852 3 ай бұрын
@@zoran-horvat yes, I am exaggerating for a comedic effect, but my issue isn't in LOC, it's in the fact that sum types are a closed set, while implementors of an interface is an open state. Which means that while you've made invalid states unrepresentable, you still can introduce a bug by forgetting to handle one of the valid states.
@zoran-horvat
@zoran-horvat 3 ай бұрын
@@johndowson1852 I have implemented a sum type with records, not with interfaces.
@Justin-wj4yc
@Justin-wj4yc 2 ай бұрын
@@zoran-horvat you inherited form PublicationInfo. so it's an open set
Master the Design of Classes
11:56
Zoran Horvat
Рет қаралды 34 М.
You May Have Never Learned This Lesson Right
13:02
Zoran Horvat
Рет қаралды 12 М.
За кого болели?😂
00:18
МЯТНАЯ ФАНТА
Рет қаралды 3,2 МЛН
coco在求救? #小丑 #天使 #shorts
00:29
好人小丑
Рет қаралды 34 МЛН
Master the Design of Functional Behavior in C#
19:17
Zoran Horvat
Рет қаралды 13 М.
Val - The Rust Killer | Prime Reacts
16:54
ThePrimeTime
Рет қаралды 96 М.
How to Avoid Null Reference Exceptions: Optional Objects in C#
18:13
My Favorite Code "Anti-Patterns" (Break These)
16:52
Conner Ardman
Рет қаралды 56 М.
"You're Doing Validation Wrong in .NET" | Code Cop #023
14:44
Nick Chapsas
Рет қаралды 40 М.
Why is C# Evolving This Way?
15:02
Zoran Horvat
Рет қаралды 23 М.
17 Pieces of C# Syntax That Make Your Code Short
12:41
Zoran Horvat
Рет қаралды 26 М.
Replace Is Number Saves 440GB A WEEK
9:54
ThePrimeagenClips
Рет қаралды 310 М.
AWS CEO - The End Of Programmers Is Near
28:08
ThePrimeTime
Рет қаралды 550 М.
За кого болели?😂
00:18
МЯТНАЯ ФАНТА
Рет қаралды 3,2 МЛН