There might be good use cases for `goto`, but this is not one of them. It's best when code reads from top to bottom. The `goto` statements break that readable flow. Since the jump-to labels are quite far, I'm forced to scroll to the end to see what they do. After I scrolled, I find some code using variables defined at the beginning, which I haven't memorized earlier, so now I have to go back to top. Effectively I'm making several jumps back and forth to understand how this works, and then a few more jumps to verify carefully that it should work well. This is a nightmare, and it doesn't have to be. If you replace the `goto` statements with the code at their jump-to labels, the code becomes: if (pipe(in)) { return -1; } if (pipe(out)) { close(in[0]); close(in[1]); return -1; } if (pipe(err)) { close(out[0]); close(out[1]); close(in[0]); close(in[1]); return -1; } This is a lot more readable: everything I need to understand these guard statements are right there above. You probably don't like the duplication when closing `in`, and again later in the code if `fork` failed, closing `in` and `out`. It's true that code duplication is not good, but I think it's the lesser of evils here, and it's outweighed by the improvement in readability. --- Avoid comments when possible. Instead of a comment, try to code in a way to make the comment unnecessary. For example: > /* This section is never be reached without a goto */ This comment could be replaced with this statement: return pid; Not only this is short and sweet, it has the great benefit that since it's code, it is enforced: the lines below it will not be reached, no matter what changes in the code above the `return` statement. And we're kind of back to the pesky `goto` statements. Without the `goto` statements, this concern here about the comment and the logic wouldn't exist in the first place.