As a point of reference it's not hard to make Times iterative, you just need to put everything inside a single function call:
ClearAll[g] g[0, total_] := total; g[n_, total_] := g[n - 1, total*n] g[n_] := g[n, 1] Block[{$IterationLimit = 30000}, g[5000] === 5000! ] True
Critique of Leonid's method
This has now been fixed. I shall preserve the timings below using Leonid's old method simply as a reference and an example of the large effects of relatively small changes.
I think it needs to be pointed out that while Leonid's meta-programming method is quite convenient it has an extremely high overhead in the case of low complexity recursion (iteration).
Consider these timings:
ClearAll[f, g] def[ f[1] := 1 ] def[ f[n_] := n + f[n - 1] ] g[n_] := g[n, 0] g[0, tot_] := tot g[n_, tot_] := g[n - 1, n + tot] $IterationLimit = 1*^6; SetAttributes[timeAvg, HoldFirst] timeAvg[func_] := Do[If[# > 0.3, Return[#/5^i]] & @@ Timing@Do[func, {5^i}], {i, 0, 15}] Table[ {n, g[n] // timeAvg, f[n] // timeAvg}, {n, 5^Range[0, 7]} ] // TableForm[#, TableHeadings -> {None, {"n", "Manual", "Meta-programming"}}] & 
Most troubling is the apparent computational complexity of the meta-programming method:

(Note the logarithmic scale.)
Again, this complexity problem has been corrected.