Comment by bccdee
2 months ago
> Each step modifies state that the next step needs.
I've been bitten by this. It's not the length that's the problem, so much as the surface area which a long function has to stealthily mutate its variables. If you have a bunch of steps in one function all modifying the same state, there's a risk that the underlying logic which determines the final value of widely-used, widely-edited variables can get hard to decipher.
Writing a function like that now, I'd want to make very sure that everything involved is immutable & all the steps are as close to pure functions as I can get them. I feel like it'd get shorter as a consequence of that, just because pure functions are easier to factor out, but that's not really my objective. Maybe step 1 is a function that returns a `Step1Output` which gets stored in a big State object, and step 2 accesses those values as `state.step1Output.x`. If I absolutely must have mutable state, I'd keep it small, explicit, and as separate from the rest of the variables as possible.
Yeah the immutability angle is the right instinct. The Step1Output approach is essentially what we landed on - each phase returns a typed result that gets composed into the final state. The tricky bit is when phase 7 needs to check something from phase 3's output to decide whether to run at all. You end up with either a growing "context" object that accumulates results, or a lot of explicit parameters threading through.
The discipline tax is real though. Pure functions are easier to test in isolation but harder to trace when you're debugging "why did this transaction get coded wrong" and the answer spans 6 different step outputs.
> harder to trace when you're debugging "why did this transaction get coded wrong" and the answer spans 6 different step outputs
That's definitely a pain, but I'm not sure it's easier when this is one variable being mutated in six different places. I think you're just running into the essential complexity of the problem.
Completely agree - it's essential complexity either way. The mutation approach just spreads it across time (when did this value change?), while the immutable approach spreads it across space (which step produced this value?).
The immutable version is probably easier to debug in practice since you can inspect each step's output independently. The "6 places" complaint was more about cognitive load during debugging than actual difficulty - you're jumping between files instead of scrolling through one. But that's a tooling/IDE problem, not an architecture one.