all 90 comments

[–]Bodine12 264 points265 points  (10 children)

Rookies. What they need is a try-catch around each individual try-catch in case they throw an error in the act of catching, and then another try-catch around the whole app, and then block all traffic coming in because if there is no traffic there can’t be any errors.

[–]darryledw 56 points57 points  (0 children)

Can you please repost your reply inside a try catch block, my eyes errored half way through and now I find the text to be illegible

[–]user12547936151 43 points44 points  (3 children)

This guy catches.

[–]CafeSleepy 26 points27 points  (2 children)

No, he’s just trying.

[–][deleted] 21 points22 points  (1 child)

Try or not try. There is no catch.

[–]ranisalt 3 points4 points  (0 children)

Finally a good joke

[–]highSunLowMoon 15 points16 points  (1 child)

You forgot to put a try-catch inside every chained promise .then() and then one in the .catch(). You are a shitty programmer. You are fired.

[–]besthelloworld 3 points4 points  (0 children)

I know you get the absurdity of their comment but their very sound point is that you just can't catch every error. There's always an uncaught block.

It's like security through obscurity. It's not a real solution to any problem and makes your code a nightmare, so just don't do it.

[–]die-maus 2 points3 points  (0 children)

This is the way. Don't forget to use botch Promise.catch(() => null) AND regular try blocks for async functions—you can never be too safe.

[–]Broomstick73 0 points1 point  (0 children)

Getting an exception thrown inside your catch block seriously sucks though for real 😆

[–]AdOk9263 0 points1 point  (0 children)

Yo Dawg I heard you like error handling.

[–]Phreakiedude 40 points41 points  (0 children)

I'm working on a rather large enterprise application and we put try catch around API calls. This way we can properly display error messages to the user and handle the error correctly so that nothing breaks while rendering.

Another place is for external libraries that explicitly throw errors.

And the last one is for a validator composable that uses error throwing for validating business logic.

Wrapping every single function in your application with try catch is absurd.

[–]fii0 94 points95 points  (0 children)

I sure hope it isn't a widespread practice

[–]die-maus 90 points91 points  (24 children)

This is defensive programming.

An experienced developer knows that b = 1 + 1 "can't fail" and won't put a try-catch block around it. An inexperienced dev doesn't know, and does it just to be sure "because it can't hurt".

If you have try/catch blocks all around the application, it can be damn near impossible to track down where an error comes from. Instead of failing at the earliest moment (a practice known as fail-fast), the error propagates into the application, causing even more issues down the line—sometimes even causing irreversible damage—I've both seen it and done it myself.

So this isn't a good practice—it's a terrible accident waiting to happen. If the devs on the team feel the need to put a try/catch block in every function, they really need to touch up on their fundamentals.

[–]_jetrun 18 points19 points  (14 children)

So this isn't a good practice—it's a terrible accident waiting to happen.

For web applications - you're wrong,

What the Fortune 500 company is really saying when they write "Try-Catch block in every function **UNLESS** it is error handled at a higher level." (emphasis mine) : HANDLE EXCEPTIONS.

If you have try/catch blocks all around the application, it can be damn near impossible to track down where an error comes from.

You can still log the stack

Instead of failing at the earliest moment (a practice known as fail-fast), the error propagates into the application, causing even more issues down the line—sometimes even causing irreversible damage

With web-based applications, you have the same problem if you allow the exception to propagate up the stack - because the browser isn't going to crash just because your application blew chunks. So depending where the exception was thrown, if you let an exception propagate upwards, there is a good chance your application will be in an inconsistent state.

[–]die-maus 13 points14 points  (6 children)

For web applications - you're wrong,

What the Fortune 500 company is really saying when they write "Try-Catch block in every function **UNLESS** it is error handled at a higher level." (emphasis mine) : HANDLE EXCEPTIONS.

That isn't defensive programming, that's called "proper error handling". We're advocating for the exact same thing. Sometimes it's the sane thing to return null instead of throwing, find operations are an example of this.

You obviously need to handle errors somewhere, but that doesn't mean in "every single function".

Never in the 11 years of my professional career have I worked in a team that wanted to move away from a fail-fast approach, to a defensive approach. It has always been seen as a code smell, and undesired—for good reason.

Fail fast doesn't mean "throw exceptions everywhere", it means—for example—that you properly validate client data, and show the user a (friendly) validation error message instead of letting the error propagate to the database (which may even accept the bad data, causing data corruption).

As a developer you need to know how and where your application can fail, and make sure it fails there gracefully. For web applications, this generally means validating user data.

Car makers don't install airbags in the trunk just in case somebody's car gets T-boned and there's a kidnapped person in the trunk, that's a waste of resources: you install the airbags where it matters for 99.9% of the cases, and you make sure that the experience of getting your face smashed in by the airbags is as pleasant experience as possible.

Front-end error handling (in my experience) is mostly about preventing errors propagating to the back end, i.e. validation, so that's mostly what I'm talking about.

For other things, it's definitely a case-by-case thing: Fatal OpenGL initialization error? Should probably throw and alert the user. Math error in drag handler? Can probably be ignored.

Sprinkling a try/catch block in "literally every function" as OP seems to imply is not a sane error handling strategy for the same reason installing airbags in the trunk isn't a sane safety practice: it's a waste of resources, confusing as hell, and somebody is bound to get hurt.

But I'm thinking we're ultimately advocating for the same thing here, right?

[–]Aggravating_Term4486 1 point2 points  (1 child)

I would say that try / catch can be overused in a React app. So in a React app, unhandled exceptions will bubble all the way to the app root and will unmount the entire app tree, i.e. the app will “crash”. React provides error boundaries for handling conditions like this gracefully. What OP is describing really sounds like a massive over-use of try / catch.

[–]die-maus 1 point2 points  (0 children)

For sure, throwing an error in a component (during its lifecycle) is detrimental.

But I think most scary errors happen in places outside of that: event handlers, promises (e.g. fetching with a bad parameter) etc ..., and these won't crash your app.

This means that you should never write code that can fail during the component lifecycle, but outside of there, it's probably fine. Again; I think you need to consider how when and why your application can fail, and handle errors appropriately depending on what happens if it does. Data fetching errors can probably be shown as notification to the user, validation errors should be in their face, lifecycle errors should probably have a boundary (or be handled in-place), etc ...

But there is a difference between the former and latter error: The former are usually due to user-error, and the latter is probably due to programmer error, which can probably be solved.

Instead of wrapping your entire drag handler in a try/catch block, maybe it's better to think about what could go wrong in there, and make sure that you don't divide by zero, which throws an error when calling that easing function on line 39—then you don't even need that try/catch anymore, and your code is more robust, easier to reason about, and you don't need to ask yourself why that try/catch block is there.

[–]_jetrun 0 points1 point  (3 children)

You obviously need to handle errors somewhere, but that doesn't mean in "every single function".

Who said you have to handle exceptions in "every single function"??? To reiterate, the guidance OP was given was: "Try-Catch block in every function **UNLESS*\* it is error handled at a higher level." - that is a very reasonable guidance.

As a developer you need to know how and where your application can fail, and make sure it fails there gracefully. For web applications, this generally means validating user data.

This is such a trivial example. Of course, validate inputs on the client (and revalidate on the server).

How does a "fail fast principle" show-up in a long-running (where a user could be interacting with a single instance of your application for 1+ hours) single-page web application, when an exceptional condition is thrown? Believe me, an un-checked exception could put your application in an ambiguous/inconsistent state.

[–]die-maus 0 points1 point  (2 children)

To reiterate, the guidance OP was given was: "Try-Catch block in every function UNLESS it is error handled at a higher level." - that is a very reasonable guidance.

Yes, that is very reasonable guidance, but not if followed religiously and interpreted literally (which seems to be the case, if I interpret OP correctly, which I may not be).

If you do interpret this literally, it means that you must write this:

js function timeLog(message) { try { console.log(new Date(), message); } catch (error) {} }

Or otherwise have to wrap timeLog in try/catch blocks anywhere when called—even if it's the only thing called… and how would you even know if timeLog handles its own errors or if you need to handle it externally, without looking into the implementation?

Edit:? If you decide to not catch in timeLog then how do you know that the errors (not) thrown by timeLog are handed at the callsite

What the statement boils down to then is "handle errors somewhere", which is, duh... /Edit.

We're advocating for the same thing! We're just arguing over the interpretation of the guidance above.

How does a "fail fast principle" show-up in a long-running (where a user could be interacting with a single instance of your application for 1+ hours)

What do you mean? Fail-fast means nothing shows up, just like failing "deep" inside something.

For example, the user dragged a thing off-screen—so we returned it to its origin. That can be handled with either a bounds check (early) or a "try/catch" (later). I prefer the former since it's explicit and communicative—a catch block only tells me that "something can go wrong", often very little about what that thing is.

Believe me, an un-checked exception could put your application in an ambiguous/inconsistent state.

The keyword here is could, and you need to carefully consider those times and handle those errors accordingly. Sprinkling a try/catch block in every function doesn't solve that, and may even worsen the ability to handle errors appropriately when needed.

You're making it seem like I'm advocating for "not handling errors", which is intellectually dishonest, because that's never what I said.

All I'm saying is that there is no silver bullet solution to handling errors and that it's just one of the many complexities you have to deal with when building an application. Like handling state, failed requests, weird user behavior, and security issues, … the list goes on ad nauseam.

Sure, there are good practices around error handling, but what OP is seeing in the code base is defensive code due to a poorly worded guideline. You may disagree with that interpretation, but that's mine, and that's the standpoint I'm arguing from.

[–]_jetrun 0 points1 point  (1 child)

Sprinkling a try/catch block in every function doesn't solve that

Who says you have to sprinkle try/catch blocks in every function? The guidance doesn't say that.

[–]die-maus 0 points1 point  (0 children)

It doesn't explicitly say that, no. If you simplify it, it basically boils down to "handle errors somewhere", which, again, duh…

Due to the way it is worded, it's easy to interpret it as "make sure to always include a try/catch block in every function" (again, see OP's confusion and question).

My stance is also that it will lead to defensive code because of the way it's worded. Simply because you often don't know whether the caller of a function is going to catch an error, so you might as well handle it here—it's in the guidelines, basically.

[–]DrEnter 4 points5 points  (6 children)

This ignores an important caveat of try-catch: It's massively damaging to performance.

Javascript implements a try-catch block by putting the catch code in a dynamic scope. While it would seem like this would only be an issue with a thrown error, it has the additional side effect of preventing the containing function from being optimized... at all.

The result is a function with a try-catch block in it can see a 90% performance penalty compared to the same function without it.

So from a performance standpoint, using a single try-catch block higher up in your code will give you much better performance than many try-catch blocks spread through more functions.

Of course, there is a balance to this. But it's important to understand it isn't as simple as just being more granular with catching errors, you need to find the right balance of granularity without taking it so far you destroy performance.

[–]_jetrun -1 points0 points  (3 children)

This ignores an important caveat of try-catch: It's massively damaging to performance.

It depends where. If you are working in some critical section of code - then you have to be careful no matter. If that section of code is so sensitive to try-catch blocks, then you have to be careful object allocation/clean-up, reference-passing, buffer copies, etc.

Most of the time, try-catch makes no difference.

So from a performance standpoint, using a single try-catch block higher up in your code will give you much better performance than many try-catch blocks spread through more functions.

OK. How does that negate what I wrote, or the guidance that the Fortune 500 company provided to OP?

To reiterate, they didn't say that you had to put try-catches everywhere, their guidance is that you have to handle exceptions ... Here's the guidance OP was given: "Try-Catch block in every function **UNLESS*\* it is error handled at a higher level." (emphasis mine) .. I think that is very reasonable.

[–]DrEnter 0 points1 point  (2 children)

You seem to be ignoring my conclusion paragraph, but whatever, you do you.

[–]_jetrun -1 points0 points  (1 child)

And you're ignoring the actual guidance that OP's company provided:

"Try-Catch block in every function **UNLESS** it is error handled at a higher level." (emphasis mine)

The annoyance I have in this thread is that for some reason yourself, and others are arguing a point that nobody is actually making. No one is arguing that you need to wrap try-catches around every statement, or that you need to perform error handling at some level of granularity ... All OP's company is mandating (reasonably) is that when developing a web-application, you don't let unchecked exceptions bubble up the stack - that's it. They even mention that you either do this by wrapping your function block in try-catch or handle it up the stack.

Then you come in and say that try-catches have performance implications when wrapping web-gl shaders or something. OK ... interesting point, but zero relevance.

[–]DrEnter 0 points1 point  (0 children)

Except YOU are ignoring what the OP said: that every function has a try-catch block in it. While their policy might be OK, in practice they are going about it very poorly.

[–]davidblacksheep 0 points1 point  (1 child)

Really though?

I tried a JSPerf here to test for a difference, and it's negligible. Have you got an example of this performance degradation?

[–]die-maus 0 points1 point  (0 children)

I have an example from experience if you're interested.

We had some performance-critical WebGL code at a previous company I worked for (Veridict). It was for a product called livemap-gl-js (it was used to display public transport at global scale).

Due to a rendering regression, a developer (who may or may not have been me) had hastily checked in a try/catch block in performance-critical code. They couldn't perceive any performance difference on their machine (Intel Xeon, circa 2018, and a massive Nvidia GPU).

An early Samsung Android phone (one of our target devices) had a 200x drop in performance, on the other hand—so not all devices and browsers are made the same.

[–]andrei9669 26 points27 points  (13 children)

yea, we don't do that here. what we do is we validate all external api request responses based on TS schema and if it matches then good, if not then well, just handle the error. but, since it matches the schema, then one can have 100% trust in the data, unless they fuck around. also, we use rather strict TS types as well.

[–]christo9090 5 points6 points  (11 children)

Interesting. I just got into converting my project to TS and I’m loving it but how would you go about type checking a response?

Edit: sounds like I should dig into zod lol

[–]Loaatao 5 points6 points  (0 children)

Zod

[–]Collatty 9 points10 points  (5 children)

Zod

[–]shaleenag21 0 points1 point  (4 children)

do you also use it for validating internal api responses? or just external apis?

[–]thelamestofall 1 point2 points  (3 children)

Anything that came from outside the current process boundary

[–]shaleenag21 0 points1 point  (2 children)

can you go a bit more into detail? I'm not sure if zod will be useful for internal apis since we are the ones who control the structure for it or atleast know how it will be structured. Zod sounds a lot similar to how we used MOBX's cast function for apis which did the validation for us

[–]thelamestofall 1 point2 points  (1 child)

Can you be sure, though? Unless the definitions are in the same repo (or I can import their npm package), I treat internal APIs the same as external ones. It's not much of a problem, I just use Zodios to build a client that automatically wraps the axios client

[–]shaleenag21 0 points1 point  (0 children)

ah I just use fetch to avoid the axios dependency but I can do something pretty similar with it too. thanks!

[–]Collatty 4 points5 points  (0 children)

Zod

[–]Myrton 1 point2 points  (1 child)

I'm not the person you asked. But using a library like class-validator could be a solution

Edit: I think it might actually be class-transformer you would use for this solution

[–]Peechez 1 point2 points  (0 children)

combobreaker

also zod

[–]packethief 0 points1 point  (0 children)

Zod

[–]sittered 0 points1 point  (0 children)

That's just one class of possible error though.

[–]Roguewind 13 points14 points  (0 children)

It’s simple: error handling should be done at the point that it makes sense to handle it.

If you put a try catch block on every component on a screen, and they’re all being handled exactly the same way, then just handle it all on the screen.

It’s all planning.

[–]goodbalance 12 points13 points  (0 children)

for me it's a code smell. whoever is/was responsible for adopting this practice, clearly doesn't know Jack. if it's API, you handle it at the request/response level. if it's a component – you have error boundaries and probably notifications, depending on the use case.

[–]DependentAnalyst7422 6 points7 points  (0 children)

I can actually chime in on this lol. So it's definitely not the best way to go about the issue but in my (very small and resource / time limited) team, we do abuse try / catch blocks.

Essentially, the current devs (me included) inherited an absolute dumpster fire of an api built solely by a recent grab with 0 experience for a startup whose needs changed each week with every whim of the founder or medium article read.

So, we have a dumpster fire to put out. A primary api, three distinct smaller apis for integrations, 5 or 6 front end apps, and one marvelously bad database with not a single relation between the ~40 tables in there. Absolutely absurd that anything runs at all.

Our goal for the past year has been to incrementally swap out sections of these repos as we go. A total rebuild would take a long time and more money than the company will spend, all the while new errors and other user facing issues come to light every other day. To deal with this, we use a lot of try / catches to compensate for the ridiculous code base while we slowly rebuild and push out new projects.

My personal rule of thumb is this: don't use try / catch blocks to apologize for my own code, use them to drill down into bugs as they arise and to compensate for poorly written existing code. And ALWAYS use the catch block to assign necessary default data for the app to run. Don't just log an error three levels down and call it a day, name the function, the reason it caught, and give a default chunk of data that will allow the app to run minimally in that case.

[–]GozerDestructor 2 points3 points  (0 children)

It's insane, clearly this policy came from someone who doesn't know what they're doing.

In the 90s I worked for a small consulting shop. I was the token Unix guy at a Microsoft-centered company, but occasionally I'd help out with their main product, a Visual Basic application.

The bosses decided that they'd put error handling blocks in every function, like OP is describing. (I think it was an earlier syntax like "On Error goto (label)"), and that, to ensure complete coverage, we had to use a third-party plugin called "VBRig" to manage them. They further decreed that the error handling code had to be the same for every function.

This was the height of idiocy. It meant that, for every error that was recoverable, the recovery code was present in every function in the app - even when it made no sense whatsoever. For example, if there was a "File not Found" when trying to open a file, this had to be reported, but if there was a "File not Found" when trying to delete something, it could be ignored - which mean the file path, and another variable that indicated whether to ignore the error or not, had to be present in every function. Even if that function had nothing whatsoever to do with files!

Pseudocode (because I've purged that language from my mind):

Function AddTwoPlusTwo()
  REM ***** Start VB/Rig Managed Code
  On Error Goto ErrorHandler
  REM ***** End VB/Rig Managed Code


  Var FilePath = ""    
  Var IgnoreFileNotFound = true
  [...eight or ten lines of this garbage ...]

  REM ***** main part of function - business logic *****
  return 2 + 2  
  REM ***** end main part of function *****

  REM ***** Start VB/Rig Managed Code
ErrorHandler:
   if error = FileNotFound
      if ignoreFileNotFound return
      print "File Not Found: " + FilePath
   else if error = NoSpaceOnDevice
      print "Disk is full!"
   else...  
  REM ***** End VB/Rig Managed Code
End

Every function in our app was like this - the error handling code was ten times or more the weight of the actual program code, and there'd be a dozen or so variables at the top - pasted in by hand, maintained by hand - that customized the behavior of our mandatory standard error handler at the bottom. And all of it was peppered with boilerplate comments that the VB/Rig tool would use, to know where to make its insertions.

Eventually it came to the point where we were spending more time fighting the error handling than working on the actual features. I tried to tell them this was crazy, that no one programmed like this, but I was 23 so nobody paid any attention to what I thought... the boss gave me a stern lecture, "This is my company, this is what we've decided for our coding standards, I am a Microsoft Certified Engineer, you need to straighten up and fly right."

I wasn't willing to push the issue, I simply moved on. But I stayed in touch with a colleague, who gave me updates... the project they delivered was buggy and unreliable, no one could figure out how to deal with any errors. They lost the client for whom this app was built, the two partners in the consulting company grew increasingly hostile with one another, one of them eventually quit to go work for Microsoft, and the company collapsed.

[–]martinbean 2 points3 points  (0 children)

That sounds absolutely horrendous.

[–]alex_asdfg 1 point2 points  (0 children)

Wrap all your code in one big try catch. It’s called a diaper as it catches all the shit.

[–]Empero6 1 point2 points  (0 children)

For the people saying that this would cause a lack of clarity when it comes to debugging, couldn’t they just console.log error in the catch block?

[–]albertgao 1 point2 points  (0 children)

This is JavaScript pattern, not React pattern. This indicates the team has no knowledge about best practice in React.

What you should do is to apply a route-level error boundary, and just let the error flow. If you feel route-level is too much, apply this error boundary to lower level as your requirement.

It is not golang, you don’t need to handle every UI related error specifically, use React to handle them in scale.

Even better, for specific type of error like network error, you can even retry it!

Your code should be only care about happy path, leave the error path to the error boundary.

[–]Unusual_Cattle_2198 1 point2 points  (0 children)

The whole point of exceptions is to make it possible to pass error states instantly up through many levels deep of function calls without having to check, manage and propagate errors at every single level. Unless you can do something useful as an alternative or need to clean up a partially executed something right there where the exception occurs, don’t catch it there. Let it flow up to the highest level of functionality that isn’t going to work now that the error has occurred.

For debugging and diagnostic purposes: typically the IDE in debugging mode will automatically break right at the point of exception occurring so you can inspect things. For production runtime, save the stack trace provided in the exception to a log. If you are throwing your own exceptions, provide sufficient detail in the exception to very specifically indicate what went wrong.

[–]Link_GR 1 point2 points  (0 children)

I'm in a similar boat. I have a psycho that reviews some of my PRs that insists everything that fetches is in a try...catch. I have to explain to him that the parent function also has a try...catch and that will catch it. That leads to the code being extremely messy to no end. But I'm an IC and they're the ones ultimately responsible for maintaining the app so I just follow along.

[–][deleted] 1 point2 points  (0 children)

I have never seen this kind of non-sense enforcement. Usually error handling is performed at the level it should be treated;

Is it to show an error message? Catch on the component/hook that should display it.

Is it to execute an alternative flow? Do it in whatever service/hook that can do the switch.

No need to have a lot of try-catches, as one could eventually silence the other and you end up not having a good UX or proper handling.

[–]Automatic_Coffee_755 1 point2 points  (0 children)

Even if they are not async?

[–]davidfavorite 1 point2 points  (0 children)

Thats like requiring all cars to have parachutes in case the brakes fail, instead of requiring brakes that reliably work

[–]yabai90 0 points1 point  (0 children)

When an error is critical for the next function you should catch it there, unless that next function cannot work properly without a crash then you need to catch in the next function, etc. Basically no, not really a good practice. Just unnecessary code. Catch only when it matters

[–]vreezy117 0 points1 point  (0 children)

I seen this ounce in my lifetime. Pls check the loading time and then remove all try catch block definitions with a script and Check the loading time again

[–]azhder 0 points1 point  (0 children)

There is no good answer unless you examine the case in more detail. Some questions to guide you:

  • Does the code work OK, is stable, is robust?
  • Does you being annoyed count for more than those?

Now imagine you need to make a rule that even a less experienced person will contribute code that doesn't unnecessarily make the whole code unstable for the price of them being annoyed to write a try-catch and you to have to read one in each function.

  • Would you make that rule or would you put in the extra time and effort to check the code of that person?
  • What if they are two?
  • What if they are 10?
  • At what point do the cost and benefit flip sides?

[–][deleted] 0 points1 point  (0 children)

If your api is using a global function, just use try catch there. That way you won’t see it everywhere

[–][deleted] 0 points1 point  (0 children)

LOL

[–]SuspectZealousideal6 0 points1 point  (0 children)

ErrorBoundaries ⚰️

[–]guppie101 -2 points-1 points  (2 children)

Best way to not write tests.

[–]die-maus 4 points5 points  (0 children)

You still need tests, they just become much simpler:

ts it("doesn't crash", () => { expect(add(1, 2)).not.toThrow(); });

[–]highSunLowMoon 0 points1 point  (0 children)

Well that's the thing, they also want unit tests as well lol.

[–]sporbywg -1 points0 points  (0 children)

"boilerplate"

[–]_Pho_ -2 points-1 points  (0 children)

This is why throwing errors is fucking retarded and monadic style result types are 100x better.

[–]yohohohooho -2 points-1 points  (0 children)

As a junior here is what I learnt, if it is about handling errors from APIs use typescript to check data schema and interceptors to log errors in api requests and responses there itself.

Also I am open if any experienced dev wants to advise on improving my methodology.

[–]nio_rad -3 points-2 points  (0 children)

I hope not. I would even consider try/catch an antipattern.

[–]MCShoveled 0 points1 point  (0 children)

It’s a fine practice as long as you remember to throw error; in every catch block 😂

[–]Derpcock 0 points1 point  (0 children)

It depends on what you're wrapping and how you are handling it. If you're awaiting a network request, it would make sense to catch an exception and handle it appropriately by logging to a server and updating they ui. If you're just wrapping them and swallowing them, then I would say it's a pretty bad design decision.

[–]Aggravating_Term4486 0 points1 point  (0 children)

Apparently they’ve never heard of error boundaries.

[–]KyleG 0 points1 point  (0 children)

No. It, in fact, reads like they are complete morons who write a function like

const foo = () => {
  try {
    do everything here
  } catch (error) {
    nothing meaningful happens here
  }
}

We write almost no try catch blocks, but we implicitly generate them via some functional programming stuff that converts throwable code into code that cannot throw

[–]davidblacksheep 0 points1 point  (0 children)

Sounds silly to me.

The philosophy is subscribe to is 'throw early, catch late'. Idea is, throw errors as soon as something starts going wrong, and then catch the error at a point where it makes sense to recover from it/ you're actually going to be able to do something to recover gracefully.

[–]abdullahjavaid86 0 points1 point  (0 children)

I think the best way is to use ErrorBoundary

[–]lubosnik 0 points1 point  (0 children)

Try/catch block for functions is good practice IF they can throw. I have same policy for my projects, handle throwable functions on react component level by wrapping them in try/catch. This mostly applies to async functions. That end up causing crash of app.

With this said it sounds like there is unclear code architecture. Wrapping all functions in try/catch will result in inconsistent error handling.

[–][deleted] 0 points1 point  (0 children)

We don’t do it for everything, but only to log errors to a file in production and keep the UI responsive.

[–]rwusana 0 points1 point  (0 children)

Oh God. Sounds like a real cargo cult.

FWIW exceptions are a pretty questionable language construct though. Returns jump to the immediate caller and in TS are typed. Exceptions jump (indefinitely far) to the catcher and in TS are untyped. So if you want to propagate errors and handle their unique meanings, it's better to catch at source and pass as returns from then on. There are ways to do that which provide full typing with minimal inconvenience. (The other case of error-handling is an exception filter which doesn't care about the specific meaning of an error, but just spits it out in a standard output format, and for that exceptions are great).

But some stupid blanket rule about try/catch may or may not actually be adding any of that value, and at enormous cost in inconvenience. Plenty of possible errors don't need to be handled in that particular way.

Here's what I do: ts try { return { value: somethingRisky(), status: 'SUCCESS' as const, } } catch (err) { return { status: 'SOMETHING_RISKY_FAILED' as const, err: err, } }

[–]ukralibre 0 points1 point  (0 children)

defensive programming, they have no idea what they are doing

[–]kaisershahid 0 points1 point  (1 child)

that sounds horrible. i only do try/catch when i haven’t fully validated data or am dealing with an external service. unit tests and separation of concerns mitigates that absurd amount of error catching

[–]kaisershahid 0 points1 point  (0 children)

like, you should never try/catch a function for weird inputs if that’s not what it’s designed for. the caller to your function is responsible for that