To check everything else together or fail fast: I think it really comes down to the application itself. Most of the time, as a user, I would be infuriated, when I'm gettin a new error over and over again, like why didn't just give me all to errors at once, so I can fix all at once :D The only exception is when an error is caused by something that was previously empty for example, that could still cause new errors, but I'm ok with that.
@@adambickford8720 With deferred execution (using yield) the caller can decide whether to stop after the first error or continue doing all of the validation.
Nick, I was waiting for you to mention "parse, don't validate"! If you havent read the article, I really recommend it. The final code was so, so close. If only the function returned a proof that the thing has been validated, all the code can then be written against the validated type. So in this example: `public static Validation Validate(User user)` Now, User usually refers to the type in the domain and the input is the unvalidated type, so it would be more like `public static Validation Validate(Command.User user)`. This is for all the lost souls that happen to come across this comment :')
forget about validation package you use, it doesn't work anymore the thing I use the best one it's the tValidation only with that package in my course u find hapiness and success.
I hate errors which only contain the error message as the identifier, because the error message may change. Sometimes you need to handle certain validation results in a special manner, especially on the frontend. I personally always make my validation results contain a human-readable identifier such as "WrongPassword", along with a description.
For HTTP you can't go wrong with errors as return values. Google's HTTP best practices documentation does a good job steering you towards the pattern you've described. I would also recommend including a unique, hardcoded error key (12-length hex character strings is what I prefer) that makes source code searches trivial for in investigating the source of the error. You can have more than one "Missing X field" errors in a project, giving each one a unique key is important when users report a new 400 error where there wasn't one before.
Yeah too bad that I'm almost begging to MS (via a github issue) to extend the ValidationResult or - even better - make it customizable so that we could add any kind of object to a validation error: without any success so far.
IMO errors should always contain 3 things and preferably 4: 1. the path to the thing that is failed (if you are validating json input, json path expressions would be good for this; there isn't really a good spec for generalizations though so just be self consistent) 2. an error code that is easy for a human to identify and distinguish from other possible errors (eg not SYS-1234 but something like EmptyStringValidation) and is the same for the same error throughout the system and effectively never changes in any past or future version 3. error context data (actual value, minimums, maximums, etc; also the shape/schema of this object should never change over time) 4. optional but preferred: a string description in the primary language of the userbase (or if it is identified, in the language preferred by the user) It is also best if a validation always returns as many identifiable errors from the input state as possible. Even though this might be more computationally expensive up front it generally saves time and processing vs a fail-fast scenario where each error is returned one at a time and the user repeatedly submits their fixed input to get the next error.
@@temp50 we use a coworkers library in our code so our way to do it is similar, but insted of Errors.New we put the errors in the domain. So it would instead be return new User.Errors.InvalidEmailAddress("Error message") the errors themself are basically just a record that inheirts from a record called ErrorBase
I think the Code Cop series is losing its way. In the example post, the list example is clearly superior. Sure there are even better ways, but most of the alternatives explored don't have the very basic functionality of returning every individual error without combining them all into a single string or exiting as soon as the first error is encountered. Also, I think Nick has way overcooked the cost of allocating a list vs using yield. I get the impression that he gets into perf sensitive code a lot from his videos, but for the vast majority of LOB apps, it's a total non-issue. Just because the "good" advice can be improved, doesn't make it bad advice.
I feel like fail fast or not is an application concern. If you are on the server-side, then you just need to know if the thing is valid or not for insertion. But, if you are returning a message to the user, then you want every error listed there. That's why I'd rather make the method ready for both cases using IENumerable as a return and a "yield return ValidationError(errorCode)". That way you could go for a "Any()" call if you want the fail fast approach or a ToList if you want to return all of the errors to the user depending on the use case.
I prefer to apply functional concepts to the domain, instead of building my domain on top of a functional library. So in this case, I might have a UserForm type, that has a Submit method which returns a Submitted type, that has two properties User? and Errors?, which are null annotated to be not null when SubmittedUser.IsValid is true and false respectively. TreatAllWarningsAsErrros means that the compiler knows and enforces that you submit the form and check validity before accessing the User or Errors properties. Now I don't have to explain to devs unfamiliar with function programming what a monoid is, or why our code uses Lst instead of List
I think in this example, the idea of not failing fast seems to have to do with something like form validation rather than, say, precondition checks. If you find all the issues at once, it's possible to annotate the UI with all the errors at the relevant place, allowing the user to correct mistakes without having to try again repeatedly until no errors are found.
WHY is no one ASKING for more Fucntional episodes!!! Yes more functional concepts, please! That stuff is so f***ing cool! You have teased us before, but is it time for the Monad explanation?? What does it even mean??
A monad is just a monoid in the category of endofunctors, what's the problem? (that's a meme, but it's not as absurd/crazy as it sounds) " [...] a monad is a type M for which the following functions are defined: Return-Takes a regular value of type T and lifts it into a monadic value of type M Bind-Takes a monadic value m and a world-crossing function f; extracts from m its inner value(s) t and applies f to it Return and Bind should have the following three properties: Right identity Left identity Associativity" taken from Functional C# by Enrico Buonanno (worth a read on Manning!)
@@thethreeheadedmonkey not a bad explanation, fairly pragmatic. by the way is your name a reference to Monkey Island? cuz if so you're my kinda people.
@@thethreeheadedmonkey out of context of the book that is a circular definition. If someone doesn’t know what a monad is, they aren’t going to know what a monadic value is either, lol. Nick is good at explaining complicated concepts. I think it would be a good edition. … my category theory is a little rusty, but a monoid sits right there between a group and a semigroup? And endofunctors are functors that map to themselves? Yeah, Nick should totally explain. I’m waiting patiently.
@@WillEhrendreich Haha, it absolutely is a reference to Monkey Island! Not my explanation, though - that's from a book I read and all credit to the author (who does an impressive job with many examples in said book).
I think that there are so many little class nooks and crannies in .NET, especially with the plethora of nuget and github packages out there, that videos like this are very useful. I wish there were more. Good job, Nick.
7:20 why do you need to do extra hoops to return null instead of just empty enumerable? This approach makes consumers make unnecessary checks which also invalidates why you have IsValid flag in the first place.
Returning null would be good if it was an out parameter. Something like bool IsValid(model, out IEnumerable? errors) To avoid the nullability warning, there is a neat [NotNullWhen(false)] parameter attribute. That’s how it’s done in dictionary’s TryGetValue for example. But here with a tuple, I agree that having a nullable errors causes a double check to address the nullability warning, and you can’t do anything with that. Allocating an empty array is not ideal either imo.
I just don't get why it doesn't return something like ICollection (or the actual type). It would make it obvious that you can get the count without worrying about the enumeration behind the returned value.
@metlov You can"t do any async with out, like database validation for email And btw if you return Array.Empty() it's optimized to not allocate anything
Interesting, good video, thanks. I don't see a horrible problem with empty or null errors meaning no errors were found. There comes a point where a consumer needs to be intelligent enough to follow a spec or interpret or code for the result.
Static function cannot be dependency injected if desired in the future. I'm glad compilers don't give one error at a time. That is, send the user a full set of available info.
Best practices when writing APIs in C# dictate returning all validation errors for data input to an API. This approach has been standard for +20 years. I believe this is the first post in this series where Nick is obviously wrong.
I'm definitely on team validate everything and not fast failing. Think about everything that goes on under the hood when a web request is made. Theres an enormous amount of work going on before you even receive the request object. Its far more efficient to validate the entire request instead of validating one thing at a time. Users also vastly prefer knowing everything that is wrong instead of fixing one thing, only to be then informed about the next problem after the next submission. Maybe theres a more performant way to validate instead of always creating a list, maybe lazy load it when an error is first encountered, maybe pass in a stringbuilder, there are many ways. But still, in the end, taking the entire user experience into account, its far more efficient to validate the entire request instead of fast-failing
I agree, but there then I would put a side condition of a silent notification, put alerts everywhere, but don't hinder the workflow. If there are 5 lines wrong, tell the user all the 5 lines, not after one is repaired, you tell about the next. And don't use popups ! User doesn't want to confirm to have read 20 messages one by one. Building a list, display one popup would be essential.
For form validation, you need to put the feedback next to the each element of the form, so none of these mechanisms are suitable. So maybe your use case is a server validating its inputs before processing them. If that's the case, I'd consider 'parse, don't validate'. The server receives an HTTP request and attempts to parse a (valid) User out of it. If the parser returns a User, the server goes on to process it. If the parser doesn't return a User, it returns an error and the server handles or returns that error. It doesn't much matter how the parser signals errors - exceptions work fine for this case, as do Either- or Result-style discriminated unions. What I would try to avoid is the possibility of constructing a User object that might not be valid.
@@lordmetzgermeister I love algebraic data types as much as the next man but in this particular case, the server’s likely to just bale with a 400 status response. I agree exceptions have no place in form handling - apart from anything else, form validation failures aren’t exceptional.
Hey Nick, thank you for your video. As you mentioned, i would really like to see more functional programming by you, because it catches my interest when I sneaked into GO. Thanks
The "more of a functional developer" just threw out the List monad for multiple errors and replaced it with the Either monad for a single error. In my opinion this (and many other episodes of code cop) is just splitting hair.
Well the list of error is a better approach than the previous boolean return, to make it more expressive, he could create a ValidationResult type containing the status (validated or not) and the list of problems when validations failed.
the difference between yield return and creating a list is minimal. to consume the IEnumerable the caller has to convert to a list or array anyways. overall I think the suggested approach by the user good. It would be even better if using an enum for the error class plus a message string to make it easier for the caller to handle it. So honestly I don't get the point.
The difference is that the list allocation still happens even if there are no errors. Also to consume the Enumerable you need to loop through it, not necessarily convert into an array or list. Why would you?
I like value objects for validation. 'Validation' just becomes the attempt to construct a UserName instance. Allows reusing the same logic in the create/update endpoint. Prevents duplicating domain logic vs validation logic. (public static Result Create(string? value))
The DataAnnotations Validator class returns an ICollection of ValidationResults when calling TryValidateObject and I don't see why we can't do the same. However there is a boolean parameter to control when to validate all properties just as FluentValidation has a parameter to control whether it will return immediately or not upon encountering the first exception
Isn't there valid justification to return a list of all errors to make your API more user friendly, and relay everything wrong with the request that was received? I can see scenarios where end users get frustrated with an API not giving them the whole validation picture, forcing them to discover through trial and error what a valid request should look like.
There absolutely is. FluentValidation does, and it's great. But that's only for basic input validation (!) at the user-facing edge of the system. Past that point, fail as fast as possible.
personally I think its case to case. If you are filling out a form then I think validating all errors would be the best. If its something else, lets say a button that does something to a specific entity then just showing one at a time is better
Post is comparing Validate with IsValid which are two completely different things. Personally, if I'm writing everything from scratch (mostly library code), then I prefer the IsValid approach (one per validation rule), because it's compliant with Single Responsibility. For application code however, we have Fluent, so no point in reinventing the wheel, especially that we have a much broader enumerable of rules to support.
Since you showed in your channel the OneOf library, I tend to use this when I do my validations as well as returning the different objects to an endpoint. But I think that with the LanguageExtensions library the name is way more intuitive, so I'll give this a try. Nick, do you think these libraries will be deprecated when C# incorporates discriminated unions?
Make Validate() an extension method of User to chain calls all along. Then you'll get user.Validate().Match(...) Although Match() should have the error first and the result second. Otherwise you can't continue chaining the happy path.
It can help if you're aggregating multiple levels of validation and error processing. Without that you'd end up passing the object around anyway. It's less common for sure, but makes the interface simple and uniform.
I really love to see how 'senior' devs enjoy overcomplicating simple things. It would be a nightmare to see the same devs' code for a complex business logic problem.
Function honesty is underappreciated, should not need to read how a method is implemented to be able to use it. Another overlooked concept is like Kent's new book Tidy First. So much code base is ugly, filled with IDE recommendations, spelling mistakes and out of date packages. So much easier to read and understand when your clean your workspace before making dinner like a good chef. Also turn on warnings as errors, basics first!
shouldn't be the error in monad on left side in match? because when input is right, answer is right.... :) also in definition you wrote Validation, i'm not familiar with library you use but i find it odd that if you define monad value type on left, it should be on left in Match.... if this wasn't an error ofc :)
The library author made some... unique decisions in how he structured this out. Nick has it right, but it's not intuitive that it would work this way, haha.
Had the same thought, but the docs seem to agree: in the Type, the failure is defined first, but when matched, the error goes second. Guess it depends on your world view, but I would usually go with Happy Path first. Been using the OneOf package for these kinds of things.
You are missing the point. Look at a large project with hundreds of validations and then you will understand how this particular extension becomes valuable.
Return is: again: depends. Depends on what kind of entity your API is suppose to talk to. Is it a backend service of a frontend application? Than you might wanna indeed list every invalid property. But if you are talking to other services, it is usually enough to response with a machine-readable "GTFO" and maybe log out the problem within service where the problem occurred.
I feel to see the practical difference between returning the monad over the validationResult. For me it looks similar but just a bit different in style? Am i missing something?
Given that LanguageExt has had some updates and i cannot seem to find good documentation for their latest release, i know i would love to hear you go over functional programming and monads and an in depth view of the LanguageExt package
I had a really hard time trying to use the LanguageExt package as it is intended instead of just for the monad types. I'm still interested in understanding it more but for now I've just written static Try and TryAsync functions and write result objects as needed without needing LanguageExt. It feels like a package I should know better but it's been difficult.
Hmmm a validation function that doesn't first test: if( user == null) And i would agree with most, if you validate one should get *all* possible errors that would invalidate the object being validated... Make no sense otherwise... Also why not have an ErrorCollection where each has detailed info for each error, then carry the validation status/flag for the validation collection. So... class ValidationInfo { { bool isValid =false; List allErrors = new(); } So one could have an ErrorInfo with detailed info, and a convenient way to get is the object is valid as well. Then whatever needs to interrogate and get all the errors if desired. So you allow is valid and all the errors if desired. Ah, guess i just learned about ValidationResult... :)
You STILL cannot act on the validation failure though, unless you have a switch statement with literal sentences as cases.... You need to return your errors as codes, enums, error types inherriting from a common type... something else than strings.
The problem with the aka good approach here is. What if you have hundreds of errors or hundred of things to validate? What if there is no good context? I think someone would have pointed that out. Maybe some people wants to be bombarded with a lot of errors showing on their face. But there's also the camp that fix error one by one. Which can also be bad if you have hundred of errors. That will take time.
I'd appreciate more content on functional programming in C#. I'm still a beginner what the paradigm is concerned. While C# is rather functional friendly, a lot of things are cumbersome to implement. Lots of boilerplate, so help would be welcome. I'm trying to use the LanguageExt package, but it's documentation is incredibly sparse. It reads more like a white paper, but I guess that figures. It basically assumes you know your functional stuff and the package's function signatures are incredibly confusing if you have no idea what exactly, for example, an 'Atom' - apart from the obvious - is supposed to do or be used for. Currently stuck with mostly Options and some collections. The Result type seems lackluster to me, but I might just not understand how to use it.
More interesting question is: how to pass validations constraints to OpenApi documentation in case when it is not implemented with data annotations, to prevent users from sending obviously wrong requests?
If you're using the Swagger package you can implement schema filter and change the properties for any model before the json is generated. I did that to read from custom attributes a project recently.
I still find utilising IValidateable object the best for validation. It encapsulates the validation logic into the model and keeps the code in one place.
What are the thoughts on static class vs. extension methods (yes, I know that's technically static as well). I.e. UserValidator.Validate(user) versus user.Validate()?
I wonder if anyone in .NET community tried something like Go enforces. You have shown that approach with tuple, but I'm just wondering if you did it in non-validation scope. Like, instead of int.TryParse(string input, out int value) -> bool it would be int.Parse(string input) -> (int value, Error error)
The problem I see with tuples is that there is no enforcement in implementing all validation in a large project in a uniform manner. After 6 months there is new code written by a new engineer with a new tuple as a result.
You know, I'm kinda split watching your channel. On one hand I appreciate helpful C# tips, being .NET dev myself. On the other hand, I feel like I need to skip almost a half of video to get this one tip that would be possible to explain in two minutes. The other half of video is promoting channel, private course etc. I would gladly buy your Udemy course where you would summarize all those tips. But I feel like watching your channel is more wasteful than helpful.
The example at start of the video.. they both look horrible, when all flows result in similar outputs and you're just evaluating conditions, that's the place to use a switch expression to begin with.
The monad thingy.. why? A guardian if is so much simple and easier to read. One of the first indicators of good code is how easy is to follow and understand the logic, without having to stop and spend unnecessary time figuring it, even if that time is 2 seconds. Code written by the 'sophisticated genius' developer is usually a pain in the ass for the others.
gotta say, you may have a point here. The resultant code is not immediately parsable by a a human unfamiliar with the library. I personally use FluidValidation but I am open for new, potentially better ways to achieve things.
Why both Tupple and ValidationResult response types are bad. Because from the contract you see it is perfectly valid to get that validation was successful and non-empty list of errors at the same time. ValidationResult is slightly better because it is impossible to create something like this without reflection but this is why a non-nullable list of errors is better.
No diss, but all are bad imho... Return a tupple, OK, no issue, but returning a list of error strings??? Mother of all... Have enum UserValidationErrors, return (bool,List). Last thing i want is to waste time/resources checking strings THAT MIGHT CHANGE down the road because someone felt like a period was actually a comma. Even better (if range of errors is known to be short and has no possible way of growing) use [Flags] to return a single composite value. Assuming it's int64, that 64 possible errors so should be plenty for most validations of this sort... That said, I'm not even sure most C# devs these days even know what a bitwise operation is, most don't know how to check for nulls that much is certain.
The expression "result.Match(Results.Ok, x=>Results.BadRequest(x.Head))" feels wrong on so many levels. I would read this as: if result is ok, call the bad request handler. Is it really supposed to be this way?
you can do result.Match(x => Results.Ok(x.Tail), x => Results.BadRequest(x.Head)) but that also seems strange since it's the error that's first in the type arguments, why is success first all of a sudden?
Why not just keep the validation method and the property it is validating against in the class that owns the property? How we present the result could be a different concern
Cause you might have different validation sets, different places to validate. You can have one user-interface, or two user-interfaces, you can have file import, or clipboard import. Sometimes you just validate on file open, or reading data from somewhere.
I really think that dotnet standard validation attributes and interfaces are way to go when it comes to validation... They are so well integrated everywhere. Reading adivce like that just gives me the creeps.
Aside to this masterpiece, why are *we* (C# / .NET developers) so obsessed with Fluent* things? Those usually produce hard to read code without compiler checks 🤷♂
fluent validation also sometimes is not so fluent if validation rules are complex, even I do not like some rule mechanisms in Java world especially if it is a rule mechanism and it is used only for validation!!. They also may rule to use some scripting language aside, etc. It is hard to find bugs in them and also the details as you mentioned above. Sometimes I like writing the things on my own in a very simple way.
I don't understand. I am not the brightest sometimes. But You want compile time checks for a runtime process. Or you want to know if you have made a mistake before runtime. For the former, wouldn't writing tests solve that. And for the latter, writing tests would solve that also. Who writes 200 lines of RuleFor validation checks. Put them in a method, use InternalsVisible (for testing), and just call the method in the constructor. It produces readable descriptive code, small units for testing. I like Fluent Validation, it saves time. Request validation happens at runtime, it only makes sense that mistakes are found at runtime. I guess you could write a source generator. That would be awesome! That is above my paygrade. I am just a humble code monkey. But I think writing tests are easier.
Agreed, depending on which library you mean. Now with source generators or even code gen during MSBuild, stringly-based/reflection APIs should be avoided within one .NET assembly, whenever possible.
I’m a fan of the Fluent libs(validation/assertion). With Validation I like the separation and they my POCO models are just that - plain - no validation related stuff. And FluentAssertions is just a joy.
I do like the result pattern and i use it in F#, Rust etc, but I just I always try stick idioms inside the framework already. And there's already idoim that exists for this and thats the Try pattern. I.e TryParse.
11:50 but this is even kinda worse than the original case on the left side of the image - in console app user at least can see all errros in output. Hate when app shows me errors 1 at a time - r u kidding me, guys? What if it have 10+ bad fields user have to suffer 10 times? Only because it made with result-like class is not making it good
At a minimum, if you are creating a list, do not return IEnumerable, because the consumer will have to do ToList again to check if there is any error and then return/print/do something with the errors. It's better to return IReadOnlyList.
having watched 1:12 into video, I feel like the "bad code" is a jr. dev way to do validation, the error here (in my opinion) is you are returning false with an error message - you can represent the same thing you are trying to achieve by just throwing an exception
Fun stuff, the specs for Email is to have an '@' symbol in between 2 strings. The email validation in the code is appropriate according to the specs but what we think of as a 'valid email' will depend on the organization who is building their own email server and how they decide to implement it. Entertaining talk about it here th-cam.com/video/mrGfahzt-4Q/w-d-xo.htmlsi=9o6gRxm9Do8OsqOB
I myself would define an enum for return value with Success as first val and rest FailedXXX where XXX is the message. Would be lighting fast and everything's pretty constant already in this example. That doesn't work only if you want dynamic error messages but why would you, enum values could describe in comments what they are doing to check, worst case you could add a string as part of tuple.
But something like "X should be in range [0..10] but is 12" is very common, and maybe what you called dynamic ? Imagine compile error, only returning the errorcode, not the location associated. Would be complety useless.
To check everything else together or fail fast: I think it really comes down to the application itself. Most of the time, as a user, I would be infuriated, when I'm gettin a new error over and over again, like why didn't just give me all to errors at once, so I can fix all at once :D The only exception is when an error is caused by something that was previously empty for example, that could still cause new errors, but I'm ok with that.
Agreed. Fail fast is about the worst UX possible.
That's why the good lord gave us IEnumerable and IAsyncEnumerable. Let the caller decide.
@@JoshWeeks Seems completely unrelated, what am i missing?
@@adambickford8720 With deferred execution (using yield) the caller can decide whether to stop after the first error or continue doing all of the validation.
@adambickford8720 with IEnumerabe you get both options. You can choose to only get the first result or keep enumeraring to get all results
Nick, I was waiting for you to mention "parse, don't validate"! If you havent read the article, I really recommend it. The final code was so, so close. If only the function returned a proof that the thing has been validated, all the code can then be written against the validated type.
So in this example: `public static Validation Validate(User user)`
Now, User usually refers to the type in the domain and the input is the unvalidated type, so it would be more like `public static Validation Validate(Command.User user)`.
This is for all the lost souls that happen to come across this comment :')
Validation nomad: someone who wanders the world seeking positive reinforcement
forget about validation package you use, it doesn't work anymore the thing I use the best one it's the tValidation only with that package in my course u find hapiness and success.
I hate errors which only contain the error message as the identifier, because the error message may change. Sometimes you need to handle certain validation results in a special manner, especially on the frontend. I personally always make my validation results contain a human-readable identifier such as "WrongPassword", along with a description.
For HTTP you can't go wrong with errors as return values. Google's HTTP best practices documentation does a good job steering you towards the pattern you've described. I would also recommend including a unique, hardcoded error key (12-length hex character strings is what I prefer) that makes source code searches trivial for in investigating the source of the error. You can have more than one "Missing X field" errors in a project, giving each one a unique key is important when users report a new 400 error where there wasn't one before.
+1, I do this too.
Yeah too bad that I'm almost begging to MS (via a github issue) to extend the ValidationResult or - even better - make it customizable so that we could add any kind of object to a validation error: without any success so far.
IMO errors should always contain 3 things and preferably 4:
1. the path to the thing that is failed (if you are validating json input, json path expressions would be good for this; there isn't really a good spec for generalizations though so just be self consistent)
2. an error code that is easy for a human to identify and distinguish from other possible errors (eg not SYS-1234 but something like EmptyStringValidation) and is the same for the same error throughout the system and effectively never changes in any past or future version
3. error context data (actual value, minimums, maximums, etc; also the shape/schema of this object should never change over time)
4. optional but preferred: a string description in the primary language of the userbase (or if it is identified, in the language preferred by the user)
It is also best if a validation always returns as many identifiable errors from the input state as possible. Even though this might be more computationally expensive up front it generally saves time and processing vs a fail-fast scenario where each error is returned one at a time and the user repeatedly submits their fixed input to get the next error.
@@temp50 we use a coworkers library in our code so our way to do it is similar, but insted of Errors.New we put the errors in the domain. So it would instead be return new User.Errors.InvalidEmailAddress("Error message")
the errors themself are basically just a record that inheirts from a record called ErrorBase
I think the Code Cop series is losing its way. In the example post, the list example is clearly superior. Sure there are even better ways, but most of the alternatives explored don't have the very basic functionality of returning every individual error without combining them all into a single string or exiting as soon as the first error is encountered.
Also, I think Nick has way overcooked the cost of allocating a list vs using yield. I get the impression that he gets into perf sensitive code a lot from his videos, but for the vast majority of LOB apps, it's a total non-issue.
Just because the "good" advice can be improved, doesn't make it bad advice.
true developers ONLY write to console 🗿
Chuck Norris doesn't write to Console; Console writes to Chuck Norris.
@@UweKeim Yeah, and even if you write to > void, Chuck Norris will print it out!
Chuck Norris is stdout.
True developers never need to see the output. They know what it will be before they write the code.
No seriously, it's one of the 12 factor app principle. Always log to stdout, it's the runtime environments job to pick it up and ship it.
I feel like fail fast or not is an application concern. If you are on the server-side, then you just need to know if the thing is valid or not for insertion. But, if you are returning a message to the user, then you want every error listed there. That's why I'd rather make the method ready for both cases using IENumerable as a return and a "yield return ValidationError(errorCode)". That way you could go for a "Any()" call if you want the fail fast approach or a ToList if you want to return all of the errors to the user depending on the use case.
I prefer to apply functional concepts to the domain, instead of building my domain on top of a functional library.
So in this case, I might have a UserForm type, that has a Submit method which returns a Submitted type, that has two properties User? and Errors?, which are null annotated to be not null when SubmittedUser.IsValid is true and false respectively.
TreatAllWarningsAsErrros means that the compiler knows and enforces that you submit the form and check validity before accessing the User or Errors properties.
Now I don't have to explain to devs unfamiliar with function programming what a monoid is, or why our code uses Lst instead of List
I think in this example, the idea of not failing fast seems to have to do with something like form validation rather than, say, precondition checks. If you find all the issues at once, it's possible to annotate the UI with all the errors at the relevant place, allowing the user to correct mistakes without having to try again repeatedly until no errors are found.
WHY is no one ASKING for more Fucntional episodes!!! Yes more functional concepts, please! That stuff is so f***ing cool! You have teased us before, but is it time for the Monad explanation?? What does it even mean??
A monad is just a monoid in the category of endofunctors, what's the problem?
(that's a meme, but it's not as absurd/crazy as it sounds)
" [...] a monad is a type M for which the following functions are defined:
Return-Takes a regular value of type T and lifts it into a monadic value of type M
Bind-Takes a monadic value m and a world-crossing function f; extracts from m its inner value(s) t and applies f to it
Return and Bind should have the following three properties:
Right identity
Left identity
Associativity"
taken from Functional C# by Enrico Buonanno (worth a read on Manning!)
@@thethreeheadedmonkey not a bad explanation, fairly pragmatic. by the way is your name a reference to Monkey Island? cuz if so you're my kinda people.
It would be nice to see some self-made examples instead of yet another dependency for only a few lines of code
@@thethreeheadedmonkey out of context of the book that is a circular definition. If someone doesn’t know what a monad is, they aren’t going to know what a monadic value is either, lol. Nick is good at explaining complicated concepts. I think it would be a good edition. … my category theory is a little rusty, but a monoid sits right there between a group and a semigroup? And endofunctors are functors that map to themselves? Yeah, Nick should totally explain. I’m waiting patiently.
@@WillEhrendreich Haha, it absolutely is a reference to Monkey Island! Not my explanation, though - that's from a book I read and all credit to the author (who does an impressive job with many examples in said book).
I think that there are so many little class nooks and crannies in .NET, especially with the plethora of nuget and github packages out there, that videos like this are very useful. I wish there were more. Good job, Nick.
7:20 why do you need to do extra hoops to return null instead of just empty enumerable? This approach makes consumers make unnecessary checks which also invalidates why you have IsValid flag in the first place.
Returning null would be good if it was an out parameter. Something like bool IsValid(model, out IEnumerable? errors)
To avoid the nullability warning, there is a neat [NotNullWhen(false)] parameter attribute. That’s how it’s done in dictionary’s TryGetValue for example.
But here with a tuple, I agree that having a nullable errors causes a double check to address the nullability warning, and you can’t do anything with that. Allocating an empty array is not ideal either imo.
I just don't get why it doesn't return something like ICollection (or the actual type). It would make it obvious that you can get the count without worrying about the enumeration behind the returned value.
@metlov
You can"t do any async with out, like database validation for email
And btw if you return Array.Empty() it's optimized to not allocate anything
@@x0rld159 you didn’t watch the video carefully. Either with or without an allocation, the caller sees only IEnumerable
Those advices are very funny. The left one is some junior code, the right one, some junior code with max 6 months of experience.
because they are written by junior with max 6 months of experience
A real junior would nested the validations.
Interesting, good video, thanks. I don't see a horrible problem with empty or null errors meaning no errors were found. There comes a point where a consumer needs to be intelligent enough to follow a spec or interpret or code for the result.
Agreed. This is pretty intuitive for me:
if(!errors.Any()) {
....;
}
Static function cannot be dependency injected if desired in the future. I'm glad compilers don't give one error at a time. That is, send the user a full set of available info.
Best practices when writing APIs in C# dictate returning all validation errors for data input to an API. This approach has been standard for +20 years. I believe this is the first post in this series where Nick is obviously wrong.
I'm definitely on team validate everything and not fast failing. Think about everything that goes on under the hood when a web request is made. Theres an enormous amount of work going on before you even receive the request object. Its far more efficient to validate the entire request instead of validating one thing at a time. Users also vastly prefer knowing everything that is wrong instead of fixing one thing, only to be then informed about the next problem after the next submission. Maybe theres a more performant way to validate instead of always creating a list, maybe lazy load it when an error is first encountered, maybe pass in a stringbuilder, there are many ways. But still, in the end, taking the entire user experience into account, its far more efficient to validate the entire request instead of fast-failing
I agree, but there then I would put a side condition of a silent notification, put alerts everywhere, but don't hinder the workflow.
If there are 5 lines wrong, tell the user all the 5 lines, not after one is repaired, you tell about the next.
And don't use popups ! User doesn't want to confirm to have read 20 messages one by one. Building a list, display one popup would be essential.
For form validation, you need to put the feedback next to the each element of the form, so none of these mechanisms are suitable.
So maybe your use case is a server validating its inputs before processing them. If that's the case, I'd consider 'parse, don't validate'. The server receives an HTTP request and attempts to parse a (valid) User out of it. If the parser returns a User, the server goes on to process it. If the parser doesn't return a User, it returns an error and the server handles or returns that error. It doesn't much matter how the parser signals errors - exceptions work fine for this case, as do Either- or Result-style discriminated unions. What I would try to avoid is the possibility of constructing a User object that might not be valid.
oh no don't mention exceptions as a suitable solution in front of Nick 😁
@@lordmetzgermeister I love algebraic data types as much as the next man but in this particular case, the server’s likely to just bale with a 400 status response. I agree exceptions have no place in form handling - apart from anything else, form validation failures aren’t exceptional.
Hey Nick, thank you for your video. As you mentioned, i would really like to see more functional programming by you, because it catches my interest when I sneaked into GO.
Thanks
I feel so validated now.
The email validation, checking for @ sign is better than most, especially better than any regex base ones which almost always rejects valid emails.
exit early is not always the right strategy when validating a complex object. as a user I would want to see everything that's wrong all at once.
True. Different use cases, though.
The "more of a functional developer" just threw out the List monad for multiple errors and replaced it with the Either monad for a single error.
In my opinion this (and many other episodes of code cop) is just splitting hair.
Well the list of error is a better approach than the previous boolean return, to make it more expressive, he could create a ValidationResult type containing the status (validated or not) and the list of problems when validations failed.
Oh, I see later in the video that improvement I suggested (I made the comment in the beginning).
🤘nice one.
You like functional style more eh? 🧐
Do you have any reading you could recommend for clean code vs functional?
the difference between yield return and creating a list is minimal. to consume the IEnumerable the caller has to convert to a list or array anyways. overall I think the suggested approach by the user good. It would be even better if using an enum for the error class plus a message string to make it easier for the caller to handle it. So honestly I don't get the point.
The difference is that the list allocation still happens even if there are no errors. Also to consume the Enumerable you need to loop through it, not necessarily convert into an array or list. Why would you?
Wow, I was implementing similar approach for my nest.js apps. Happy to see I was doing it in at least not a bad way :D
I like value objects for validation. 'Validation' just becomes the attempt to construct a UserName instance. Allows reusing the same logic in the create/update endpoint. Prevents duplicating domain logic vs validation logic. (public static Result Create(string? value))
Interesting video but with last proposal, we return only one error. Is it a way to return all errors on one call?
you could return some aggregate error object (or throw a validation exception)
@@Krokoklemmee "throw a validation exception" I'm pretty sure Nick would say that this is a swearing expression.
The DataAnnotations Validator class returns an ICollection of ValidationResults when calling TryValidateObject and I don't see why we can't do the same. However there is a boolean parameter to control when to validate all properties just as FluentValidation has a parameter to control whether it will return immediately or not upon encountering the first exception
Isn't there valid justification to return a list of all errors to make your API more user friendly, and relay everything wrong with the request that was received? I can see scenarios where end users get frustrated with an API not giving them the whole validation picture, forcing them to discover through trial and error what a valid request should look like.
There absolutely is. FluentValidation does, and it's great.
But that's only for basic input validation (!) at the user-facing edge of the system. Past that point, fail as fast as possible.
personally I think its case to case. If you are filling out a form then I think validating all errors would be the best. If its something else, lets say a button that does something to a specific entity then just showing one at a time is better
Post is comparing Validate with IsValid which are two completely different things. Personally, if I'm writing everything from scratch (mostly library code), then I prefer the IsValid approach (one per validation rule), because it's compliant with Single Responsibility. For application code however, we have Fluent, so no point in reinventing the wheel, especially that we have a much broader enumerable of rules to support.
Since you showed in your channel the OneOf library, I tend to use this when I do my validations as well as returning the different objects to an endpoint. But I think that with the LanguageExtensions library the name is way more intuitive, so I'll give this a try. Nick, do you think these libraries will be deprecated when C# incorporates discriminated unions?
Make Validate() an extension method of User to chain calls all along. Then you'll get user.Validate().Match(...)
Although Match() should have the error first and the result second. Otherwise you can't continue chaining the happy path.
Try ErrorOr library
Not sure what the purpose of returning what was passed in for a success is?
It can help if you're aggregating multiple levels of validation and error processing. Without that you'd end up passing the object around anyway. It's less common for sure, but makes the interface simple and uniform.
Another way is to maintain a Context object. Fewer allocations, but greater complexity in some cases
I really love to see how 'senior' devs enjoy overcomplicating simple things. It would be a nightmare to see the same devs' code for a complex business logic problem.
Function honesty is underappreciated, should not need to read how a method is implemented to be able to use it.
Another overlooked concept is like Kent's new book Tidy First. So much code base is ugly, filled with IDE recommendations, spelling mistakes and out of date packages. So much easier to read and understand when your clean your workspace before making dinner like a good chef. Also turn on warnings as errors, basics first!
shouldn't be the error in monad on left side in match? because when input is right, answer is right.... :) also in definition you wrote Validation, i'm not familiar with library you use but i find it odd that if you define monad value type on left, it should be on left in Match.... if this wasn't an error ofc :)
The library author made some... unique decisions in how he structured this out. Nick has it right, but it's not intuitive that it would work this way, haha.
Had the same thought, but the docs seem to agree: in the Type, the failure is defined first, but when matched, the error goes second.
Guess it depends on your world view, but I would usually go with Happy Path first. Been using the OneOf package for these kinds of things.
Everything you did with installing a separate extension seems like overkill, I'd probably hate the guy who would do this in my codebase.
You are missing the point. Look at a large project with hundreds of validations and then you will understand how this particular extension becomes valuable.
That validate method will throw argument null exceptio when user is null
nullable
Return is: again: depends. Depends on what kind of entity your API is suppose to talk to. Is it a backend service of a frontend application? Than you might wanna indeed list every invalid property. But if you are talking to other services, it is usually enough to response with a machine-readable "GTFO" and maybe log out the problem within service where the problem occurred.
Monadic validation for failfast
Applicative Functor validation for aggregation of errors
I feel to see the practical difference between returning the monad over the validationResult. For me it looks similar but just a bit different in style? Am i missing something?
Given that LanguageExt has had some updates and i cannot seem to find good documentation for their latest release, i know i would love to hear you go over functional programming and monads and an in depth view of the LanguageExt package
I had a really hard time trying to use the LanguageExt package as it is intended instead of just for the monad types. I'm still interested in understanding it more but for now I've just written static Try and TryAsync functions and write result objects as needed without needing LanguageExt. It feels like a package I should know better but it's been difficult.
Chuck Norris doesn't write to Console; Console writes to Chuck Norris.
Code is not failing when written by Chuck Norris so there is no need for validation.
Exceptions? Validation results? I just return an integer opcode. RTFM :P
Hmmm a validation function that doesn't first test:
if( user == null)
And i would agree with most, if you validate one should get *all* possible errors that would invalidate the object being validated... Make no sense otherwise... Also why not have an ErrorCollection where each has detailed info for each error, then carry the validation status/flag for the validation collection. So...
class ValidationInfo {
{
bool isValid =false;
List allErrors = new();
}
So one could have an ErrorInfo with detailed info, and a convenient way to get is the object is valid as well. Then whatever needs to interrogate and get all the errors if desired. So you allow is valid and all the errors if desired.
Ah, guess i just learned about ValidationResult... :)
The null check is redundant in current language versions, because the input parameter is not nullable.
You STILL cannot act on the validation failure though, unless you have a switch statement with literal sentences as cases....
You need to return your errors as codes, enums, error types inherriting from a common type... something else than strings.
Language Extensions are awesome, excited fro when it becomes integrated hopefully by dotnet 10
The problem with the aka good approach here is.
What if you have hundreds of errors or hundred of things to validate?
What if there is no good context?
I think someone would have pointed that out.
Maybe some people wants to be bombarded with a lot of errors showing on their face.
But there's also the camp that fix error one by one.
Which can also be bad if you have hundred of errors.
That will take time.
I'd appreciate more content on functional programming in C#. I'm still a beginner what the paradigm is concerned. While C# is rather functional friendly, a lot of things are cumbersome to implement. Lots of boilerplate, so help would be welcome.
I'm trying to use the LanguageExt package, but it's documentation is incredibly sparse. It reads more like a white paper, but I guess that figures. It basically assumes you know your functional stuff and the package's function signatures are incredibly confusing if you have no idea what exactly, for example, an 'Atom' - apart from the obvious - is supposed to do or be used for.
Currently stuck with mostly Options and some collections. The Result type seems lackluster to me, but I might just not understand how to use it.
IValidatabkeObject or FluidValidations. For error as values I use ErrorOr
More interesting question is: how to pass validations constraints to OpenApi documentation in case when it is not implemented with data annotations, to prevent users from sending obviously wrong requests?
If you're using the Swagger package you can implement schema filter and change the properties for any model before the json is generated.
I did that to read from custom attributes a project recently.
OMG.. After so many videos Nick finally has said that he is Functional programming :-) So, what about F# ;-)
Omg.. Even uses THE word... Monad 🙂
more fsharp is a good thing. even if you don't use it to pay your bills, it will help your csharp code to know fsharp, without question.
Result pattern is the way to go in my opinion.
These are guard checks. Proper client side validation is better for the UX.
As Zoran would say, why are name and email stupid strings? #PrimitiveObsession
Completely agree.
You're splitting hairs. This is an example, the topic is not anemic data models.
I still find utilising IValidateable object the best for validation. It encapsulates the validation logic into the model and keeps the code in one place.
What are the thoughts on static class vs. extension methods (yes, I know that's technically static as well). I.e. UserValidator.Validate(user) versus user.Validate()?
I wonder if anyone in .NET community tried something like Go enforces. You have shown that approach with tuple, but I'm just wondering if you did it in non-validation scope. Like, instead of int.TryParse(string input, out int value) -> bool it would be int.Parse(string input) -> (int value, Error error)
The problem I see with tuples is that there is no enforcement in implementing all validation in a large project in a uniform manner. After 6 months there is new code written by a new engineer with a new tuple as a result.
Bit of a pedantic point but shouldn't the tuple names start with lowercase? I was looking at msft docs today and they seem to use it as lowercase
The names of the tuple items should be PascalCase
You know, I'm kinda split watching your channel. On one hand I appreciate helpful C# tips, being .NET dev myself. On the other hand, I feel like I need to skip almost a half of video to get this one tip that would be possible to explain in two minutes. The other half of video is promoting channel, private course etc. I would gladly buy your Udemy course where you would summarize all those tips. But I feel like watching your channel is more wasteful than helpful.
A 5 second ad is 7 minutes?
The example at start of the video.. they both look horrible, when all flows result in similar outputs and you're just evaluating conditions, that's the place to use a switch expression to begin with.
The monad thingy.. why? A guardian if is so much simple and easier to read.
One of the first indicators of good code is how easy is to follow and understand the logic, without having to stop and spend unnecessary time figuring it, even if that time is 2 seconds.
Code written by the 'sophisticated genius' developer is usually a pain in the ass for the others.
gotta say, you may have a point here. The resultant code is not immediately parsable by a a human unfamiliar with the library. I personally use FluidValidation but I am open for new, potentially better ways to achieve things.
Why both Tupple and ValidationResult response types are bad. Because from the contract you see it is perfectly valid to get that validation was successful and non-empty list of errors at the same time. ValidationResult is slightly better because it is impossible to create something like this without reflection but this is why a non-nullable list of errors is better.
No diss, but all are bad imho... Return a tupple, OK, no issue, but returning a list of error strings??? Mother of all... Have enum UserValidationErrors, return (bool,List). Last thing i want is to waste time/resources checking strings THAT MIGHT CHANGE down the road because someone felt like a period was actually a comma. Even better (if range of errors is known to be short and has no possible way of growing) use [Flags] to return a single composite value. Assuming it's int64, that 64 possible errors so should be plenty for most validations of this sort...
That said, I'm not even sure most C# devs these days even know what a bitwise operation is, most don't know how to check for nulls that much is certain.
That guy who posted that on Linkedin is so noob. 😢 I did those exact things 10 years ago.
The expression "result.Match(Results.Ok, x=>Results.BadRequest(x.Head))" feels wrong on so many levels. I would read this as: if result is ok, call the bad request handler. Is it really supposed to be this way?
you can do result.Match(x => Results.Ok(x.Tail), x => Results.BadRequest(x.Head))
but that also seems strange since it's the error that's first in the type arguments, why is success first all of a sudden?
Why not just keep the validation method and the property it is validating against in the class that owns the property? How we present the result could be a different concern
Cause you might have different validation sets, different places to validate.
You can have one user-interface, or two user-interfaces, you can have file import, or clipboard import.
Sometimes you just validate on file open, or reading data from somewhere.
While waiting for discriminated unions and builtin Result type let's see Scott Wlaschin videos on railway oriented programming.
I really think that dotnet standard validation attributes and interfaces are way to go when it comes to validation... They are so well integrated everywhere. Reading adivce like that just gives me the creeps.
Aside to this masterpiece, why are *we* (C# / .NET developers) so obsessed with Fluent* things? Those usually produce hard to read code without compiler checks 🤷♂
No clue. For me, they seemed cool when I was starting out with programming but the more experience I have the less appealing I find it
fluent validation also sometimes is not so fluent if validation rules are complex, even I do not like some rule mechanisms in Java world especially if it is a rule mechanism and it is used only for validation!!. They also may rule to use some scripting language aside, etc. It is hard to find bugs in them and also the details as you mentioned above. Sometimes I like writing the things on my own in a very simple way.
I don't understand. I am not the brightest sometimes. But You want compile time checks for a runtime process. Or you want to know if you have made a mistake before runtime. For the former, wouldn't writing tests solve that. And for the latter, writing tests would solve that also. Who writes 200 lines of RuleFor validation checks. Put them in a method, use InternalsVisible (for testing), and just call the method in the constructor. It produces readable descriptive code, small units for testing. I like Fluent Validation, it saves time. Request validation happens at runtime, it only makes sense that mistakes are found at runtime. I guess you could write a source generator. That would be awesome! That is above my paygrade. I am just a humble code monkey. But I think writing tests are easier.
Agreed, depending on which library you mean. Now with source generators or even code gen during MSBuild, stringly-based/reflection APIs should be avoided within one .NET assembly, whenever possible.
I’m a fan of the Fluent libs(validation/assertion). With Validation I like the separation and they my POCO models are just that - plain - no validation related stuff. And FluentAssertions is just a joy.
I wonder if the point is simply to return all errors instead of just the first error
I do like the result pattern and i use it in F#, Rust etc, but I just I always try stick idioms inside the framework already. And there's already idoim that exists for this and thats the Try pattern. I.e TryParse.
From a user experience you should get all errors not just the first error
In what deep and dark junkyard you find such weird and cringe code snippets??? It is so weird that I cannot imagine who makes them
You know both examples are bad when even Resharper complains about the conditions for both solutions
Why not just use fluentvalidation?mi don't see how nicks approach is better
It's like "stop programming - buy a package".
Some people must be able to program things like fluentvalidation.
Need more functional code.
Fail fast for services, list every validation error for user interfaces.
Great advice!
You didn't use the monad part of the type
What happend to Contracts?
Can u speak more about functional progreming
11:50 but this is even kinda worse than the original case on the left side of the image - in console app user at least can see all errros in output. Hate when app shows me errors 1 at a time - r u kidding me, guys? What if it have 10+ bad fields user have to suffer 10 times? Only because it made with result-like class is not making it good
Crying while installing the FluentValidation package in the background
How is it possible for this post to have so many likes I really don't understand 😅...
DataAnnotations?
I just return a bool but return early. if (validateObject) { ... } else { fail code }
At a minimum, if you are creating a list, do not return IEnumerable, because the consumer will have to do ToList again to check if there is any error and then return/print/do something with the errors. It's better to return IReadOnlyList.
Nick, your example of a tuple isn't good either. If you're going to return a boolean to indicate success, then use the TryParse pattern.
having watched 1:12 into video, I feel like the "bad code" is a jr. dev way to do validation, the error here (in my opinion) is you are returning false with an error message - you can represent the same thing you are trying to achieve by just throwing an exception
Fun stuff, the specs for Email is to have an '@' symbol in between 2 strings. The email validation in the code is appropriate according to the specs but what we think of as a 'valid email' will depend on the organization who is building their own email server and how they decide to implement it.
Entertaining talk about it here th-cam.com/video/mrGfahzt-4Q/w-d-xo.htmlsi=9o6gRxm9Do8OsqOB
I myself would define an enum for return value with Success as first val and rest FailedXXX where XXX is the message. Would be lighting fast and everything's pretty constant already in this example. That doesn't work only if you want dynamic error messages but why would you, enum values could describe in comments what they are doing to check, worst case you could add a string as part of tuple.
But something like "X should be in range [0..10] but is 12" is very common, and maybe what you called dynamic ?
Imagine compile error, only returning the errorcode, not the location associated. Would be complety useless.