(Side note: I swear I’ve written this blog post before but if so I can’t find it).
Back in university a friend and I were often partnered on supervisions. As a result, I got to compare and contrast our problem solving styles a lot. They were quite different.
If I had to characterize the key difference between our styles (and bear in mind this is filtered by time, perspective and biases) it was at what point in solving the problem understanding happened.
For me I would tend to mull over a problem, trying to get to the core nature of it. Eventually once I had what I though was the “key idea” of the problem I would split the problem open along those lines, everything would fall into place and I’d end up with a nice tidy solution.
(Sometimes of course I would try to split the problem along those lines, find that I’d been wrong about the key idea, and have to go back to the drawing board. Possibly even often).
I don’t have access to his internal state, but I’d be surprised if that’s what he did. His approach looked more like bulldozing a path to a solution, always solving the part immediately in front of him.
The resulting solutions were typically longer than mine and, at least to me, much harder to follow.
Lets call these two approaches “theory building” and “direct problem solving”.
At the time I thought my theory building approach was better. These days I’m not so sure.
One distinguishing feature of our two styles was that my solutions were nicer and tidier. But the other distinguishing feature is that he got to his solutions sooner and would often succeed in cases where I failed.
And I think this is often the case with these two approaches. In many ways it’s surprising how often theory building works – many problems just aren’t nice, and no amount of thinking about them is going to make them so. Many problems are actually nice but require tools that are so far from obvious a priori that you’ll never see them except in retrospect. Having tools which can just soldier on and deal with these sorts of things is incredibly important, because otherwise you’ll just get stuck and fail to make progress.
I do still think the theory building solutions are better, but sometimes what you really need is just to get things done and they’ll tend to fall down there. And sometimes the theory only becomes visible once you’ve seen the direct solution and can refine it down to its essence.
The feedback also goes the other way: Once you’ve done theory building, you’ve provided yourself with a tool you can use the next time you want to do direct problem solving.
I’m talking about mathematics here, but this carries over almost verbatim to programming. A lot of Hypothesis has been constructed theory building style, but there are parts of it where there was nothing to do but just brute force my way into a solution. I’m currently trying to finally crack py.test function scoped fixtures, and while there ended up being a clever idea or two in there that I needed to come up with to crack specific problems, by and large there’s nothing to do here but sheer brute force.
Ultimately I don’t think either approach is actually better, because I don’t think you can get very far if you try to make do with just one or the other. Excessive reliance on direct problem solving will sometimes lead you to some very strange and unnecessary places, while excessive reliance on theory building will eventually lead to you getting nowhere fast.
So the real solution is to let both work in tandem and refactor mercilessly: Theory build where you can and it’s easy or worthwhile, direct problem solve where you can’t. But when you engage in direct problem solving, the theory building should be sitting there at the back of your mind trying to see what’s really going on, and then maybe can come in and pick of the pieces and replace it with something nicer if it turns out to be possible.