I highly recommend using TRY/CATCH blocks and RAISERROR instead of @@ERROR/@result checks. I have a blog entry that shows how to properly use transactions and TRY/CATCH blocks, including nested transactions to revert only the failed procedure call work so that the calee can resume a different path and continue the transaction, if it feels like: Exception Handling and Nested Transactions.
<Update>
You are being inconsistent with regard to procedures return mode. UpdateTHIS and UpdateTHAT return 0/1 as a return value, while the wrapper DoStuff returns as a result set (SELECT). It means you cannot write DoMoreStuff that calls DoStuff because it has to use INSERT ... EXEC to capture the result, and you'll quickly find out that INSERT ... EXEC cannot nest. I recommend using RETURN @result instead, for consistency.
</update>
I also have an unrelated commend, which is just an element of style: I find long IF... ELSE IF... ELSE IF ... ELSE IF ... blocks difficult to read and follow. I always found that expressing the same as a DO ... BREAK ... BREAK ... BREAK ... WHILE (FALSE) is easier do read. T-SQL does not have a DO ... WHILE construct, so a WHILE ... has to be used instead:
BEGIN TRANSACTION
WHILE (1=1)
BEGIN
EXECUTE @result = dbo.UpdateTHIS @ID = 1;
IF @result != 0
BEGIN
ROLLBACK;
BREAK;
END
EXECUTE @result = dbo.UpdateTHAT @ID = 21
IF @result != 0
BEGIN
ROLLBACK;
BREAK;
END
...
COMMIT;
BREAK;
END
Again, this is no important as is just a code formatting style, but is a suggestion in case you agree that it results in code that is easier to read.