I usually write code like this:
function MyFunction(val1, val2, ...): TOutput;
begin
// set the default output value
Result := 0;
// make sure the inputs are valid
if not(val1 valid) then Exit;
if not(val2 valid) then Exit;
// do the process
if somethingbad then Exit;
Result := process output;
end;
I always avoid try..except block, I personally think it is not good to show that 'panic' message to users. In the old DOS days I use IOResult, now I use all the think I can to check if the process may go wrong.
I am okay to use try..finally block to free the object/memory if I perform some hardware operations, like disk operation. But I usually will check the existence of the target/source file, the free disk space, etc first before performing that 'dangerous' operation.
Maybe not the best, but that it is what I usually do.