> And be warned: some will resist this and surrender to the convenience of their current mental context, betting they’ll “remember” how they did it. Time will make that bet age badly. It’s 2026 — other AI agents are already in execution loops, disciplined to code better than that.”
Hard disagree: separating code from its context is exactly how you end up in the situation of needing to “remember”. Yes, helper functions and such can be useful for readability, but it's easy to overdo it and end up with incomprehensible ravioli code that does nothing terribly complicated in a terribly complicated manner.
This has given really good results in terms of helping decide whether to extract these helper functions or not - they have to both be memorable enough in name and arguments that the code calling them can understand what's going on without always having to dive in, and also provide a meaningful compression of the logic above so that it can be comprehended without having to jump across many hundreds of lines.
I feel your pain. Everything is so convoluted that 7 layers down you ask yourself why you didn't learn anything useful...
Oh gee, thank you for this wrapped error result, let me try to solve a logic puzzle to see (a) where the hell it actually came from, and (b) how the hell we got there.
Am reminded also of a discussion of software engineering between John Ousterhout (of whom I'm a big fan) and Robert Martin[0][1].
Lots and lots of little components, but not in a way that actually makes anything easier to actually find.
When I worked on Firefox, we eventually had to remove a bunch of indirection (the interested can actually search bugzilla.mozilla.org for deCOMtamination for some instances of this), but that project wasn't a thing until there was clear evidence that there were problems with virtual function calls on hot paths.
- the application is profiled well enough to prove that some piece of code is on the hot path
- the developers are not doing a great job
The article describes a couple of straw men and even claims that they’re right in principle:
> Then someone on the team raises an eyebrow. “Isn’t that an extra function call? Indirection has a cost.” Another member quickly nods.
> They’re not wrong in principle.
But they are wrong in principle. There’s no excuse for this sort of misinformation. Anyone perpetuating it, including the blog author, clearly has no computer science education and shouldn’t be listened to, and should probably be sent to a reeducation camp somewhere to learn the basics of their profession.
Perhaps they don’t understand what a compiler does, I don’t know, but whatever it is, they need to be broken down and rebuilt from the ground up.
Also, it's pointer indirection in data structures that kills you, because uncached memory is brutally slow. Function calls to functions in the cache are normally a much smaller concern except for tiny functions in very hot loops.
For non-async fns, the article already made this point:
> In release mode, with optimizations enabled, the compiler will often inline small extracted functions automatically. The two versions — inline and extracted — can produce identical assembly.
The article mixes together two distinct points in a rather muddled way. The first is a standard "premature optimization is the root of all evil" message, reminding us to profile the code before optimizing. The second is a reminder that async functions compile down to a state machine, so the optimization reasoning for sync functions don't apply.
[0] https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-a...
Ideally using an allocator per request would solve this issue, but Rust has no real support for it.
A workaround that works is to stop using async and just use a native thread per request. But then most crates and frameworks these days use async. So indeed async abstraction us very leaky regarding the cost.
Async is telling the OS "I'll do it myself" to threading and context switches.
I think this type of confusion (or more likely people talking past one another in most cases) is a fairly common problem in discussing programming languages and specific implementations of concepts in a language. In this case the perceived purpose of an abstraction based on a particular “view point”, leads to awkward discussions about those abstractions, their usefulness, and their semantics. I don’t know if there is way to fix these sorts of things (even when someone is just reading a comment thread), but maybe pointing it out can serve to highlight when it happens.
Async in rust is done via cooperative scheduling. If you call await you enter a potential suspension point. You're willingly telling the scheduler you're done running and giving another task a chance to run. Compound that with something like tokio's work stealing and now you'll possibly have your task migrated to run on a different thread.
If this is in hot path making another call to await is probably the worst thing you can do lol.
The author demonstrates later with a dead simple inlining example that the asm is equivalent. Wonder why he didn't try that with await ;)
If this function is in the hot path the last thing you'll want to do is to needlessly call await. You'll enter a suspension point and your task can get migrated to another thread. It is in no way comparable to the dead simple inlining example given later.
This is why you should always benchmark before making guesses, and to double check you're even benchmarking the right thing. In this case they used the findings from a nonasync benchmark and applied it to async. This will lead you to a very wrong conclusion, and performance issues.
2. Please read the blog. That's literally what is said.
Dynamic dispatch in general is much, much faster than many people’s intuition seems to indicate. Your function doesn’t have to be going much at all for the difference to become irrelevant. Where it matters is for inlining.
Dynamic dispatch in Rust is expected to be very slightly faster than in C++ (due to one fewer indirections, because Rust uses fat pointers instead of an object prefix).
```
fn does_a_many_step_process():
first_step_result_which_is_not_tied_to_details_internal_to_the_step_implementation = well_named_first_step_which_encapsulates_concerns();
second_step_result_in_same_manner = well_named_second_step_which_encapsulates_concerns();
...etc
}
```The logic of process flow is essentially one kind of information. All the implementation details are another. Step functions should not hide further important steps - they should only hide hairy implementation details that other steps don't need to know about.
Other people prefer to have big blocks of code together in one place, and that's fine too. It just personally makes it harder for me to track stuff.
As for the context of the article, maintainability is almost always worth the cost of the function lookup. The proof here that the cost is almost non-existent means to me the maintainability is always worth the perceived (few cycles) impact unless this is real-time code.
But the real cost is that having a myriad of them is usually very difficult to get the right cut. not too small not too big and having a clear intend of what it exactly does.
so nothing new. API design is hard. naming thing even more so.
This is an incomplete sentence:
> All cases where you are in a CPU intensive blocking task that, if you’re not careful, could starve all the others.