Hacker News new | ask | show | jobs
by ssivark 2256 days ago
> if handed a big ball of mud, is it riskier to start from scratch and go for something more triumphant, or try to evolve the mud gradually?

Reminiscent of Chesterton’s fence. But then, we end up in such a “complex” situation only when one thing can have multiple causes & effects — which is difficult to model correctly in a clean slate formulation.

The simplest solution seems to be to avoid making software that complex in the first place (we can exert far more control than in the physical world).

But then if we think about Peter Naur’s perspective about programming as a mode of theory building (of the domain) (unsurprising, given the basic cybernetics principles such as the law of requisite variety & the good regulator theorem), then the answer seems to be — unless your domain is really complex, think hard before you implement, and keep refactoring as your understanding improves (and truly to pick problem formulations / frameworks / languages which make that feasible. Of course, easier said than done.) The key point is to keep refactoring “continuously“ to match our understanding of the domain, rather than just “adding features”.

Aside: In my experience, software built on a good understanding of the domain will function well, untouched, for a long time — so long as it is suitably decoupled from the less-well-understood parts. The latter kind, though, generates constant churn, while also being an annoying fit. Really brings home the adage “A month in the laboratory can save a day in the library.”

2 comments

> But then, we end up in such a “complex” situation only when one thing can have multiple causes & effects — which is difficult to model correctly in a clean slate formulation.

This is why you should keep paying your employees that worked for the company for years, having written all of the mediocre code when they still could not program well at all.

> The key point is to keep refactoring “continuously“ to match our understanding of the domain, rather than just “adding features”.

This is also what I wanted to say.

One important part of that is that refactoring is a pretty difficult skill, and many programmers do not have it.

So... for those people, some other advice is probably better.

I wish this process was called 'factoring' and you had to be able to name the concept that was being isolated. Often 'refactoring' just means moving code around or isolating code for it's own sake. If a factor was properly isolated you shouldn't have to do that one again. Sometimes you choose different factors, but that's much less common.
"Factoring" is sometimes used in the Forth world, since code being factored into small words is of such eminence.

And it offers good lessons about what's worth factoring and how. Forth words that are just static answers and aliases are OK! They're lightweight, and the type signatures are informal anyway. "Doing Forth" means writing it to exactly the spec and not generalizing, so there's a kind of match of expectations of the environment to its most devoted users.

On the other hand, in most modern environments the implied goal is to generalize and piling on function arguments to do so is the common weapon of choice, even when it's of questionable value.

Lately I've cottoned on to CUE as a configuration language and the beauty of it lies in how generalization is achieved while resorting to a minimum of explicit branches and checks, instead doing so through defining the data specification around pattern matching and relying on a solver to find logical incoherencies.

I believe that is really the way forward for a lot of domains: Get away from defining the implementation as your starting point, define things instead in a system with provable qualities, and a lot of possibilities open up.

> If a factor was properly isolated you shouldn't have to do that one again.

This assumes that later code changes don't undo/blur the factoring, which while ideal is not at all consistently the case in the real world.

Refactoring is a little over arrow of a name, because code hygiene is more than just isolating factors, but the “re” part is right because you are always aiming to remove infelicities that were actively added in previous coding.

This lines up with a principle from the Toyota Production System (TPS) in manufacturing--reduce complexity.

In TPS, they found that a focus on reducing complexity leads to improvements in the metrics you'd want to measure: better quality, reduced costs, and customer satisfaction.