The Hidden Similarity Between Async Code and Better Thinking
Hatched by Frontech cmval
Jun 13, 2026
10 min read
1 views
84%
What are we really buying when we buy time?
Most people hear async and think faster software. That is only half the story. The deeper promise is not speed, but the ability to stop confusing waiting with work.
That sounds technical, but it is also a philosophy. A system that can pause while a database responds, a file loads, or a network request returns is making a practical admission: the world does not always move at your pace. Some tasks need attention only at the moments when new information arrives. Everything else is wasted motion.
Now consider a different kind of micro change in a programming language: letting an f string expression be any valid Python expression. On the surface, that looks like a small convenience. In reality, it reflects the same instinct. Reduce friction. Remove artificial constraints. Let the expression fit the thought, instead of forcing the thought to fit the syntax.
These two ideas come from different corners of programming, but they point toward one deeper question:
What if the smartest systems, and the smartest language features, are the ones that know when to wait and when to get out of the way?
That question matters because modern software is not one thing. Sometimes it is a waiter, sometimes a factory, sometimes a calculator, and sometimes a translator between human intent and machine execution. The mistake is to treat all of these as if they should be optimized in the same way.
The first mistake: treating every delay as inefficiency
In everyday life, waiting feels bad. In software, that intuition is only partly useful. A web app spends much of its life waiting for data to arrive from a client, a remote API to respond, or a database query to finish. In those moments, the program is not failing to work. It is doing a different kind of work: coordinating attention.
This is why asynchronous code matters. It acknowledges that a program can be productive even when it is not actively computing. If one request is waiting on the network, another can move forward. That is concurrency: not doing everything at once, but making progress on multiple tasks by switching intelligently between them.
The crucial insight is that concurrency is not a moral virtue. It is a fit for a specific shape of problem. If the bottleneck is waiting, concurrency helps. If the bottleneck is heavy computation, such as image processing, matrix multiplication, or machine learning training, then the issue is different. You do not need more clever pausing. You need more raw throughput, often through parallelism, multiprocessing, or specialized hardware.
This is where many teams go wrong. They hear that asynchronous systems are modern and performant, then try to apply them everywhere. But the world has at least two kinds of slowness:
- Waiting slowness, where time disappears into I/O.
- Thinking slowness, where time disappears into computation.
These are not the same problem. A fast restaurant does not cook burgers faster by having the cashier multitask. A data science pipeline does not speed up matrix multiplication by yielding control more often. Different delays demand different solutions.
The first discipline of performance is not optimization. It is diagnosis.
If you do not know whether your system is waiting or calculating, you will improve the wrong thing beautifully.
Async is not about doing more, it is about knowing what can be paused
The most useful way to understand async is not as a tool for parallel efficiency, but as a model of interruption.
When you write something like await get_burgers(2), you are declaring a social contract with the runtime. You are saying: this operation may take a while, so let me step aside until it is ready. In return, the system can use that moment to serve someone else. That is not a trick. It is a coordination strategy.
The deeper mental model is this: asynchronous code converts idle time into reusable time.
Imagine a busy restaurant with one chef and ten customers. If the chef had to stand motionless in front of each order until the oven finished, the whole kitchen would collapse. Instead, the chef places the order in the oven, marks it pending, and turns to the next task. That does not mean the burgers appear faster in the oven. It means the chef is not personally trapped by the oven’s schedule.
That distinction matters because it reveals why async is powerful for web applications. The web is a choreography of delays. Requests travel across networks. Databases answer when they can. Filesystems respond when they are ready. Real users are not waiting on arithmetic, they are waiting on systems to hand off information. Async lets one server handle those handoffs with less wasted standing around.
This also explains why async and parallelism are often confused. They can coexist, but they solve different bottlenecks. Concurrency is about structure, how tasks are interleaved. Parallelism is about capacity, how much actual work can happen at the same moment. A good web API often needs both: concurrency to stay responsive while waiting, and parallelism to crunch through expensive workloads.
That combination is especially powerful when a web application talks to machine learning components. The request handling layer is mostly I/O bound. The model inference or training layer may be CPU bound or GPU bound. One side benefits from yielding, the other from brute force. A mature system respects both realities instead of trying to flatten them into a single abstraction.
This is why some languages and frameworks became popular in the first place. They made it easier to treat waiting as a first class event rather than as an accident. The appeal was never merely technical elegance. It was relief from a fundamental mismatch between human intuition and machine behavior.
The tiny syntax change that reveals a bigger design principle
At first glance, allowing any valid Python expression inside an f string seems unrelated to concurrency. One changes how code waits. The other changes how code reads. Yet both point to the same architectural preference: make the path from intention to execution shorter.
A restrictive syntax forces the programmer to split a thought into multiple steps. A more flexible syntax allows the thought to remain whole. That matters because software is not only executed by machines. It is also read, maintained, debugged, and revised by humans. Every unnecessary constraint is a little tax on cognition.
Think of the difference between saying, “I need a formatted string, but first I must extract the value, store it in a temporary variable, and then inject it,” versus, “I can express the full calculation directly where I need the result.” The second is not just shorter. It is closer to how you actually think.
That is the same elegance async seeks in another dimension. Instead of forcing the programmer to manually manage every waiting state, it gives a direct way to say, “this part depends on something external, so suspend here and resume later.” In both cases, the system becomes better when it can represent the shape of the problem more honestly.
This yields a useful design principle:
Good abstractions do not hide complexity by pretending it is gone. They hide it by making the important distinction obvious.
For async, the distinction is between compute time and wait time. For f strings, it is between the string’s surface form and the full expression underneath. In both cases, the language stops demanding ceremony where intention would suffice.
The broader lesson is that progress in programming often comes from removing fake boundaries. Not boundaries that protect us from real danger, but boundaries that only existed because of historical limitations. When those limits fall away, code becomes less like a set of hoops and more like a precise description of action.
A framework for choosing the right kind of speed
If you want to build faster systems, do not start by asking, “Should I use async?” Start by asking, what kind of slowness am I actually paying for? That question leads to a better decision tree.
1. If the task waits on the outside world, optimize for concurrency
Use async patterns when the program spends much of its time waiting for:
- network calls
- database queries
- file I/O
- client responses
- remote APIs
Here, the problem is not that the machine is too weak. It is that the program is underusing time while other systems do their part. Concurrency helps you reclaim those gaps.
2. If the task is dense with computation, optimize for parallelism
For CPU bound work such as:
- image processing
- computer vision
- machine learning training
- large scale numerical operations
you want more actual execution capacity. This may mean multiple processes, native extensions, vectorized libraries, or specialized processors. The program is not waiting. It is busy.
3. If the task is awkward to express, optimize for language shape
Sometimes the bottleneck is neither I/O nor CPU. It is the friction of writing and reading the code itself. In those cases, syntax features that let you express more with less ceremony matter more than raw speed. A small ergonomic gain can save hours of maintenance later.
This framework is useful because it keeps us honest. Many engineering discussions go wrong when people use one category of improvement to solve another category of problem. They introduce async where they need batching. They introduce threads where they need better data access patterns. They introduce clever syntax where they need architectural clarity.
The most robust systems are not built by picking one buzzword and applying it everywhere. They are built by matching the shape of the tool to the shape of the wait.
Speed is not one thing. It is a family of different satisfactions: less waiting, less recomputation, less ceremony.
That is why good engineering often feels less like force and more like alignment.
The real connection: both ideas respect the limits of attention
The deepest link between async programming and expressive language design is not technical. It is about attention.
Async respects the attention of the runtime. It says, do not pin the whole system to a task that is not ready yet. Let the system’s attention move elsewhere until the world responds. Flexible f string expressions respect the attention of the programmer. They say, do not force me to break a clear thought into needless fragments. Let the code reflect the shape of the idea.
In both cases, the design principle is the same: do not waste scarce attention on things that do not need it.
That is surprisingly profound. Human creativity and machine throughput both collapse when attention is trapped in the wrong place. A synchronous system can become a hallway of idle waits. An overly rigid syntax can become a hallway of tiny obstacles. The best abstractions remove those hallways.
This is why the best engineering often feels invisible. When it works, you do not notice the waiting, because waiting is being handled. You do not notice the ceremony, because ceremony has been reduced. You do not notice the machinery, because the system is shaped around the actual problem.
The point is not to eliminate delay. Delay is part of reality. The point is to classify delay correctly.
If you misclassify a waiting problem as a computation problem, you will throw CPU at latency. If you misclassify a computation problem as a waiting problem, you will yield your way into inefficiency. If you misclassify a thought problem as a syntax problem, you will bury clarity under cleverness. The craft is in knowing which kind of friction you are facing.
Key Takeaways
- Do not optimize for speed until you know what is slow. Separate waiting on external systems from actual computation.
- Use concurrency for I/O bound work. Web requests, database calls, and file operations are prime candidates for async patterns.
- Use parallelism for CPU bound work. Heavy numerical tasks, image processing, and machine learning need more execution capacity, not more yielding.
- Treat syntax as a cognitive tool, not just a formatting choice. Small language improvements can reduce mental overhead and make intent clearer.
- Look for abstractions that match reality. The best systems describe the shape of the problem directly instead of forcing it through arbitrary steps.
Conclusion: the best systems know when not to insist
We tend to praise software for what it can do. But one of the most important signs of maturity is what it does not insist on doing. It does not insist that every task block everything else. It does not insist that every idea be broken into tiny pieces before it can be expressed. It does not insist that waiting is failure or that elegance is decoration.
That is the shared wisdom behind async code and more expressive syntax. Both are forms of restraint. They create room for the real work to happen without unnecessary obstruction.
So maybe the modern question is not, “How do we make code faster?” A better question is:
How do we design systems that stop wasting attention on the parts of reality that cannot be rushed?
Once you start asking that, you begin to see performance, readability, and even language design as variations of the same discipline: the art of making room for what actually matters.
Sources
Hatch New Ideas with Glasp AI 🐣
Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)
Start Hatching 🐣