So, I'd take "good architecture" with ok and above readability, over excellent readability but "poor architecture" any day. Where architecture in this context means the broader code structure of the whole project.
So, I'd take "good architecture" with ok and above readability, over excellent readability but "poor architecture" any day. Where architecture in this context means the broader code structure of the whole project.
The novel function that might take "5 seconds to read" for the 20 people contributing to a mature project with a good architecture might nonetheless take 10 minutes for a new hire to decipher because they don't know the local vocabulary (architecture, idioms) and need to trace and interpret things elsewhere in the project and its libraries.
Meanwhile, writing implementations in a way that tried to avoid a local vocabulary altogether might make naive reads easier, but less readable to experienced team member because they're not as concise as they could be.
Your general advice to "make things easily readable" is good advice, but like with writing compelling prose or making accessible graphic design, you need to consider your audience and your choices might look different than the ones somebody else might make.
This is where “simple recursive data structures” can be simple to read but difficult to track and comprehend. An architecture that descends through distinct layers at least has landmarks that the recursive one does not have. If you are not at the root or the leaf you don’t really know where you are, lost in the middle.
function isReadyToDoThing(Foo foo) {
return foo.ready
}
function processStuff((Foo foo) {
if isReadyToDoThing(foo) {
res = workflowA(foo)
res2 = workflowB(foo)
return res && res2
}
}
This might be dumb, if isReadyToDoThing is trivial, and it could be easily inlined. Or alternatively it could be a good way to self-document, or annotate a preferred approach (imagine several similar named methods). Regardless if you don't know the code, you'll want to go look at the method, especially if it is in a different file.But also consider:
function isReadyToDoThing(Foo foo) {
return foo.attribute1 && foo.attribute2 && ! otherThing(foo)
}
This or more complex logic might be encapsulated, in which case this is probably good to separate.Making these kind of tradeoffs involve thinking about the overall system design, not just the way you structure the code within a given function.
I think people get too caught up in "small functions" and lose the readability of code locality.
Further - the more of them you need (call depth) the worse this problem becomes.
But who is the viewer of that name?
How much context can they be assumed to have? The name of the class? The name of of the module? The nature and terminology of the business that the function serves? The nature and terminology of the related subsystems, infrastructure and libraries?
There is a context dependent local optimum for how to name something. There are conflicts of interest and trade-offs have to made.
All of this.