Developers, don’t be afraid to exit or panic
A 5 minutes story written on Sep 2018 by Adrian B.G.
If a function or process should not continue, it is OK to “exit”!
The issue is more prominent to junior level programmers, but I can assure you that it is not exclusive to them.
I have found this “problem” at two main stages: at the function and at the process level. I will try to “treat” both of them in the same article. I will use the exit keyword to signal a function return or a process exit. Error can be an exception, an error or an exit code.
I think that, we developers, have a psychological issue with the “downstate”. We build “things” that are supposed to run forever, we often only take into consideration the golden path.
I think it is hard for us to write the code that will disable/shutdown our “baby” (process).
I. Process level
I identified a few basic examples, in which I think it is OK to close the process.
Cannot fulfill ANY of my functions
I think that this rule applies to the majority of cases: if you cannot achieve your main functionality try harder, degrade your service and then exit with a bang (error).
In case of a partial failure you should throw errors without exiting, hopefully, an alert will be triggered and a developer will respond. Example: you have to respond to a request with data from 2 sources, and only one of them is inaccessible.
It should not be a common problem, but I want to mention it anyway. The most obvious case is a CLI tool. Validate and sanitize your input, and if one of them is invalid you can return with an error. Other example: an invalid connection string to the database. The input refers to all its forms: command parameters, environment variables, config files.
If your program’s only function is to parse a file or use a resource that is missing or cannot be accessed, there is no reason to continue, exit with an error. Fail fast so the users can notice.
When closing a process in error, usually is enough to exit with a status/code of 1, but if you have multiple failures you can use other positive numbers, just make sure you communicate that to the clients, trough a help command or the documentation.
II. Function level
Like I mentioned at the beginning, the “fear” of quick exit from a function when it cannot fulfill its purpose should not exist.
Do not stale or return the wrong result, rather tell your caller that something is wrong.
Asserts can be a powerful weapon, but not all developers agree with them. It is an easy and obvious way to check that all the assumptions are true before executing your primary functionality. Two things I want to mention here:
- return a meaningful error message
- do not overuse them (they are not a placeholder for tests)
You can write “vanilla” asserts as well
if (myCond == false) return error; but a specialized framework will make your code simpler, in my opinion.
Do not forget to return an error or throw an exception if you exit a function earlier, and you could not “achieved your duty”. As for how the code should look, I like to keep the line of sight as left as possible and the happy path at the end**. More about this: Should I return from a function early or use an if statement?
When accessing a resource (file, API, database, random number generator …) you can use this technique. Meaning if you failed to access the resource, you can try again (if you are not limited by a time deadline) using an exponential pause. Example: you cannot access the database, try again 3 more times and then exit with an error. The first retry could be done after 10ms, the 2nd after 600ms and the 3rd after 2 seconds.
For more details about how to implement and why to do it, see:
Exponential back offs can introduce “cascading failures” to your services. The requests wait, the queues fill up, the new requests are rejected, and the resources will have a hard time recovering with all those retries coming on them.
The opposite to exponential back off is to avoid a retry and fail as quickly as possible. You can read more about these 2 topics:
If you own a lock, started a transaction, or blocked a resource do not forget to release it before exiting.
If you want to have a HA service, and only a part of your functionalities are affected, you may want to keep your process running, and respond with what you have.
A partial response is better than none, most of the times.
If your app instances are controlled by an auto load balancer, or Kubernetes, you should think twice before ending the process. A new identical sibling will be launched and most likely it will have the same problem as the closed process. You do not want to have all of your instances up&down each second, creating havoc in your clusters.
If you have a problem, do not forget to tell your clients too. In the case of a web service you should also use the proper HTTP codes:
Conflict with the “one return” rule
When possible, it is nice to have only one return statement in a function, but do not decrease the quality of the code only to achieve this. I prefer to have many returns than a 5 indentation level function and a few more temporary variables just to stick with this rule. A more comprehensive article about this:
I curate a list of articles, talks and papers for one/two times per month. They are mostly related to computer science, distributed systems, databases, Go, containers and Cloud solutions.