Exceptions Are Extremely Expensive… Do This Instead

Поделиться
HTML-код
  • Опубликовано: 22 ноя 2024

Комментарии • 183

  • @MilanJovanovicTech
    @MilanJovanovicTech  4 месяца назад +5

    Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
    Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt

  • @mohammedelrasheed8498
    @mohammedelrasheed8498 4 месяца назад +4

    00:02 Exceptions are meant for exceptional situations
    02:06 Instead of exceptions, consider using the result pattern for error handling
    04:07 Handling validation errors with exceptions
    06:10 Handling exceptions using an IExceptionHandler interface in .NET 8.
    08:03 Performance test using k6 for API requests
    10:02 Implement alternative error handling method without throwing exceptions
    12:05 Utilize existing libraries or create custom abstractions for handling exceptions efficiently
    14:10 Using result pattern is faster than exceptions for flow control
    16:12 Consider using result object instead of exceptions for performance

  • @daneel_olivaw
    @daneel_olivaw 3 месяца назад +1

    As you mentioned in your final remarks if we are doing client-side validation, the number of server-side exceptions should be minimal. With result objects, we have
    1. Arguably less readable/maintainable code. Having result checks on each layer is way less fun than having a middleware that handles all exceptions in one place and returns the appropriate result to the user.
    2. More memory allocations, since ALL happy paths are now boxed with the Result object. Try testing with a 5% failure rate, and compare memory allocations. In my benchmark, using ErrorOr, resulted in 10x memory allocations.
    3. Lost stack trace

    • @MilanJovanovicTech
      @MilanJovanovicTech  3 месяца назад +2

      1. I don't find it less readable, but that'll be a personal opinion. I agree that layering exacerbates the problem, so the obvious solution is not to have many layers - which is doable.
      2. Yes, that's expected.
      3. We can add the stack trace to the result at time of creation.

  • @reginaldo8597
    @reginaldo8597 3 месяца назад +9

    I think the gained performance won't matter as much in a real world enterprise solution and I'll explain why. The selected premise is invalid, it sets up an application to fail hundreds of thousands of times per minute in order to be true. I understand that was done to highlight how expensive they are, but an application is not expected to fail like that. We generaly see a couple of exceptions once in a while, just like you said, they are for exceptional cases. If the application is failing like that, it means something is wrong with the caller or the application. Adding so much boilerplate for that is not worth it and will only polute the code.

    • @MilanJovanovicTech
      @MilanJovanovicTech  3 месяца назад +2

      Balanced argument, makes sense. It was never about performance for me (but I did want to make this video, though) - I much prefer the explicitness of Result pattern. With discriminated unions coming to C#, I think this approach will only become more popular.

    • @reginaldo8597
      @reginaldo8597 3 месяца назад

      ​@@MilanJovanovicTech fair enough, it was a good video, I didn't know it was so expensive. I personally feel pain having to use wrappers everywhere, makes the code more complex to maintain, specially for junior developers. Java pulled something similar with the Optional return types. I wish they did something like Typescript union types, rather than adding a wrapper for that. If C# adds something similar to Typescript, then I would have to agree that it will only become more popular.

    • @poneciak57
      @poneciak57 3 месяца назад

      After throwing an exception non garbage collected languages like C++ for example needs to do something with memory (garbage collected too) so they perform what is called "stack unwinding" and it can lead to some errors like memory leaks because pointers will get unwinded from the stack. Like its easy to say that you should watch out for this but thats why we have the Result pattern (or whatever it is called)

    • @mionel237
      @mionel237 28 дней назад

      you're wrong. too tired to explain but you can try saying the same thing while looking into mirror. keep up the good work milan, stay awesome

  • @br3nto
    @br3nto 4 месяца назад +27

    Like everything, exceptions are a trade-off. In one codebase, we went from Exceptions to success/failure wrappers, then back to Exceptions after about 2 years. Exceptions were waaaay simpler, and the code was drastically reduced when we removed the success/failure wrappers. The code was easier to follow and find bugs when we used exception because there was less of/else/switch control flow, and the return types were simpler. The apps were also easier to debug because breakpoints, and stepping through code was simpler when using exceptions as there was less indirection.

    • @br3nto
      @br3nto 4 месяца назад

      To be fair we were also using Dapper which was adding an annoying amount of unnecessary indirection.

    • @br3nto
      @br3nto 4 месяца назад +3

      Another point is that most of the time, your request should be going through the success state rather than the failure state. Usually the failure state is identified early, where as the success state has the expensive operations after all the failure states have been identified.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +3

      "stepping through code was simpler when using exceptions as there was less indirection" - isn't this counterintuitive, since exceptions will essentially throw you to an entirely different part of the code (indirection)?
      Whereas with a return statement, you go back to where you called the current method from.

    • @br3nto
      @br3nto 4 месяца назад

      @@MilanJovanovicTech maybe at the start… but then extra abstractions start getting added to make handling different and similar responses consistent, and more and more things eventually get layered on top. Exceptions handlers were easy because there was a single place where exceptions got mapped to error responses and response codes. Again, I think having other libraries like Dapper mixed in exacerbated the problem. Our code never looked like your example. We had thin controller actions.

    • @br3nto
      @br3nto 4 месяца назад +4

      @@MilanJovanovicTech it just occurred to me that returning errors as part of your return type is the same conceptually as checked exceptions in Java. Different syntax obviously, but the same type of patterns emerge. Any caller must handle your error, return it, or wrap and return it. In that sense it’s also like the “coloured function” problem that the Primagen talked about when using async; the pattern spreads. It’s hard to contain and isolate its usage. It’s almost like an all or nothing. So that’s another reason why I like exceptions over returning error values. There’s just less code.

  • @bogella2225
    @bogella2225 4 месяца назад +3

    Im not sure where I saw it, but congrats on being MVP again.

  • @vunder84
    @vunder84 3 месяца назад +3

    The biggest problem of this video is that the author misleads the audience that "you should not use exceptions because they slow down your app". This completely incorrect statement!
    Exceptions cost a lot, but the result in k6 depends on your app arch+tech solutions. In the same way I can prove that the SIMD instructions slow down your app by testing app using double and SIMD to sum and mul numbers in the "calculator app"
    It is important to tell what solution/technique is better to use in which cases/scenarios

    • @MilanJovanovicTech
      @MilanJovanovicTech  3 месяца назад

      You're right, there are far more important reasons not to use exceptions

  • @gfantin88
    @gfantin88 4 месяца назад +6

    Great video! In my case, most applications I write are in a controlled environment, so exceptions are really exceptions and a tiny portion of our responses. So, in my case, I find it hard to defend using results in every method on the chain.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      That's perfectly acceptable. What do you do for errors (e.g., validation)?

  • @ajdinhusic2574
    @ajdinhusic2574 4 месяца назад +8

    Great video! however one thing to note: All these stats about requests per hour (800,000 vs 15 million) are still not realistic in a real-world scenario, because this means that ALL these requests fail. In a true setting though, a percentage of requests might fail, and some will succeed.
    Therefore the gap between the two situations isn't as big as we think in the first place, and the 800,000 r/s might be more toward a few-million r/s (depending on the api consumer behavior of course).

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +3

      I kind of tried to summarize that in my closing remarks, appreciate your comment though.

    • @ajdinhusic2574
      @ajdinhusic2574 4 месяца назад

      @@MilanJovanovicTech I appreciate your videos too! Really good job, Milan!

  • @chanep1
    @chanep1 3 месяца назад +1

    Well done, exposing real pors and cons

  • @anton-ivanchenko
    @anton-ivanchenko 4 месяца назад +7

    I use exceptions in most of my methods. If an action needs to be attempted without throwing an exception, then I create a separate "Try..." method. I don't see the point of using the Result type everywhere, as it adds dozens of checks to the code after each operation. But I agree that an exception should not control the flow of execution. When an exception occurs, it should reach the top of the call stack and return the result to the user (if possible).

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Makes sense ☝️

    • @adolfomartin5456
      @adolfomartin5456 4 месяца назад +1

      This is a functional solution, that you are using in other places: I usually program in JavaScript and Promises is like Result, they can be resolved or rejected, the if/else logic you said is somehow hidden with Promises.

    • @anton-ivanchenko
      @anton-ivanchenko 4 месяца назад

      @@adolfomartin5456 In C#, tasks can be used in a similar way to promises in JavaScript. But then there is the problem of large nesting of the code, and a lot of inconvenience. In JavaScript, you can also put await before promises and in that case you will still need a type similar to "Result". If you need to do a dozen asynchronous operations sequentially, then your approach will require a dozen levels of nesting.

  • @sokka7460
    @sokka7460 3 месяца назад +3

    No, exceptions are for what they are, they bubble up to ensure some one catch and manages the exception. Result pattern doesn't. Exceptions for everything that needs to be handled or the application crahses

  • @Rizkiaqa
    @Rizkiaqa 4 месяца назад +3

    Great Video !
    Almost my code using Exception to bypass all code after error occurs, then handle it by try..catch . We don't consider receiving request speed because the number of users arenot so many. Now the code is live production. Better not touching it anymore as long as it is not broke :D

  • @CreativeB34ST
    @CreativeB34ST 4 месяца назад

    I would love to see a follow up video on this that goes deeper into using exceptions for handling invariants and other validation checks in a DDD Domain Layer. In this case I prefer exceptions because I don't want to dirty my whole domain layer return types with a Result type. DDD states that the domain layer should use domain objects as arguments and return types. So in my eyes, (domain) exceptions are the only way to go.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      "DDD states that the domain layer should use domain objects as arguments and return types" - where does it state it? I don't recall reading something like this.

  • @janjoska2549
    @janjoska2549 4 месяца назад

    I would like to know what are the criteria for your decision to use result pattern in certain parts of your software. For example, a method allocates 1MB array. This operation can clearly fail as there might not be enough memory available. Will you use Result pattern for this? What will you do when you clearly cannot perform your intended operation? How will you interrupt work that cannot be finished. Will you place Result return object to basicly every method in call chain up to the entry point of your application? What if you forget to evaluate the result somewhere, system will throw null reference exceptions someplace else.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Wouldn't that throw an OutOfMemoryException anyway?
      "What will you do when you clearly cannot perform your intended operation?" - Can I return a meaningful error to the user? If yes, return an error. If not throw an exception.
      "Will you place Result return object to basicly every method in call chain up to the entry point of your application?" - Yes, that's what will happen.
      "What if you forget to evaluate the result somewhere, system will throw null reference exceptions someplace else." - Write a test so you don't forget.

  • @krccmsitp2884
    @krccmsitp2884 4 месяца назад

    A simple alternative to a Result type might be to return a tuple like (T? Value, string? Error) where either of the elements is null. This can be simply checked with a switch expression and pattern matching.

    • @tehwabbbit
      @tehwabbbit 4 месяца назад +2

      Honestly, just use the exception type as that's what it's designed for. This video is not good advice.

    • @krccmsitp2884
      @krccmsitp2884 4 месяца назад +1

      @@tehwabbbit exceptions are for exceptional situations, not for business rules.

    • @DanWalshTV
      @DanWalshTV 4 месяца назад +1

      That's the standard approach in Golang

    • @krccmsitp2884
      @krccmsitp2884 4 месяца назад

      @@DanWalshTV never used that, but good to know

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      How do we maintain consistency with a Tuple, though? In a large team, devs can do weird things.

  • @ASUSVOLT
    @ASUSVOLT 4 месяца назад

    Need to think about it... Thanks for video.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +1

      Check out some of my other videos on the Result pattern

  • @oshastitko
    @oshastitko 3 месяца назад

    What about the Open/Closed Principle in the Result Pattern? If we need to add one more result option, we have to modify a class used by the Result Pattern....

    • @MilanJovanovicTech
      @MilanJovanovicTech  3 месяца назад +1

      Nah, we'd have a Result and Result in reality. That takes care of both cases.

    • @oshastitko
      @oshastitko 3 месяца назад +1

      ​@@MilanJovanovicTech The class should be closed for modification and open for extension. For example, if a subclass of our class introduces additional result options, and if T is an enum or something similar (which we can't inherit), how does this fit with the Open/Closed Principle?

    • @marko5734
      @marko5734 2 месяца назад

      ​@@oshastitkoYou just exposed the flaws of inheritance. The derived class lose flexibility beacuse it's obligated to use parameters from the base constructor which doesn't need, so it cannot override. So the only option is to introduce new parameters in the base constructor and make most the parameters nullable, or you add new abstract void method, which again as you pointed out will break ocp. Maybe inheritance is not the best option here beacuse result pattern can be so diverse, and instead, object composition should be used?

  • @eu.jeanoliveira
    @eu.jeanoliveira 4 месяца назад +1

    Hi, Milan Jovanović. What is your opinion about exceptions in parameter validation during object initialization? Since there is a trend that says you should not create an object with an invalid state.
    By not using the exceptions approach, you would have to use something like FluentValidation. Which, in short, creates an object with a possible invalid state and then checks whether or not it is invalid.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +1

      You could also expose a factory method, that returns a failure Result/Error in case some of the input arguments are invalid

    • @arunnair7584
      @arunnair7584 4 месяца назад +1

      Yes. I never create an object in an invalid state. All parameters are checked before creating the object. If any attributerequires a change, a new object is created. There are no setters or getters in the object. Immutable objects are the norm. Saves you a lot of headaches especially in products that have to be maintained for years.

  • @higoramorim85
    @higoramorim85 3 месяца назад

    Amusing concepts, I saw something like this with Kotlin and Ruby, uses exceptions when it really needs, best regards

  • @botyironcastle
    @botyironcastle 4 месяца назад +1

    use a functional language if you want to use the result pattern , c# is not meant for that

  • @kyreehenry9202
    @kyreehenry9202 4 месяца назад

    The result pattern feels cumbersome to me.
    Consider an application using a repository pattern, where the repository is standard and doesn’t involve use cases. In this setup, you have an abstraction layer-let's call it "services." This service layer interacts with repositories, handles validation, and, crucially, manages the conversion between DTOs and entity models. Using the result pattern in this context makes the code more complex and less readable.
    I've shifted from the result pattern to using exception handling instead.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      The problem here is more about too many layers of indirection rather than the result pattern. But I get your point of view, and trust you've made the right decision. Do you have a global exception handler now?

    • @kyreehenry9202
      @kyreehenry9202 4 месяца назад

      @@MilanJovanovicTech I like yours better

  • @yatsuk
    @yatsuk 3 месяца назад

    If your service in 99% of time returns bad responses than yes - do not use exceptions. Otherwise there is no point to spend a time for fighting with exception because it is a rare case and performance issues will be in some another places - like slow db requests, caching, pools etc. And better to spend your time there.

    • @MilanJovanovicTech
      @MilanJovanovicTech  3 месяца назад

      Not the only reason to use Result: ruclips.net/video/WCCkEe_Hy2Y/видео.html

    • @yatsuk
      @yatsuk 3 месяца назад

      @@MilanJovanovicTech validation issue is still issue - in majority cases requests contain a proper json and validation does not fail.

  • @beyaz1404
    @beyaz1404 4 месяца назад

    Realy great video! Clear explanation !
    I wnt to ask question.
    In this functional error handling; how to manage response checks?
    ..
    ..
    ..
    var result = callX();
    if(result.Sucess)
    {
    return result.ErrorMessage;
    }
    ..
    ..
    in this case 5 time call generates 20 line of error check.
    is there any good solution for this problem? espacially c# language

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      What's wrong with this solution?

    • @beyaz1404
      @beyaz1404 4 месяца назад

      @@MilanJovanovicTech
      - Code sizes growing very fast, many call generates extra 5 line code for error check.
      this reduce readability.
      - occurs complex variable names like userNameResult & userName

  • @БорисСлавков-д3ш
    @БорисСлавков-д3ш 4 месяца назад

    I've never seen someone using exceptions as a result code, yes there is more work to be done, but what would say about ppl using a b c for naming their variables... Naming a variable properly is also a lot of work...

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Not sure if you've been lucky not to see this in use, or...
      It's almost a standard, I'm afraid 😅

  • @omineirotech
    @omineirotech 3 месяца назад

    I don’t think it was a trustable test, since it turns a exceptional situation (UserNotFound) into a situation that happens multiple times sequentially. Internally cache of the machine will change the results here… besides that, it is not usual thing at all.

  • @davidtaylor3771
    @davidtaylor3771 4 месяца назад

    For years I have been doing what I refer to as "Exception driven development", where I model the core architecture via exception flows. This video has been a revelation that I might be doing something wrong.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +4

      I also believe in "if it ain't broke, don't fix it" 😁

  • @Rick-mf3gh
    @Rick-mf3gh 4 месяца назад +3

    After all the work of cascading the Result result up and down the call stack, you still have to handle exceptions being thrown.
    And having used this technique in an enterprise level app, I can't express how much of a PITA it was. Extra code, complexityand risk with zero tangible benefits.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Handle exceptions as low-level as possible, if you know how to handle them. Unhandled exceptions (and caught by a global handler) aren't relevant since you don't know how to handle them.
      What did the rest of your team think of this approach? Someone had to introduce it.

    • @Rick-mf3gh
      @Rick-mf3gh 4 месяца назад

      @@MilanJovanovicTech When I try to post a reply containing links to dotnetfiddle, my posts are not saved. 😥

    • @Rick-mf3gh
      @Rick-mf3gh 4 месяца назад

      When I said "handle exceptions" I was referring to a global exception handler. 👍
      As for what the team thought: We rapidly grew to hate it. But the guy that introduced it was not capable of admitting his mistakes.
      For example:
      Imagine a class used in multiple places in the codebase: dotnetfiddle id: wffxQD
      You then add validation: dotnetfiddle id: J5112m This code builds without warnings, but it is actually broken.
      To apply this change could require a significant cascading refactor due to ClassUsedByDozensOfOtherClasses now possibly returning a failure.
      And the compiler will not identify if you have missed handling a possible Failure. Accidentally missing a failure could have disastrous results.
      (I had to only post the dotnetfiddle ids because youtube appears to block posts with links.)

    • @Rick-mf3gh
      @Rick-mf3gh 4 месяца назад

      @@MilanJovanovicTech When I said "handle exceptions" I was referring to a global exception handler. 👍
      As for what the team thought: We rapidly grew to hate it. But the guy that introduced it was not capable of admitting his mistakes.
      For example:
      Imagine a class used in multiple places in the codebase: dotnetfiddle id: wffxQD
      You then add validation: dotnetfiddle id: J5112m This code builds without warnings, but it is actually broken.
      To apply this change could require a significant cascading refactor due to ClassUsedByDozensOfOtherClasses now possibly returning a failure.
      And the compiler will not identify if you have missed handling a possible Failure. Accidentally missing a failure could have disastrous results.
      (You'll have to create your own dotnetfiddle links based on the ids provided due to youtube seeming to block posts with links.)

    • @Rick-mf3gh
      @Rick-mf3gh 4 месяца назад

      @@MilanJovanovicTech When I said "handle exceptions" I was referring to a global exception handler. 👍
      As for what the team thought: We rapidly grew to hate it. But the guy that introduced it was not capable of admitting his mistakes.

  • @helshabini
    @helshabini 4 месяца назад +2

    Great video. I've always known that exceptions were expensive, but never imagined the difference would be that huge. What are your views on Tuples (containing an error) as simple return types rather than the complexity of Result types? Similar to Go's error handling methodology?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +1

      Result standardizes the response from your methods. Tuples are awesome, but I'm afraid of maintaining consistency in a large team.

  • @tiagomabango
    @tiagomabango 4 месяца назад +1

    You are the best, very nice. Thanks for the content, I love that!

  • @Koshala123
    @Koshala123 4 месяца назад

    Asking just to polish up my knowledge , imagine we are using a try catch block. Inside the catch block we are not throwing an exception but returning an int value or something Is that bad too. Does that fall under the same category as this

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      @@Koshala123 Not really. You catch an exception, know how to handle it, and return a meaningful response

  • @pinyorungoral6037
    @pinyorungoral6037 4 месяца назад

    How you handle flow when your code nested call service layer 3-4 level? And did you try disable log and test again?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +3

      Using exceptions there's typically a top-level handler.
      With Result pattern, you'd short-circuit the failure from level 3 upward.

  • @rohit704
    @rohit704 4 месяца назад

    I am just thinking , who will result pattern will work with Que and DLQ ? If errors comes it should stop the process.

  • @SXsoft99
    @SXsoft99 3 месяца назад

    programing is like food "ow that's not good for you, that's not good for you either, that affects you in that way, you should eat this and that, etc" same is with programing "ow that is a performance hog, ow that's an anti-patern, ow that is that"

  • @pedroferreira9234
    @pedroferreira9234 4 месяца назад +43

    and how many applications go behond 1k request a second? Does the applications you built do 1/2k/s? Be pragmatic and default to exception handling first. There are many other ways (infrastructure) to increase request/s very cheaply , and if, somehow, this really gets to be a problem, then c# was probably a bad choice.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +23

      I still don't recommend using exceptions for flow control. Exceptions are for exceptional situations.
      Use result pattern (or similar) for expected and common errors.
      Going with exceptions ends up with dozens or hundreds of custom exception classes. Using generic exceptions ends up with lots of duplication.
      With errors as types, you can group them in a single file with much less ceremony than creating a new exception class.

    • @sameerband2933
      @sameerband2933 4 месяца назад +9

      I would partially agree with you both. I would use the result pattern when it's a business case. For example user not found but I would use exception for something unexpected. For example dB connection, network, stream, anything related to communication, etc because exception would give me something more that I can later use may be to retry or change the approach

    • @patfre
      @patfre 4 месяца назад +2

      Exception should only be used when something has actually gone wrong. Like when you try and access memory that isn’t the programs memory. Having a user not it exist doesn’t need to have an exception since the server doesn’t need it anyway it’s only the one requesting it and since it’s slower to do it it’s just worse. Besides the more you use it and the deeper the call stack the worse the penalty. No company will see the exception spam and think this is a qualified programmer because they don’t know simple optimization. The method also have benefits such as being simpler to understand the intent. Using exception will just lead to the server crashing needlessly because someone forgot a catch statement somewhere after a new exception was put in or changed and that’s expensive for a company because it could take hours to fix it all while no money is being made. So only if the server absolutely requires something it can’t get or do what it should do then it should throw an exception otherwise no.

    • @jamesbarrow
      @jamesbarrow 4 месяца назад

      ​@@sameerband2933agreed. Result for application concerns, exception for infrastructure concerns. If an infrastructure concern becomes an application concern, then mapping to a result makes sense

    • @antonmartyniuk
      @antonmartyniuk 4 месяца назад +1

      When using exceptions your Service interface doesn't tell you that UserNotFoundException may be thrown. You could include this in code summary, but it can be quickly become outdated and not all devs write the code summary (and that's bad).
      With a result pattern your interface is explicit that you gonna have errors in this method

  • @juniorzucareli
    @juniorzucareli 4 месяца назад +11

    For this example in the video, I totally agree with not using exceptions, but for domain classes, I'm totally against using result, for me it has to be exception.
    If used for domain classes, the consumer of the class will have to do ifs all the time, this is hell, and I think it's unnecessary, normally input validations are done in higher layers, like controllers for web api. If you reached the domain class, it is understood that the inputs are ok, and if for some reason, the inputs arrived at the domain class with invalid data, an exception must be thrown, this applies to the class constructor, the methods within and etc...

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +3

      I can work with that ☝️

    • @CesarDemi81
      @CesarDemi81 4 месяца назад +3

      Totally agree.
      Domain classes are actually the only place where it's justified to throw exceptions because, as you stated, that really IS an exception. A previous validator should have catched an incorrect value and the domain validations are the last line of defense prior to save data in an inconsistent state.
      The rest of things, like validating input from an endpoint or checking whether the item you're trying to edit or delete actually exist, then a results-based flow should be followed.

    • @juniorzucareli
      @juniorzucareli 4 месяца назад +1

      Yes!!
      I have an example that may cause problems with returning result from the domain class:
      Imagine that you have a method called calculateAmount within the order class, and this method receives, for example, items and a discount coupon to calculate the value, if the values ​​passed by parameter are invalid, and the method returns a result, forcing the consumer of the class method put an if to check, if the if is not done, and then there is a save in the database, this is a silent bug that can cause a lot of problems...
      (the example I gave was stupid, but the idea is valid and has happened to me several times)

    • @CesarDemi81
      @CesarDemi81 4 месяца назад +1

      @@juniorzucareli yeah, in that area, the convoluted work that brings along doesn't really add much value in exchange of all the effort that requires to handle it. At that point it's much more useful to throw as that never should have happened.

    • @matthewrossee
      @matthewrossee 4 месяца назад

      "if you reached the domain class, it is understood that the inputs are ok" why do you think so? a genuine question

  • @CRBarchager
    @CRBarchager 4 месяца назад

    Awesome video, Milan. I've surprised by the performance hit exception has. I knew they were slow but not by this much. I'm definately going to have rethink some of our solutions namely one that throws 100.000+ exceptions a day.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      I'll do another benchmark with a remote server, to see how much of a difference it makes. Will post an update here.

  • @user-rp9iis1en6h
    @user-rp9iis1en6h 4 месяца назад

    When you will grow as a senior developer, you will learn mostly how to set up the project keeping third party libraries minimal.

  • @dionismendanha4649
    @dionismendanha4649 4 месяца назад

    how to deal with trace in logs when not using exceptions?

    • @LordOfCoding
      @LordOfCoding 4 месяца назад

      From the "Error" state back to the call state, you always add your call signature to a stack in the Result

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      You can log details from the Error, and even append a stack trace to the Result

  • @alexmotaufc3871
    @alexmotaufc3871 4 месяца назад

    With this the "stack trace" would be lost ?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      You can always add it when creating the Result using Environment.StackTrace

    • @MaciejLisCK
      @MaciejLisCK 4 месяца назад

      @@MilanJovanovicTech But may make both solutions run at same time.

  • @thanhnguyenduy78
    @thanhnguyenduy78 2 месяца назад

    How can I get source code of this

  • @vintage8
    @vintage8 4 месяца назад

    It's becoming popular not throwing exceptions nowadays. Why do you expect that many exceptions to be thrown in the first place? Aren't you gonna handle exceptions that might occur in the front-end? Or are you worried that users might go into the javascript and modify it so that your back-end throws exceptions to decrease performance for other users?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Popular? I'm pretty much the only one advocating for using a Result object or similar.
      That doesn't mean not using exceptions (as explained in the video).

    • @vintage8
      @vintage8 4 месяца назад

      @@MilanJovanovicTech RUclips always recommends me something about exceptions and I see that you didn’t respond to any of the questions I asked.
      Edit: forgot to add ErrorOr (@amantinband which also says the same thing about you), OneOf or any other packages does the same thing right? or am I mistaken? If these does not correlate to your video then my apologies.

  • @DanielOpitz
    @DanielOpitz 4 месяца назад +4

    I'm not sure if that minimal performance gain for very rare cases is worth the effort and extra dependency. Also logging will become complexer with the shown concept.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      @@DanielOpitz Some will find the real value in this, good enough for me

  • @pawesydorow933
    @pawesydorow933 4 месяца назад

    How it's should work with DDD when we have rich domain with many rules, and method return some value for us?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Errors are a good approach here to describe possible failure scenarios, if that makes sense for your use case.

  • @peeetee6012
    @peeetee6012 4 месяца назад

    The tests have a weak point - they run locally. If you add real network latency, even just 5ms, the impact of exceptions expensiveness becomes less important.
    Returning of result object instead of throwing an exception looks ok when the code is trivial, but it becomes nightmare when the code is complex and deep.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      1) Will do the same benchmark with a remote service, and post an update
      2) Regarding deep call chains - I agree. Which is why I keep it as shallow as possible. Typically I have an endpoint-handler-entity. Comes out to passing the result 3x at most. The entity could also return void.

  • @compman73
    @compman73 4 месяца назад

    Awesome same always

  • @patfre
    @patfre 4 месяца назад

    I noticed you use “sealed” a lot and I will just want to let you know this is not the best thing to do. According to Microsoft themselves you should only ever use sealed if and only if there’s an inherent security risk to not this might be classes that are unsafe in nature or handle sensitive information. They also directly say that the performance gain you gain from it is not good enough of a reason to do it as someone might need to extend it later.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      When they need to extend it, they can remove sealed. Just coding style, I like using it. Not forcing it up any one's throat.

  • @portusdelphini
    @portusdelphini 4 месяца назад

    Absolutely true for c++ thou

  • @tomtoups
    @tomtoups 4 месяца назад

    in modern processors, the context switch to Ring 0 or Kernel mode that happens when an exception is thrown is not as expensive as it used to be. For example, on x64 processors, the SYSCALL op code was added for this transition rather than the software interrupt op code. In many cases the exception doesn't even make a context switch at all. Now that doesn't mean you should be throwing or catching exceptions willy nilly, because that's bad for other reasons

  • @redcrafterlppa303
    @redcrafterlppa303 4 месяца назад

    I prefer the split rust does way better. In situations the application can't recover. The application simply terminates with a message set at the location the application fails. For everything requiring more graceful handling it uses the result type to let the calling function handle that condition.
    This as well provides the fail fast strategy as you can return a failing result in your guard clauses.
    With provided syntax to get the successful value with just 1 character of source code it's quite easy to continue with the happy path without any thought about the error.

  • @necromancer151
    @necromancer151 Месяц назад

    just hint for all dev evangelist. Stop solve problems what nto a problems and make code simplier. We have bigger issues with our codes than db optimization to milliseconds or hanting down exceptions proper handling. it's like hanting down for extra ";" in code. Just stop it and do something useful instead. PS removing exceptions from code must be condier as antipattern due to increase code quantity and code quality

    • @MilanJovanovicTech
      @MilanJovanovicTech  Месяц назад

      I respectfully disagree. But what content do you suggest I make? What do you consider useful? I'm dying to know.

    • @necromancer151
      @necromancer151 Месяц назад

      @@MilanJovanovicTech maybe something from real life? architectural design? maybe new features from java or edge cases? Review of common patterns/antipatterns? Database design best practices? tools overview... a lot.. let me know if you need more suggestions :P

  • @MAUIMS-m4g
    @MAUIMS-m4g 4 месяца назад

    please sir, regarding DDD and aggregate like Order and OrderItems all methods for adding , updating and removing occurs on Frontend then sent back to backend as a patch to persist in database ,so DDD logic will be in Front-end not Back-end right?
    i.e DDD logic will be in JS or TS in case of react or angular.
    or C# but in DTOs in case of Blazor. Right ? or i misunderstood ?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Looks like you got just CRUD

    • @MAUIMS-m4g
      @MAUIMS-m4g 4 месяца назад

      @@MilanJovanovicTech
      API is CRUD !!!
      Post Get Put Delete

  • @fcoder1
    @fcoder1 4 месяца назад

    This is just a bs. If exceptions are too expensive for you, you should consider another language like rust or c

  • @enricoroselino7557
    @enricoroselino7557 4 месяца назад

    i prefer using manual result pattern, i also use it in flutter so i dont need to use dartz Either package... thank you for the benchmark ❤

  • @isacribeiro3071
    @isacribeiro3071 4 месяца назад

    Valeu!

  • @rafaspimenta
    @rafaspimenta 4 месяца назад +6

    For me, exceptions should be the default approach. Only use results when performance becomes an issue

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +4

      Even MSFT doesn't recommend using exceptions for flow control

    • @99aabbccddeeff
      @99aabbccddeeff 4 месяца назад +2

      Agreed. If it is really needed, go with a result approach. Using the result approach everywhere, it's like avoiding LINQ because it is slower than usual iteration over collections.

    • @vmia159
      @vmia159 4 месяца назад +2

      Agreed. Throwing exception and catch with exception handler introduced in dotnet 8 just made the code looks cleaner as you can return error response anywhere and stop the execution. To me result pattern is just like props drilling in React where you pass the error from the bottom layer by layer until you reach the top.

    • @CesarDemi81
      @CesarDemi81 4 месяца назад +1

      That's terrible advice. It even makes the code harder to follow and understand since you have to track what catches it and how it handles it. Not even getting started on how to debug that.

    • @gabrielalejandrobernalpere3018
      @gabrielalejandrobernalpere3018 4 месяца назад +2

      Void is a most bigger lie in a software development. Any function, in a mathematical context, must be return a value. Void is a misconception introduced by POO, an bigger mistake. Also, the truly problem are the layers that only increases the change costs in effort terms. If you need make a change and it's necessary to review many layers, it's a problem, not by errors handling, rather than layers. And a exception, as their name it indicates, represent a truly exceptional behavior, is not for any error. The exception is a very poor control-flow mechanism because encourages break the main flow.

  • @egozMaster
    @egozMaster 3 месяца назад

    oh just your opinion, and I think you dont work for any company

  • @tehwabbbit
    @tehwabbbit 4 месяца назад

    Do not do this... It goes against clean code, it adds complexity to a part of the system that os supposed to be clear, and the consuming code has to follow a different pattern. This is dangerous advice imo.
    Its a good thought exercise, but thats all

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +3

      @@tehwabbbit If anything, exceptions go against clean code. They make a method dishonest. The caller doesn't know from the method signature if a method can throw an exception

    • @tehwabbbit
      @tehwabbbit 4 месяца назад

      @@MilanJovanovicTech this is what XmlDoc comments are for in method headers, read by all IDEs automatically and standard practice in enterprise (and in many nuget packages), and if it's an endpoint utilising the data annotations that can feed into an OpenApi spec to state the possible returns.
      The only scenario it doesn't fulfil is a JS call to the backend catching the exception with the .fail() but arguably if you catch all exceptions on the server and return an error array in your DTO that's much cleaner for the consumer anyway.

    • @iliyan-kulishev
      @iliyan-kulishev 4 месяца назад

      @@MilanJovanovicTech Unless you mention that in the method summary. But then how do you do this for, let's say, a particular implementation of a given abstraction ?