4 Comments

"When I’m changing this function, I want to be able to consider what’s inside it while ignoring what’s outside it. Being able to make this distinction gives me freedom to think that I wouldn’t have if I had to consider all lines of code in the whole system at the same time."

This has emerged for me as maybe The Key Point in software design practice. I want the option to ignore things. As many as possible. I want to focus with confidence. We humans, we can only function at all because we guess pretty well at what we can safely ignore. (Well enough to survive, anyway.) If we didn't need this ability, we would just dump everything in main() and be done with it.

And yet, so many programmers act like they don't need it. They are obsessed with the details. They feel most powerful when they know all the details.

I feel most powerful when I can ignore all the details.

Expand full comment

I conjecture that the average competent programmer feels more comfortable with library-style abstraction than they do with framework-style/Template Method-style abstraction. They feel more open to trust the wisdom of recombinable small parts than to trusting the wisdom of reusable workflows. They feel more comfortable ignoring the details "above" them than they do ignoring the details "below" them in the dependency tree. I think there is much to be gained by helping them become more comfortable ignoring the details "below' them. In extracting Template Methods, workflows, frameworks, whatever they prefer to call them.

Anyone else?

Expand full comment
Nov 17, 2022·edited Nov 17, 2022

Hello Kent,

> When I’m changing this function, I want to be able to consider what’s inside it while ignoring what’s outside it

Yes!

This is where pure functional programming shines, in that it uses pure functions.

Here is the simplest definition of a pure function in terms of its properties (here the function has just one parameter, to simplify things, but it is obvious how the definition would be tweaked to reflect multiple parameters):

1. Total (as opposed to Partial): no matter what parameter it is passed, it always successfully returns a result value. In other words, it maps all values in its domain to a value in its codomain.

2. Deterministic: for every possible actual parameter, no matter how many times it is passed that parameter, it always returns the same result value.

3. Pure: It has no side effects. The only thing it does it compute the result value using its parameter value.

See https://www.slideshare.net/pjschwarz/definitions-of-functional-programming for a better definition of ‘Pure’.

A better definition of a pure function relies on the concept of Referential Integrity (see the above deck for a definition of the latter).

An expression E is referentially transparent if, for all programs P, all occurrences of E in P can be replaced by the result of evaluating E without affecting the meaning of P.

A function is pure if the expression f(x) is referentially transparent for all referentially transparent x.

See last slide of the deck for more details.

Expand full comment

I've often used the phrase "It's architecture all the way down." So may of the questions are the same at each level: What are the boundaries? What are the responsibilities? How do data and control flow across these boundaries? And of course many more. Of course the contexts and constraints shift so much from system to subsystem to package to class to method/function that the choices look quite different at each level.

Expand full comment