Error and log handling should not bury the real code logic. The repetitive code should be kept minimal.
Java
try {
FileInputStream input = new FileInputStream("input.txt");
FileOutputStream output = new FileOutputStream("output.txt");
copy(input, output);
output.close();
input.close();
} catch (Exception e) {
writeLog(e);
throw new RuntimeException(e);
}
there are couple things wrong here, from resource management point of view
input should be closed no matter copy failed or not
output should be closed no matter copy failed or not
if copy failed, the new output file should be removed from disk
from debugging point of view
the exception thrown contains low level error message, it might be confusing for the api caller or end-user.
there is no context added the exception, for example the output file path might be important
the log does not have correlation id with other logs or the end-user error message
the log does not have context info, for example the output file path might be important
to make the code production ready, we might bloat up the code like this
try {
FileInputStream input = new FileInputStream("input.txt");
try {
FileOutputStream output = new FileOutputStream("output.txt");
try {
copy(input, output);
} catch (Exception e) {
if (!new File("output.txt").delete()) {
writeLog("failed to remove copy target",
"target", "output.txt",
"traceId", traceId);
}
throw new RuntimeException("failed to copy input.txt to output.txt", e);
} finally {
output.close();
}
} finally {
input.close();
}
} catch (Exception e) {
writeLog("failed to remove copy target",
"exception", e,
"target", "output.txt",
"traceId", traceId);
throw new RuntimeException("failed to copy input.txt to output.txt", e);
}
This is actually a best case scenario. If you have extra monitoring/benchmarking code needed, and can not do the monitoring/benchmarking in the writeLog sub function, there will be more "non-functional" code around the actual logic copy(input, output)
The code has following issues
open input and close input is far apart
open output and close output is far apart
create output file and remove output file is far apart
It is very easy to forget close or remove when job is done or job failed. Java 7 introduced try-with-resource to remove finally
try(FileInputStream input = new FileInputStream("input.txt")) {
try(FileOutputStream output = new FileOutputStream("output.txt")) {
copy(input, output);
} catch (Exception e) {
if (!new File("output.txt").delete()) {
writeLog("failed to remove copy target",
"target", "output.txt",
"traceId", traceId);
}
throw new RuntimeException("failed to copy input.txt to output.txt", e);
}
} catch (Exception e) {
writeLog("failed to remove copy target",
"exception", e,
"target", "output.txt",
"traceId", traceId);
throw new RuntimeException("failed to copy input.txt to output.txt", e);
}
GOAL
Error and log handling should not bury the real code logic. The repetitive code should be kept minimal.
Java
there are couple things wrong here, from resource management point of view
from debugging point of view
to make the code production ready, we might bloat up the code like this
This is actually a best case scenario. If you have extra monitoring/benchmarking code needed, and can not do the monitoring/benchmarking in the writeLog sub function, there will be more "non-functional" code around the actual logic
copy(input, output)
The code has following issues
It is very easy to forget close or remove when job is done or job failed. Java 7 introduced try-with-resource to remove
finally
Go
the same function implemented in go
Go error handling also has similar drawbacks
os.Create(dst)
andos.Remove(dst)
is far apartShiti
handle err
likes the catch clause, but its will throw the error up again, instead of really catch it.If the error need to be "caught" and keep the code execution going, we need to use
check