This is not a valid argument when the line in question is dealing entirely with abstract constructs(which it is) and all of the variables are function-local, declared on the very same screen, and several of them exist as aliases for more complex constructs. If you haven't encountered a need for this approach, you just haven't had to write really mathy code before. Mathy code derives little value from longer names, but it does benefit from density.
If you really think something is wrong with it, post ideas for longer names that will clarify and not substantially impact density. I would probably conventionally use "n0" and "n1" instead of "m" and "n" to represent "n-1", "n" - but the usage he has is consistent throughout the file, which is more than I can say of my stuff sometimes.
I've been on the "sleep on exercise mat with some blankets" train for years, but a few months back, stepped it up by eliminating pillows. I still gravitate towards getting something under my head, but it turns out that I only need another small blanket or cloth. I wake at more consistent hours and no longer experience numbness in my arms when waking. I believe there are many more "details" I can improve too. I got into training fast and slow breathing patterns for different activities, which seems to help both the training and the recovery. A plainer diet seems to help a lot of things, although it's hard to reconcile this against the constant imposition of the world to eat pizza and drink beer.
Contrast with FPC's compiler mode pragmas for different dialects. [0] FPC shows the effects of this story in the long term: there are several major divergences in behavior supported by the modes, and some individual features can be toggled per-unit or sometimes with a finer grain. [1] Since you're allowed to mix and match different units, the fragmentation poses little threat to any individual programmer, and it's possible to dial in assertions, optimizations, and calling conventions in a targeted fashion.
Here's an easy challenge: Ask the overengineer to solve problems without introducing any new classes or functions. This starves the overengineer of oxygen, reducing their abstraction options to "loop", "branch", and "add variable". The resulting giant switch statement in a loop is likely to be a relatively clean architecture.
I've been musing about these sorts of issues of "what is really complex" lately, too. I started evaluating new languages for low-level application code again after spending a lot of time overly close to web browsers, and then ended up deciding that Free Pascal beat them on the stuff I cared about, relative to C compilers as a baseline:
* Best-in-class compile times
* Modules; preprocessor is relevant, but not dominant
* Generics, variant records, function and operator overloads, exceptions, safe string and dynamic array types
* Libraries for common things, with a reasonably conventional style: APIs, containers, bindings, etc.
* No GC; pointers are available, but language features discourage casual usage. RAII pattern is achievable.
* Strong + static types, nominal types and type aliasing.
* Good IDE support, mature tooling, compiler has very broad platform support.
The new languages do win on a lot of the language-feature aspects(particularly memory safety when performing deep optimizations), but they don't have as good a story on the tooling. The compile speed is a major feature and is only likely to remain advantageous over time - and the features already in place are quite effective at limiting source code size, boilerplate, and dangerous constructs. This despite "Pascal is verbose" being memetic.
And if I want more high-level power, I've found that writing a FSM is the most probable path to achieving it. A FSM that embraces the problem domain is a tiny compiler: input source constructs, FSM checks them, applies appropriate algorithms, then emits a list of actions. More power? Add a second FSM to make it a two-pass compiler. Add a stack, or attach it to a database. If it has to be fast, it can generate code in the last step, too. Those things add tons of power to model the problem and aren't heavily dependent on language-side support.
Yup, we already had all of that in Turbo Pascal for MS-DOS, hence why I never saw any value in C back in 1993.
On those days outside UNIX, C was just yet another systems language, with most home micros being written mostly in Assembly.
Also Turbo Pascal OOP features were adopted from Apple's Object Pascal, used to write the initial versions of Lisa, Mac OS and known software like Photoshop.
When the paper was written, the jury was still definitely out on good concurrency practices for applications: the computing world(apart from mainframes, which of course had learned every lesson 30 years earlier, but nobody paid much attention to) had only relatively recently exited the era where one might ordinarily synchronize your co-processing units at the instruction level, with hand-tuned assembly, just like any other piece of external hardware. Cooperative, event-based programming is familiar to this style. Asynchronous primitives backed by hardware scheduling were the "new kid" for a lot of programmers.
So, like with every technology, there was some exuberance and overuse of a novel formalism which shows up in this paper: "scales to 100,000 threads", it proclaims. Not much later, interest in "green threading" and "lightweight concurrency" grew as a way to get some of the architectural benefits of being asynchronous while mitigating waste from large numbers of threads, and consensus shifted towards mixing the approaches, which seems to have converged on the few-threads, message-queue-per-thread approach.
I'm seeing it as a feedback loop problem, again. Managers and stakeholders have an easy time remarking on surfaced progress. The developer has to think and communicate everything else. Going purely hands-off doesn't work, because then the developer will tend to wander towards working on the wrong problems and get mired in them. Neither does chaining them down to a deadline, since that creates rushed, ill-considered work.
So the people who are perceptually 10x tend to have hit on a strategy that keeps them in the feedback zone without being overly concerned about surfacing everything right this second, and have management that cooperate with their strategy.
Author here. I agree that being able to produce advanced value is also a factor of the work environment; and not only the engineer's ability to be in touch with it, but also the environment's ability to keep the engineer in touch when they should be. I didn't think of it, but I think it's a great point.
You wouldn't be alone as a philosopher-developer, there are a decent number of us out there.
However - shipping code is often very organic, cultivated stuff which foils attempts to establish a coherent philosophy, a through-line of "how it works" - it works because it worked the day before, all that changed was that a little bit more was added. Repeat till broken, then refactor back to sanity(else abandon ship). It's Sisyphean hill-climbing.
This in turn plays into the falsity of "engineering" as a way to describe what's going on with a lot of code. You can do it in the small, but at any scale the codebase habitually becomes a living organism. And that tends to get puzzle-solver types excited, because if it's alive and keeps changing, then they will have endless problems to solve forever!
So I believe the philosophers of the crowd ultimately tend to move away from the applications coalface and look for something relatively smaller that does allow some time for reflection and distillation of the problem.
I find your last paragraph very interesting. May I ask, did you make that move yourself and, if so, where to?
In my daily work I often refactor an old, organically grown, codebase and that's exactly what I do:
I try to think of what the system actually is / should be and try to put this in code in as concise terms as possible (that's kind of a general description of software development, but I just wanted to contrast this to simply hammering in the quickest fix you can think of). A lot of times I wonder though, if that is really what is needed and a simple 'prototyper' kind-of-developer would get the job done faster, although maybe not as elegantly (slight humble-brag there).
I followed Carmack's suggestion and never looked back. I write simpler code on average now, and I spend less time having to re-read it too because I'm changing the same lines less often. What I lose in terseness in the short-term, I regain in being able to spot key abstractions that are more crucial to long-term program health. It plays well with debugging since there are fewer places where state can act at a distance.
It's very unfashionable advice in some circles, though. It sounds just enough like "go back to writing unstructured copy-paste spaghetti" to trigger some people's kneejerk. But the ground rules of "don't take subroutines, don't jump backwards" actually keep it from getting too far out of control, because they align you towards code that only uses enough flexibility for the task at hand, hence there's a lower likelihood of missing something. Very good practice for greenfield code where you might reflexively add a generalization that you don't use. I can still get better at it.
Edit: And one of the most important things it taught me, is that I can use scope blocks and a comment instead of a function, and it'll be sufficient.
If you really think something is wrong with it, post ideas for longer names that will clarify and not substantially impact density. I would probably conventionally use "n0" and "n1" instead of "m" and "n" to represent "n-1", "n" - but the usage he has is consistent throughout the file, which is more than I can say of my stuff sometimes.