{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Mark's Space",
  "home_page_url": "https://markxu.icu",
  "feed_url": "https://markxu.icu/feeds/en/feed.json",
  "description": "Welcome to Mark Xu's personal site, sharing tech and life.",
  "language": "en-US",
  "authors": [
    {
      "name": "Mark Xu",
      "url": "https://markxu.icu"
    }
  ],
  "items": [
    {
      "id": "https://markxu.icu/blog/future-of-liberal-arts-and-engineering-en",
      "url": "https://markxu.icu/blog/future-of-liberal-arts-and-engineering-en",
      "title": "Maybe the Future Will Only Have the Liberal Arts and Engineering: Thoughts on a 3Blue1Brown Video",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. Tonight I watched 3Blue1Brown's video “There Has Never Been a Better Time to Devote Yourself to Mathematics,” and it stirred up some feelings. I will record them briefly here. I have put the link below so everyone can watch it directly. After finishing the whole video, I want to start with an attention-grabbing hot take: if we really do reach the point 3Blue1Brown describes, where AI is stronger than humans at everything humans do, then from that point on all learning will become liberal arts. Fun fact: in English, both the arts and the liberal arts are called Art. So when people in China say they are studying the liberal arts, they could equally say they are studying art. Which means that in the future, mathematics could become a so-called “liberal art” as well. Push that condition a little further, and all research—physics, chemistry, even every other kind of theoretical work—could become a liberal art. In the future there will be no distinction between the liberal arts and the sciences, only between “arts” and “engineering”. One is learning theory; the other is landing theory in practice and interacting with the real world. At that point, university will no longer be a stage of educational attainment, but a higher-order club—one where the entrance ticket is an assessment you have to pass. Everyone inside is passionate about learning, wants to learn, and likes discussing with people who share that passion, to socialize and to connect. The reason people join this club in the first place is that they were infected by people who carry that kind of passion—just as my original motivation for building this blog site came from being inspired by many like-minded people online who actually put their ideas into practice. University would then no longer be simply a few years you have to put aside in order to land a better job. This seems to loop back to how aristocrats studied science in the seventeenth and eighteenth centuries. Under the development of AGI, productivity advances, and with it comes a reasonable new mode of distribution. Because the means of production would no longer be owned by fixed capital; they would be generated by AGI. The relations of production would change as well, and distribution would necessarily change with them. Given a good distribution mechanism, people would no longer worry about making a living, and could pursue the things they love. Those who love painting would paint, those who love sports would play sports, or those who love studying mathematical theory would study it, turn theory into practice, or even just spread the joy of learning and rope more people in, or simply teach it to children. In the end, all of it becomes a kind of “service industry,” or a way of life that people enjoy. At that point, doing research would be like what The Craftsman describes: the reason for doing something well is simply that you want to get it done, and so you do it well. The inner impulse to “do a job well for its own sake.” Of course I know this is idealistic, but it really does sound lovely, doesn't it? With AGI developing continuously, perhaps the whole industry, the whole society, has realized that it is going to have to change. Look at current policy: many graduate students no longer need to write a thesis, and instead have this “Outstanding Engineer” training mechanism. All of these are changes produced in order to adapt to social development. In my view, when it comes to social development, holding fast to the unchanging in order to meet every change rarely works; against the patterns of history, it has almost never happened. We are going to change in any case, and only by changing constantly can we use new ideas to solve new problems. If we do not know what it will turn into, why not imagine it as something a little better? —Written on a night tormented by linear algebra transcribed from speech",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>Tonight I watched 3Blue1Brown's video “There Has Never Been a Better Time to Devote Yourself to Mathematics,” and it stirred up some feelings. I will record them briefly here. I have put the link below so everyone can watch it directly.</p>\n<p><VideoEmbed\n  url=\"https://www.bilibili.com/video/BV1HBnH6JEAZ/?share_source=copy_web&vd_source=3446a02e4deef26e9b4161a2ba03a1e3\"\n  title=\"There Has Never Been a Better Time to Devote Yourself to Mathematics\"\n/></p>\n<p>After finishing the whole video, I want to start with an attention-grabbing hot take: if we really do reach the point 3Blue1Brown describes, where AI is stronger than humans at everything humans do, then from that point on <strong>all learning will become liberal arts.</strong></p>\n<p>Fun fact: in English, both the arts and the liberal arts are called Art.</p>\n<p>So when people in China say they are studying the liberal arts, they could equally say they are studying art. Which means that in the future, mathematics could become a so-called “liberal art” as well.</p>\n<p>Push that condition a little further, and all research—physics, chemistry, even every other kind of theoretical work—could become a liberal art. <strong>In the future there will be no distinction between the liberal arts and the sciences, only between “arts” and “engineering”.</strong> One is learning theory; the other is landing theory in practice and interacting with the real world.</p>\n<p>At that point, university will no longer be a stage of educational attainment, but a higher-order club—one where the entrance ticket is an assessment you have to pass. Everyone inside is passionate about learning, wants to learn, and likes discussing with people who share that passion, to socialize and to connect.</p>\n<p>The reason people join this club in the first place is that they were infected by people who carry that kind of passion—just as my original motivation for building this blog site came from being inspired by many like-minded people online who actually put their ideas into practice. University would then no longer be simply a few years you have to put aside in order to land a better job.</p>\n<p>This seems to loop back to how aristocrats studied science in the seventeenth and eighteenth centuries. Under the development of AGI, productivity advances, and with it comes a reasonable new mode of distribution. Because the means of production would no longer be owned by fixed capital; they would be generated by AGI. The relations of production would change as well, and distribution would necessarily change with them.</p>\n<p>Given a good distribution mechanism, people would no longer worry about making a living, and could pursue the things they love. Those who love painting would paint, those who love sports would play sports, or those who love studying mathematical theory would study it, turn theory into practice, or even just spread the joy of learning and rope more people in, or simply teach it to children.</p>\n<p>In the end, all of it becomes a kind of “service industry,” or a way of life that people enjoy.</p>\n<p>At that point, doing research would be like what <em>The Craftsman</em> describes: the reason for doing something well is simply that you want to get it done, and so you do it well.</p>\n<blockquote>\n<p>The inner impulse to “do a job well for its own sake.”</p>\n</blockquote>\n<p>Of course I know this is idealistic, but it really does sound lovely, doesn't it?</p>\n<p>With AGI developing continuously, perhaps the whole industry, the whole society, has realized that it is going to have to change. Look at current policy: many graduate students no longer need to write a thesis, and instead have this “Outstanding Engineer” training mechanism. All of these are changes produced in order to adapt to social development.</p>\n<p>In my view, when it comes to social development, holding fast to the unchanging in order to meet every change rarely works; against the patterns of history, it has almost never happened. We are going to change in any case, and only by changing constantly can we use new ideas to solve new problems.</p>\n<p>If we do not know what it will turn into, why not imagine it as something a little better?</p>\n<p>—Written on a night tormented by linear algebra (transcribed from speech)</p>\n",
      "summary": "[AI-translated] Tonight I finished watching 3Blue1Brown's video “There Has Never Been a Better Time to Devote Yourself to Mathematics,” and it stirred up some feelings that I record briefly here.",
      "date_published": "2026-10-25T00:00:00.000Z",
      "image": "https://img.markxu.icu/imge1ca44fe-e495-4e4f-951f-46ea158d947b.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/why-unix-signal-sender-is-called-kill-en",
      "url": "https://markxu.icu/blog/why-unix-signal-sender-is-called-kill-en",
      "title": "From the 408 OS Exam to Production: Why Is the Signal-Sending Function Called kill Instead of send?",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. Recently, while reviewing the “Process” chapter of the 408 operating systems course, I got to the signal mechanism of inter-process communication IPC . Since this round was the intensive review phase, I was reading the textbook very carefully. I froze for a moment: “Wait—sending a signal between processes, and the system call is called kill ?” That cannot be right. Is all inter-process communication meant to kill the other party? To use an analogy: you send a friend a message saying “come downstairs and hang out,” and someone calls that operation . That is absurd, and genuinely counterintuitive, so I dug in a little deeper to find out what was going on. Why did the UNIX forefathers choose such a misleading name? Today let us dig into this “legacy naming” that has stayed with us for decades, and take a look at what it actually means in everyday development and in interviews. The “Force Kill” the Name Misleads You Into When I first saw the system call, or the command in a terminal, I formed an obvious assumption: its only job is to “take out a process.” When troubleshooting a production incident or debugging a program, we habitually type , or the more brutal , into a terminal. As the process vanishes instantly, I naturally equated that operation with the way a program ends. But in fact, that intuition of mine concealed a major technical misunderstanding. The essence of is to deliver a signal to a specified process , not to “kill” it in the literal sense. Terminating the process is merely the default behavior of a process on receiving a particular signal, not a functional limit of the system call itself. In other words, is nothing more than an asynchronous communication mechanism for notifications, and “killing” is just one default result among many ways of handling signals. What Does Actually Do? Let us first look at how is normally used. In C, the prototype of the system call is very simple: : the ID card number of the target process. : the number of the signal you want to send such as , , and so on . If you pass for known as the null signal , does not send a signal at all; it is used to probe whether the process exists and whether the current user has permission to send it signals commonly used for liveness checks when writing daemons . A Simple C Example The code below shows how to send a custom signal to a specified process, and how to check the process's status: Here, merely delivers the signal into the kernel's process control block PCB , completing a lightweight event notification. The Historical Puzzle: Why and Not ? Since its essence is sending a signal, why is it not called , or simply ? That would seem more intuitive. Answering that means rewinding to the wild early days of UNIX in the 1970s. Historical constraints and original intent : In the earliest UNIX designs, inter-process communication was nowhere near as complex as it is today. There were very few kinds of signals, and the one purpose, the core scenario, they were designed for was— terminating processes that had run out of control, were stuck in an infinite loop, or were hogging resources . So the intuitive mission of this system call at the time was “take it out,” and the name was extremely apt. Path dependence in the POSIX standard : As systems evolved, people found this “asynchronous notification mechanism” too useful to confine to killing processes; it could also announce state changes, reload configuration, report that a child process had exited, and more. Signals multiplied, and the feature expanded into general-purpose IPC. But by then the name had already taken root in countless system source files and early books. To maintain backward compatibility, the POSIX standard ultimately kept the name. Distinction from data-transfer APIs : Names like and usually accompany the transfer of large byte streams in network programming and pipes. Traditional UNIX signals, by contrast, are extremely lightweight: they carry no extra data payload modern can pass a small amount of data, but that is an extension , and convey only a pure event notification that “something happened.” So avoiding also avoids confusion with data transfer. Common Signals at a Glance During development and in interviews, a handful of high-frequency signals are worth knowing. Here I had AI summarize them for me, which also makes them handy for later use: Signal Name Default Number Default Behavior Typical Use Case / Meaning --- --- --- --- SIGHUP 1 Terminates the process Sent when the terminal disconnects; modern servers often repurpose it as Nginx/daemon “reload configuration” SIGINT 2 Terminates the process Ctrl + C , interrupts the foreground process SIGQUIT 3 Core dump and terminate Ctrl + \\\\ backslash , which not only quits but leaves a core dump file for debugging SIGKILL 9 Forcibly kills the process Cannot be caught or ignored . The kernel reclaims it directly; the program does not even get a chance to say goodbye SIGSEGV 11 Core dump and terminate Segmentation fault illegal memory access, such as a wild pointer or out-of-bounds access SIGPIPE 13 Terminates the process Triggered by continuing to write to a pipe/socket with no read end SIGALRM 14 Terminates the process Timer expiration triggered by the function SIGTERM 15 Terminates the process Software termination what the default command sends . Can be caught , and is the first choice for graceful shutdown SIGSTOP 19 Suspends the process Cannot be caught or ignored . Suspends execution like pressing pause SIGCONT 18 Resumes the process Resumes a process suspended by or for some other reason SIGUSR1/2 30/31 Terminates the process User-defined signals; developers can assign them whatever business meaning they like AI also went ahead and wrote an extra exam question all on its own, so I will record it here too: 💡 A high-frequency interview question : Why can 9 and 19 not be caught or ignored? Answer : Because they are the kernel's “absolute control” reserved for the administrator. If user code were allowed to catch , and someone happened to write an infinite loop that catches it and does nothing, the system could never again kill that rogue process by conventional means, and the whole system would spin completely out of control. Real-World Use Cases Where does the command actually get used day to day? Again, I had AI give me a quick summary: Daily Operations in the Shell : sends SIGTERM 15 by default. It gives the process a polite hint: “Buddy, that is about enough—wrap up what you are doing and head out.” : sends SIGKILL 9 . “No more talk—vanish this instant!” Note: using all the time easily leads to disasters such as uncommitted database transactions, temp files left behind, and unreleased locks. : sends SIGHUP 1 . In servers such as Nginx, it is commonly used for hot reloading: on receiving it, the master process reloads its configuration and gracefully restarts the worker processes, with zero service interruption. Backend Development and Graceful Shutdown When writing web services in Go, Java, or Node.js, if we stop a container with Docker which sends or press in a terminal which sends , the program cannot simply drop dead. A competent backend framework will listen for these termination signals : After receiving , stop accepting new HTTP requests. Finish the requests currently in flight and write them to the database. Disconnect from Redis and MySQL, release resources safely, and then exit gracefully. Simple Inter-Process Communication IPC In some multi-process daemons such as the Master-Worker pattern , the master process can use to tell a child process: “The log file has been rotated, so please reopen the log file handle.” What Should Ordinary Developers Watch Out For? Even if you usually write high-level business code and never call the system-level directly, you have definitely hit—or will sooner or later hit—the following signal-related “reefs”: The power of Ctrl+C : You run a long script in a terminal, your hand slips, and you press . In essence, the terminal driver has just sent to the foreground process group. Zombie processes and : A parent process forks a child; if the child dies and the parent never reaps its status through , the child becomes a zombie process. And when the child exits, the kernel sends the parent a signal. Many high-performance frameworks reap child processes efficiently precisely by catching asynchronously. Interrupted system calls : If your program is in the middle of a blocking I/O operation such as a on a socket , and a signal arrives and its handler runs, that may be interrupted and return , with set to . When writing low-level network libraries, you must explicitly handle this “interrupted by a signal” case usually by wrapping it in an outer loop and retrying . Async-signal safety Async-Signal-Safe : When writing a signal handler, what you can do inside it is extremely limited. Never call non-thread-safe or non-async-signal-safe functions such as or inside a signal handler, or you may easily cause deadlocks or memory corruption that are very hard to track down. Signals do not queue non-real-time signals get lost : Traditional standard signals 1–31 do not queue. If the same signal is sent to a process several times in quick succession, the process may only perceive it once. If your business logic absolutely cannot drop a single event, standard signals will not do the job you need real-time signals, or RT signals . Conclusion After going through all of this, I finally truly digested the inter-process communication function, and a little extra reading also gave me a deeper understanding of IPC. But honestly, this kind of misunderstanding is scary. Without AI helping me summarize things and answer my questions, I probably would not have wanted to research on my own why the IPC function is called for quite a while; I might have just assumed that inter-process communication exists entirely to finish the other party off. I also wonder how much more historical debt like this is out there... I hope this note can also help developers who are grinding through 408 operating systems, or whose scalps are tingling from wrestling with Linux signals, finally sort out the technical truth behind this historical baggage.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>Recently, while reviewing the “Process” chapter of the 408 operating systems course, I got to the signal mechanism of inter-process communication (IPC). Since this round was the intensive review phase, I was reading the textbook very carefully.</p>\n<p>I froze for a moment: “Wait—sending a signal between processes, and the system call is called kill()?”</p>\n<p>That cannot be right. Is all inter-process communication meant to kill the other party? To use an analogy: you send a friend a message saying “come downstairs and hang out,” and someone calls that operation <code>execute_friend(best_buddy)</code>. That is absurd, and genuinely counterintuitive, so I dug in a little deeper to find out what was going on.</p>\n<p>Why did the UNIX forefathers choose such a misleading name? Today let us dig into this “legacy naming” that has stayed with us for decades, and take a look at what it actually means in everyday development and in interviews.</p>\n<hr>\n<h2>The “Force Kill” the Name Misleads You Into</h2>\n<p>When I first saw the <code>kill()</code> system call, or the <code>kill</code> command in a terminal, I formed an obvious assumption: its only job is to “take out a process.”</p>\n<p>When troubleshooting a production incident or debugging a program, we habitually type <code>kill &lt;pid&gt;</code>, or the more brutal <code>kill -9 &lt;pid&gt;</code>, into a terminal. As the process vanishes instantly, I naturally equated that operation with the way a program ends.</p>\n<p>But in fact, that intuition of mine concealed a major technical misunderstanding.</p>\n<p>The essence of <code>kill()</code> is <strong>to deliver a signal to a specified process</strong>, not to “kill” it in the literal sense. Terminating the process is merely the <strong>default behavior</strong> of a process on receiving a particular signal, not a functional limit of the system call itself. In other words, <code>kill</code> is nothing more than an asynchronous <strong>communication mechanism</strong> for notifications, and “killing” is just one default result among many ways of handling signals.</p>\n<h2>What Does <code>kill(pid, sig)</code> Actually Do?</h2>\n<p>Let us first look at how <code>kill()</code> is normally used.</p>\n<p>In C, the prototype of the <code>kill()</code> system call is very simple:</p>\n<pre><code>#include &lt;sys/types.h&gt;\n#include &lt;signal.h&gt;\n\nint kill(pid_t pid, int sig);\n</code></pre>\n<ul>\n<li><strong><code>pid</code></strong>: the ID card number of the target process.</li>\n<li><strong><code>sig</code></strong>: the number of the signal you want to send (such as <code>SIGTERM</code>, <code>SIGUSR1</code>, and so on). If you pass <code>0</code> for <code>sig</code> (known as the null signal), <code>kill()</code> does not send a signal at all; it is used to <strong>probe whether the process exists</strong> and whether the current user has permission to send it signals (commonly used for liveness checks when writing daemons).</li>\n</ul>\n<h3>A Simple C Example</h3>\n<p>The code below shows how to send a custom signal (<code>SIGUSR1</code>) to a specified process, and how to check the process's status:</p>\n<pre><code class=\"language-c\">#include &lt;stdio.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;sys/types.h&gt;\n#include &lt;signal.h&gt;\n#include &lt;unistd.h&gt;\n\nint main(int argc, char *argv[]){\n    if (argc != 2) {\n        fprintf(stderr, &quot;Usage: %s &lt;pid&gt;\\n&quot;, argv[0]);\n        exit(1);\n    }\n\n    pid_t target_pid = atoi(argv[1]);\n\n    // 1. 探测进程是否存在\n    if (kill(target_pid, 0) == 0) {\n        printf(&quot;进程 %d 存活，正在发送自定义信号 SIGUSR1...\\n&quot;, target_pid);\n        // 2. 发送 SIGUSR1 信号\n        kill(target_pid, SIGUSR1);\n    } else {\n        perror(&quot;进程不存在或无权限&quot;);\n    }\n\n    return 0;\n}\n</code></pre>\n<p>Here, <code>kill()</code> merely delivers the signal into the kernel's process control block (PCB), completing a lightweight event notification.</p>\n<h2>The Historical Puzzle: Why <code>kill</code> and Not <code>send</code>?</h2>\n<p>Since its essence is sending a signal, why is it not called <code>send_signal()</code>, or simply <code>send()</code>? That would seem more intuitive.</p>\n<p>Answering that means rewinding to the <strong>wild early days of UNIX</strong> in the 1970s.</p>\n<ol>\n<li><strong>Historical constraints and original intent</strong>: In the earliest UNIX designs, inter-process communication was nowhere near as complex as it is today. There were very few kinds of signals, and the one purpose, the core scenario, they were designed for was—<strong>terminating processes that had run out of control, were stuck in an infinite loop, or were hogging resources</strong>. So the intuitive mission of this system call at the time was “take it out,” and the name <code>kill</code> was extremely apt.</li>\n<li><strong>Path dependence in the POSIX standard</strong>: As systems evolved, people found this “asynchronous notification mechanism” too useful to confine to killing processes; it could also announce state changes, reload configuration, report that a child process had exited, and more. Signals multiplied, and the feature expanded into general-purpose IPC. But by then the name <code>kill()</code> had already taken root in countless system source files and early books. To maintain backward compatibility, the POSIX standard ultimately kept the name.</li>\n<li><strong>Distinction from data-transfer APIs</strong>: Names like <code>send()</code> and <code>sendto()</code> usually accompany the <strong>transfer of large byte streams</strong> in network programming and pipes. Traditional UNIX signals, by contrast, are extremely lightweight: they <strong>carry no extra data payload</strong> (modern <code>sigqueue</code> can pass a small amount of data, but that is an extension), and convey only a pure event notification that “something happened.” So avoiding <code>send</code> also avoids confusion with data transfer.</li>\n</ol>\n<h2>Common Signals at a Glance</h2>\n<p>During development and in interviews, a handful of high-frequency signals are worth knowing. Here I had AI summarize them for me, which also makes them handy for later use:</p>\n<table>\n<thead>\n<tr>\n<th><strong>Signal Name</strong></th>\n<th><strong>Default Number</strong></th>\n<th><strong>Default Behavior</strong></th>\n<th><strong>Typical Use Case / Meaning</strong></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>SIGHUP</strong></td>\n<td>1</td>\n<td>Terminates the process</td>\n<td>Sent when the terminal disconnects; modern servers often repurpose it as Nginx/daemon <strong>“reload configuration”</strong></td>\n</tr>\n<tr>\n<td><strong>SIGINT</strong></td>\n<td>2</td>\n<td>Terminates the process</td>\n<td><strong>Ctrl + C</strong>, interrupts the foreground process</td>\n</tr>\n<tr>\n<td><strong>SIGQUIT</strong></td>\n<td>3</td>\n<td>Core dump and terminate</td>\n<td><strong>Ctrl + \\</strong> (backslash), which not only quits but leaves a core dump file for debugging</td>\n</tr>\n<tr>\n<td><strong>SIGKILL</strong></td>\n<td>9</td>\n<td><strong>Forcibly kills the process</strong></td>\n<td><strong>Cannot be caught or ignored</strong>. The kernel reclaims it directly; the program does not even get a chance to say goodbye</td>\n</tr>\n<tr>\n<td><strong>SIGSEGV</strong></td>\n<td>11</td>\n<td>Core dump and terminate</td>\n<td>Segmentation fault (illegal memory access, such as a wild pointer or out-of-bounds access)</td>\n</tr>\n<tr>\n<td><strong>SIGPIPE</strong></td>\n<td>13</td>\n<td>Terminates the process</td>\n<td>Triggered by continuing to write to a pipe/socket with no read end</td>\n</tr>\n<tr>\n<td><strong>SIGALRM</strong></td>\n<td>14</td>\n<td>Terminates the process</td>\n<td>Timer expiration (triggered by the <code>alarm()</code> function)</td>\n</tr>\n<tr>\n<td><strong>SIGTERM</strong></td>\n<td>15</td>\n<td>Terminates the process</td>\n<td><strong>Software termination</strong> (what the default <code>kill</code> command sends). <strong>Can be caught</strong>, and is the first choice for graceful shutdown</td>\n</tr>\n<tr>\n<td><strong>SIGSTOP</strong></td>\n<td>19</td>\n<td><strong>Suspends the process</strong></td>\n<td><strong>Cannot be caught or ignored</strong>. Suspends execution (like pressing pause)</td>\n</tr>\n<tr>\n<td><strong>SIGCONT</strong></td>\n<td>18</td>\n<td>Resumes the process</td>\n<td>Resumes a process suspended by <code>SIGSTOP</code> or for some other reason</td>\n</tr>\n<tr>\n<td><strong>SIGUSR1/2</strong></td>\n<td>30/31</td>\n<td>Terminates the process</td>\n<td>User-defined signals; developers can assign them whatever business meaning they like</td>\n</tr>\n</tbody>\n</table>\n<p>AI also went ahead and wrote an extra exam question all on its own, so I will record it here too:</p>\n<blockquote>\n<p>💡 <strong>A high-frequency interview question</strong>:</p>\n<p>Why can <code>SIGKILL</code> (9) and <code>SIGSTOP</code> (19) not be caught or ignored?</p>\n<p><strong>Answer</strong>: Because they are the kernel's “absolute control” reserved for the administrator. If user code were allowed to catch <code>SIGKILL</code>, and someone happened to write an infinite loop that catches it and does nothing, the system could never again kill that rogue process by conventional means, and the whole system would spin completely out of control.</p>\n</blockquote>\n<h2>Real-World Use Cases</h2>\n<p>Where does the <code>kill</code> command actually get used day to day? Again, I had AI give me a quick summary:</p>\n<h3>1. Daily Operations in the Shell</h3>\n<ul>\n<li><code>kill &lt;pid&gt;</code>: sends <strong>SIGTERM (15)</strong> by default. It gives the process a polite hint: “Buddy, that is about enough—wrap up what you are doing and head out.”</li>\n<li><code>kill -9 &lt;pid&gt;</code>: sends <strong>SIGKILL (9)</strong>. “No more talk—vanish this instant!” (Note: using <code>9</code> all the time easily leads to disasters such as uncommitted database transactions, temp files left behind, and unreleased locks.)</li>\n<li><code>kill -HUP &lt;pid&gt;</code>: sends <strong>SIGHUP (1)</strong>. In servers such as Nginx, it is commonly used for hot reloading: on receiving it, the master process reloads its configuration and gracefully restarts the worker processes, with zero service interruption.</li>\n</ul>\n<h3>2. Backend Development and Graceful Shutdown</h3>\n<p>When writing web services in Go, Java, or Node.js, if we stop a container with Docker (which sends <code>SIGTERM</code>) or press <code>Ctrl+C</code> in a terminal (which sends <code>SIGINT</code>), the program cannot simply drop dead.<br>\nA competent backend framework will <strong>listen for these termination signals</strong>:</p>\n<ul>\n<li>After receiving <code>SIGTERM</code>, stop accepting new HTTP requests.</li>\n<li>Finish the requests currently in flight and write them to the database.</li>\n<li>Disconnect from Redis and MySQL, release resources safely, and then exit gracefully.</li>\n</ul>\n<h3>3. Simple Inter-Process Communication (IPC)</h3>\n<p>In some multi-process daemons (such as the Master-Worker pattern), the master process can use <code>kill(worker_pid, SIGUSR1)</code> to tell a child process: “The log file has been rotated, so please reopen the log file handle.”</p>\n<h2>What Should Ordinary Developers Watch Out For?</h2>\n<p>Even if you usually write high-level business code and never call the system-level <code>kill()</code> directly, you have definitely hit—or will sooner or later hit—the following signal-related “reefs”:</p>\n<ol>\n<li><strong>The power of Ctrl+C</strong>: You run a long script in a terminal, your hand slips, and you press <code>Ctrl+C</code>. In essence, the terminal driver has just sent <code>SIGINT</code> to the foreground process group.</li>\n<li><strong>Zombie processes and <code>SIGCHLD</code></strong>: A parent process forks a child; if the child dies and the parent never reaps its status through <code>wait()</code>, the child becomes a zombie process. And when the child exits, the kernel sends the parent a <strong><code>SIGCHLD</code></strong> signal. Many high-performance frameworks reap child processes efficiently precisely by catching <code>SIGCHLD</code> asynchronously.</li>\n<li><strong>Interrupted system calls (<code>EINTR</code>)</strong>: If your program is in the middle of a blocking I/O operation (such as a <code>read</code> on a socket), and a signal arrives and its handler runs, that <code>read</code> may be interrupted and return <code>1</code>, with <code>errno</code> set to <code>EINTR</code>. When writing low-level network libraries, you must explicitly handle this “interrupted by a signal” case (usually by wrapping it in an outer <code>while</code> loop and retrying).</li>\n<li><strong>Async-signal safety (Async-Signal-Safe)</strong>: When writing a signal handler, what you can do inside it is extremely limited. Never call non-thread-safe or non-async-signal-safe functions such as <code>printf()</code> or <code>malloc()</code> inside a signal handler, or you may easily cause deadlocks or memory corruption that are very hard to track down.</li>\n<li><strong>Signals do not queue (non-real-time signals get lost)</strong>: Traditional standard signals (1–31) do not queue. If the same signal is sent to a process several times in quick succession, the process may only perceive it once. If your business logic absolutely cannot drop a single event, standard signals will not do the job (you need real-time signals, or RT signals).</li>\n</ol>\n<h2>Conclusion</h2>\n<p>After going through all of this, I finally truly digested the inter-process communication function, and a little extra reading also gave me a deeper understanding of IPC.</p>\n<p>But honestly, this kind of misunderstanding is scary. Without AI helping me summarize things and answer my questions, I probably would not have wanted to research on my own why the IPC function is called <code>kill()</code> for quite a while; I might have just assumed that inter-process communication exists entirely to finish the other party off. I also wonder how much more historical debt like this is out there...</p>\n<p>I hope this note can also help developers who are grinding through 408 operating systems, or whose scalps are tingling from wrestling with Linux signals, finally sort out the technical truth behind this historical baggage.</p>\n",
      "summary": "[AI-translated] While in the intensive review phase for the 408 exam, I noticed that the inter-process communication function is named kill(), which made me curious about what the name actually means.",
      "date_published": "2026-10-22T00:00:00.000Z",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/why-does-some-website-has-www-2-en",
      "url": "https://markxu.icu/blog/why-does-some-website-has-www-2-en",
      "title": "Why Do Some Websites Have “www” and Others Don’t? — The Modern Configuration Edition",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. Cover image: Tim Berners-Lee at the 20th anniversary celebration of the World Wide Web in 2009, reunited at CERN with the historic NeXT computer he had used to develop and run the world’s first web server, multimedia browser, and web page editor. Image credit: CERN The previous post covered where came from — a naming convention suggested by Tim Berners-Lee, and a necessary way of telling services apart in the early days. But today, now that the Web has gone from “one machine running one service” to “one IP hosting countless websites,” why is no longer a necessity? And how should we deal with those headache-inducing DNS details when deploying a site? That is what I want to cover in this post. Why Did So Many Websites Stop Using Later? Because the way the internet gets used changed. When a website still meant one machine serving one service, was useful: it kept the web service separate from the mail service and made things easier to manage. Later, as machines grew more powerful, management also became simpler and easier to maintain. In particular, virtual hosting, the HTTP request header RFC 9110 , reverse proxies, CDNs, and cloud platforms gradually became commonplace, so a single IP address could route requests to different websites based on the domain name. The domain name itself no longer had to do the job of explaining what the machine was for. This also matches the thought Tim arrived at by the end of his answer to Why www?. At the same time, is four characters shorter than . That means it is shorter everywhere it shows up — on posters, business cards, video voiceovers, billboards, and anywhere users type it in by hand. That is obviously better for promoting your own brand and getting people to notice it at a glance, so more and more websites choose the bare domain — , with no subdomain — as the address they present to the world. That does not mean is technically outdated. Keeping has some practical benefits. It tells the network infrastructure plainly that this is a website; and if you later want to split off , , or other services, the boundaries stay clearer. Cookie scope RFC 6265 can also be controlled more carefully: a site sitting on will not, by default, send host-level cookies that belong only to the site over to . Where It Actually Gets Tricky: Bare Domains and CNAME At this point you might think that since is just a subdomain, you can simply skip it. But things get a little more complicated when you deploy a website, especially the first time you try a CDN, a hosting platform, or a cloud provider. Here is a first way to understand the two most common DNS records: An A record points a name straight at an IP address. A CNAME RFC 1034 §3.6.2, RFC 1035 instead says: for this name, keep looking under another name. CNAMEs are a good fit for connecting to a hosting platform. When the platform changes its server IP, you do not need to edit your own DNS every day; you just keep pointing at the domain the platform provides. But under the traditional DNS model, once a name has a CNAME, it cannot also have records of other types — RFC 2181 §10.1 states this explicitly. The problem is that the bare domain is not an ordinary subdomain. That name itself has to carry the domain’s foundational records, such as NS and SOA; it often also holds MX records for email and TXT records for domain verification. Under the traditional rules, a bare domain cannot simply become a CNAME, because those records would then have nowhere to coexist. This is also why, in the past, many hosting platforms told you: point at a CNAME, and have the bare domain do a redirect. As an ordinary subdomain, can point at the platform quite naturally; the bare domain, because of the historical structure of DNS, is not that straightforward to handle. How Do Modern DNS Providers Solve This? Later, DNS providers offered solutions under various names, such as ALIAS, ANAME, or CNAME flattening. Their specific implementations are not entirely the same, but the idea is close: you write a CNAME-like target in the dashboard, the provider resolves that target to an IP for you, and then returns it to the resolver in a form the bare domain can use. Examples include Cloudflare CNAME Flattening, DNSimple ANAME, and AWS Route 53 Alias. The IETF once had a draft, but it has not yet become a formal standard. The provider does the extra resolution work at the DNS edge. As these features became widespread, connecting a bare domain to a CDN or hosting platform was no longer as awkward as before, so “no ” became easy again. So Which One Should I Choose? If it is just a personal blog or a small project, you can simply go with each platform’s recommendation — and besides, there is AI now. If you like shorter addresses, use the bare domain; if you want to keep the traditional habit, or might later separate the site from other subdomain services more clearly, use . Both approaches can be done well, and neither is much trouble. Whichever one you choose as the main address, you still have to get redirects and certificates right for the other. DNS has to resolve, the HTTPS certificate has to cover it, and then a 301 has to unify the redirect. Take my own site as an example — I have both configured. Feel free to click and try. www.markxu.icu markxu.icu Google Search Central recommends choosing one canonical address and using a 301 redirect, so that link sharing, caching, analytics, and search engine indexing all stay consistent more easily. Then Why Does Adding Sometimes Fail to Load? If a website’s bare domain loads, but manually adding throws an error, that is uncommon, but it does happen. The reason is usually that the administrator configured only one of the addresses mentioned above. A complete visit roughly passes through these places in order: If any one of these steps is not configured properly, something goes wrong. The simplest case is that DNS has no record at all. The lookup result is NXDOMAIN, and the browser cannot even find the server. It is also possible that DNS already points at the correct IP, but Nginx, Apache, or the hosting platform has no configuration for that Host, so the server returns a default page, a 404, or simply refuses the request. There is also an ordering problem that often gets overlooked: if the user visits , the TLS certificate check happens before the HTTP redirect RFC 9110 §4.2.2 . If the certificate only covers , the browser will block the request at the HTTPS step, before the server has a chance to tell it “please go to the address without .” That is why certificates usually need to cover both names, or you use a wildcard certificate that includes them both. A fully configured website often lets both addresses find it, and then picks one as the canonical address: Of course, you can also do the reverse and redirect the bare domain to ; that is entirely your personal choice, and technically the difference is not obvious. References RFC 1034: Domain Names - Concepts and Facilities RFC 1035: Domain Names - Implementation and Specification RFC 2181: Clarifications to the DNS Specification §10.1 RFC 6265: HTTP State Management Mechanism RFC 9110: HTTP Semantics Cloudflare: DNS CNAME record Cloudflare: CNAME Flattening Google Search Central: Consolidate duplicate URLs",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<blockquote>\n<p><a href=\"https://cerncourier.com/a/happy-20th-birthday-world-wide-web/\">Cover image</a>: Tim Berners-Lee at the 20th anniversary celebration of the World Wide Web in 2009, reunited at CERN with the historic NeXT computer he had used to develop and run the world’s first web server, multimedia browser, and web page editor. Image credit: CERN</p>\n</blockquote>\n<p>The previous post covered where <code>www</code> came from — a naming convention suggested by Tim Berners-Lee, and a necessary way of telling services apart in the early days.</p>\n<p>But today, now that the Web has gone from “one machine running one service” to “one IP hosting countless websites,” why is <code>www</code> no longer a necessity? And how should we deal with those headache-inducing DNS details when deploying a site? That is what I want to cover in this post.</p>\n<h2>Why Did So Many Websites Stop Using <code>www</code> Later?</h2>\n<p>Because the way the internet gets used changed.</p>\n<p>When a website still meant one machine serving one service, <code>www</code> was useful: it kept the web service separate from the mail service and made things easier to manage.</p>\n<p>Later, as machines grew more powerful, management also became simpler and easier to maintain. In particular, virtual hosting, the HTTP <code>Host</code> request header (<a href=\"https://datatracker.ietf.org/doc/html/rfc9110\">RFC 9110</a>), reverse proxies, CDNs, and cloud platforms gradually became commonplace, so a single IP address could route requests to different websites based on the domain name. The domain name itself no longer had to do the job of explaining what the machine was for.</p>\n<p>This also matches the thought Tim arrived at by the end of his answer to <a href=\"https://www.w3.org/Provider/Style/www.html\">Why www?</a>.</p>\n<p><img src=\"https://img.markxu.icu/img20261002120440228.png\" alt=\"CDN\"></p>\n<p>At the same time, <code>blahblah.com</code> is four characters shorter than <code>www.blahblah.com</code>. That means it is shorter everywhere it shows up — on posters, business cards, video voiceovers, billboards, and anywhere users type it in by hand. That is obviously better for promoting your own brand and getting people to notice it at a glance, so more and more websites choose the bare domain — <code>blahblah.com</code>, with no subdomain — as the address they present to the world.</p>\n<p>That does not mean <code>www</code> is technically outdated.</p>\n<p>Keeping <code>www</code> has some practical benefits. It tells the network infrastructure plainly that this is a website; and if you later want to split off <code>api.blahblah.com</code>, <code>static.blahblah.com</code>, or other services, the boundaries stay clearer. Cookie scope (<a href=\"https://datatracker.ietf.org/doc/html/rfc6265\">RFC 6265</a>) can also be controlled more carefully: a site sitting on <code>www.blahblah.com</code> will not, by default, send host-level cookies that belong only to the site over to <code>api.blahblah.com</code>.</p>\n<h2>Where It Actually Gets Tricky: Bare Domains and CNAME</h2>\n<p>At this point you might think that since <code>www</code> is just a subdomain, you can simply skip it. But things get a little more complicated when you deploy a website, especially the first time you try a CDN, a hosting platform, or a cloud provider.</p>\n<p>Here is a first way to understand the two most common DNS records:</p>\n<pre><code class=\"language-text\">A     blahblah.com       -&gt; 203.0.113.10\nCNAME www.blahblah.com   -&gt; my-site.hosting-provider.com\n</code></pre>\n<p>An A record points a name straight at an IP address. A <a href=\"https://www.cloudflare.com/learning/dns/dns-records/dns-cname-record/\">CNAME</a> (<a href=\"https://datatracker.ietf.org/doc/html/rfc1034\">RFC 1034</a> §3.6.2, <a href=\"https://datatracker.ietf.org/doc/html/rfc1035\">RFC 1035</a>) instead says: for this name, keep looking under another name.</p>\n<p>CNAMEs are a good fit for connecting to a hosting platform. When the platform changes its server IP, you do not need to edit your own DNS every day; you just keep pointing at the domain the platform provides. But under the traditional DNS model, once a name has a CNAME, it cannot also have records of other types — <a href=\"https://datatracker.ietf.org/doc/html/rfc2181\">RFC 2181</a> §10.1 states this explicitly.</p>\n<p>The problem is that the bare domain <code>blahblah.com</code> is not an ordinary subdomain. That name itself has to carry the domain’s foundational records, such as NS and SOA; it often also holds MX records for email and TXT records for domain verification. Under the traditional rules, a bare domain cannot simply become a CNAME, because those records would then have nowhere to coexist.</p>\n<p>This is also why, in the past, many hosting platforms told you: point <code>www.blahblah.com</code> at a CNAME, and have the bare domain do a redirect. As an ordinary subdomain, <code>www</code> can point at the platform quite naturally; the bare domain, because of the historical structure of DNS, is not that straightforward to handle.</p>\n<h2>How Do Modern DNS Providers Solve This?</h2>\n<p>Later, DNS providers offered solutions under various names, such as ALIAS, ANAME, or CNAME flattening. Their specific implementations are not entirely the same, but the idea is close: you write a CNAME-like target in the dashboard, the provider resolves that target to an IP for you, and then returns it to the resolver in a form the bare domain can use. Examples include <a href=\"https://developers.cloudflare.com/dns/cname-flattening/\">Cloudflare CNAME Flattening</a>, <a href=\"https://docs.dnsimple.com/articles/alias-record/\">DNSimple ANAME</a>, and <a href=\"https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-choosing-alias-non-alias.html\">AWS Route 53 Alias</a>. The IETF once had a <code>draft-ietf-dnsop-aname</code> draft, but it has not yet become a formal standard.</p>\n<p>The provider does the extra resolution work at the DNS edge. As these features became widespread, connecting a bare domain to a CDN or hosting platform was no longer as awkward as before, so “no <code>www</code>” became easy again.</p>\n<h2>So Which One Should I Choose?</h2>\n<p>If it is just a personal blog or a small project, you can simply go with each platform’s recommendation — and besides, there is AI now.</p>\n<p>If you like shorter addresses, use the bare domain; if you want to keep the traditional habit, or might later separate the site from other subdomain services more clearly, use <code>www</code>. Both approaches can be done well, and neither is much trouble.</p>\n<p>Whichever one you choose as the main address, you still have to get redirects and certificates right for the other. DNS has to resolve, the HTTPS certificate has to cover it, and then a 301 has to unify the redirect. Take my own site as an example — I have both configured. Feel free to click and try.</p>\n<ul>\n<li><a href=\"https://www.markxu.icu\">www.markxu.icu</a></li>\n<li><a href=\"https://markxu.icu\">markxu.icu</a></li>\n</ul>\n<p><a href=\"https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls\">Google Search Central recommends</a> choosing one canonical address and using a 301 redirect, so that link sharing, caching, analytics, and search engine indexing all stay consistent more easily.</p>\n<h2>Then Why Does Adding <code>www</code> Sometimes Fail to Load?</h2>\n<p>If a website’s bare domain loads, but manually adding <code>www</code> throws an error, that is uncommon, but it does happen. The reason is usually that the administrator configured only one of the addresses mentioned above.</p>\n<p>A complete visit roughly passes through these places in order:</p>\n<pre><code class=\"language-text\">Browser\n  -&gt; DNS: does www.example.com have a record?\n  -&gt; Server: do I accept www.blahblah.com?\n  -&gt; HTTPS: does the certificate cover www.blahblah.com?\n  -&gt; Web app: return the page, or redirect to the canonical address\n</code></pre>\n<p>If any one of these steps is not configured properly, something goes wrong.</p>\n<p>The simplest case is that DNS has no <code>www</code> record at all. The lookup result is NXDOMAIN, and the browser cannot even find the server.</p>\n<p>It is also possible that DNS already points <code>www.blahblah.com</code> at the correct IP, but Nginx, Apache, or the hosting platform has no configuration for that Host, so the server returns a default page, a 404, or simply refuses the request.</p>\n<p>There is also an ordering problem that often gets overlooked: if the user visits <code>https://www.blahblah.com</code>, the TLS certificate check happens before the HTTP redirect (<a href=\"https://datatracker.ietf.org/doc/html/rfc9110\">RFC 9110</a> §4.2.2). If the certificate only covers <code>blahblah.com</code>, the browser will block the request at the HTTPS step, before the server has a chance to tell it “please go to the address without <code>www</code>.” That is why certificates usually need to cover both names, or you use a wildcard certificate that includes them both.</p>\n<p>A fully configured website often lets both addresses find it, and then picks one as the canonical address:</p>\n<pre><code class=\"language-text\">https://www.blahblah.com  -&gt; 301 -&gt; https://blahblah.com\nhttps://blahblah.com       -&gt; 200\n</code></pre>\n<p>Of course, you can also do the reverse and redirect the bare domain to <code>www</code>; that is entirely your personal choice, and technically the difference is not obvious.</p>\n<h2>References</h2>\n<ul>\n<li><a href=\"https://datatracker.ietf.org/doc/html/rfc1034\">RFC 1034: Domain Names - Concepts and Facilities</a></li>\n<li><a href=\"https://datatracker.ietf.org/doc/html/rfc1035\">RFC 1035: Domain Names - Implementation and Specification</a></li>\n<li><a href=\"https://datatracker.ietf.org/doc/html/rfc2181\">RFC 2181: Clarifications to the DNS Specification</a> §10.1</li>\n<li><a href=\"https://datatracker.ietf.org/doc/html/rfc6265\">RFC 6265: HTTP State Management Mechanism</a></li>\n<li><a href=\"https://datatracker.ietf.org/doc/html/rfc9110\">RFC 9110: HTTP Semantics</a></li>\n<li><a href=\"https://www.cloudflare.com/learning/dns/dns-records/dns-cname-record/\">Cloudflare: DNS CNAME record</a></li>\n<li><a href=\"https://developers.cloudflare.com/dns/cname-flattening/\">Cloudflare: CNAME Flattening</a></li>\n<li><a href=\"https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls\">Google Search Central: Consolidate duplicate URLs</a></li>\n</ul>\n",
      "summary": "[AI-translated] Picking up where the previous post left off, this one focuses on why we no longer use www today, and on how we actually handle it now.",
      "date_published": "2026-10-01T00:00:00.000Z",
      "date_modified": "2026-10-20T00:00:00.000Z",
      "image": "https://img.markxu.icu/img20261001152633367.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/why-does-some-website-has-www-1-en",
      "url": "https://markxu.icu/blog/why-does-some-website-has-www-1-en",
      "title": "Why Do Some Websites Have “www” and Others Don't? — The Origin Story",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. The cover image is a photograph of the world's first web server: the NeXT computer that Tim Berners-Lee used at CERN. , meaning the World Wide Web, is a pretty basic concept — even high school teachers have already covered it. But today, while reading a few blog sites, I suddenly noticed something I had never seriously thought about before: some URLs are , while others are just . Before I looked into it, I assumed every website had a , and that the browser simply decided those few letters could be left off, so it hid them from us. Today I got curious and did some digging, and it turns out the browser hiding is not the whole story at all. Current technology made unnecessary a long time ago, and most modern websites have stopped using it too — where it survives, it is mostly a leftover historical habit. So this post is mainly about the origin of the prefix; a later one will cover why it disappeared afterward, and how we use domain names today. First, the technical side: why does exist at all? The earliest Internet differed from today's Internet in plenty of infrastructure-level ways, so many of the protocols and features from back then were designed with very different intentions. If we are curious about why exists, we probably need to start from the very beginning of the Internet. The Early Internet — 1969 When the earliest Internet — or rather its predecessor, ARPANET 1969 — came into being, it connected the computers of a few universities and research institutes. Each machine had a hostname, registered directly in the centrally maintained HOSTS.TXT which DNS would later replace . The intention back then was simple: connect a few machines so they could talk to each other. Because there were so few machines, the concept of a domain name did not exist yet, and there was no need for an extra layer of indirection. A simple “machine name → address” mapping was enough. For the specifics of this period, see this article, Internet History Ramblings, Part 7 — ARPANET the other posts in that series have nothing to do with this one, but they are genuinely fun — I highly recommend reading more of them The Arrival of DNS Domain Names — 1983 As more and more machines joined, the network had already become something entirely different from what it once was. On January 1, 1983, every networked host switched from NCP to TCP/IP, an event also known as TCP/IP Flag Day. All machines dropped support for the old protocol and supported TCP/IP instead, turning the previous “single network, flat, centralized” architecture into a “multi-network, layered, delegable” one. That also met the demand of a growing number of machines joining the network. As more and more machines joined, the HOSTS.TXT file, previously maintained by hand at a central location, began to balloon. Back when storage was not the cheap, bottomless resource it is today, having every machine store a copy of HOST.TXT became increasingly unrealistic. Even harder to swallow: whenever something on the network changed, an administrator had to recompile the HOST.TXT file and push it to every machine over FTP, which usually happened once or twice a week. That delay, too, was unacceptable. Later, on November 23, 1983, Paul Mockapetris designed DNS and submitted RFC 882 and RFC 883 later replaced in 1987 by RFC 1034 / RFC 1035 . Its core ideas can be summed up in a few points: A consistent namespace: do not stuff network numbers, addresses, or routing information into names Distributed maintenance: the full database cannot be stored centrally; it has to be sharded + cached Delegated authority: whoever owns the data controls the update/cache policy A general-purpose directory service: not just host addresses, but mailboxes and future unknown record types too That guaranteed a decentralized distributed system each organization could manage the naming of its own machines that made updating the network easy. The Prerequisite for We said earlier that every organization could manage its own machines, and that by allocating domain names for itself, an organization could divide up responsibilities very conveniently. Although port technology had existed since 1980, allowing one machine to run multiple services, early machines were expensive and short on resources — you could not have a single machine handle both web service and mail service. So the early approach was to run different services on different machines, like this: That came with a number of benefits at the same time: Clear management boundaries — mail was the sysadmin's problem, the Web belonged to the newly formed network team; each side ran its own machines and no one had to take the blame for the other. Fault isolation — if the mail service went down, it could not affect the Web, and vice versa. The DNS design philosophy — the domain name system was a “hostname → IP” mapping from the very start, not a “service name → IP” mapping. You give each machine a name, and you call it whatever service it runs. So is essentially just a third-level domain, with no special privileges whatsoever. The Arrival of — 1989 In 1989, Tim the person on the cover proposed the concept of the World Wide Web at CERN. The point was not to lay down a website prefix and call on everyone to use as the domain for web service; it was to propose the URL/HTTP/HTML hypertext system, so that documents could link to each other across machines and be published across organizations. For instance, CERN's first web server used info.cern.ch, not www.cern.ch. By the end of 1990, Tim had successfully launched the first web server and browser at CERN, proving that the ideas behind the proposal were workable. He wrote the relevant code directly on a NeXT computer and kept that server running in his office. To stop anyone from accidentally shutting the server down, he stuck a label written in red ink onto the computer: “This machine is a server. DO NOT POWER IT DOWN!” But when the Web joined an existing network as a new service, administrators usually gave it its own hostname. ftp/mail/news and the like were already named after their services, so the Web carried on the pattern with www. A Little of What Came After — 1990s The popularity of the www naming may also have had something to do with the release of the Mosaic browser. In 1993, NCSA released Mosaic, the first browser that let the general public look at images and click links. NCSA's own web server was called www.ncsa.uiuc.edu. The bookmarks and sample links built into Mosaic all pointed at that address, so the first thing users around the world saw when they encountered the Web was www. Then came Yahoo 1994 , Amazon 1994 , and eBay 1995 , the first wave of commercial internet companies, and their new URLs followed the prefix as well. That is how it became familiar to the general public. Why ? So why, out of all these names, ? Someone once emailed Tim to ask exactly this; you can click the link to see Tim's explanation. His explanation is quite interesting: www was something he “suggested,” not something he “mandated.” “It is a convention. I suggested it early on, in this guide...” He suggested it in a Style Guide a guide written for webmasters — that is, he wrote an article saying “you could do it this way.” Why not use the machine's own name? “An alias was a better alternative to 'pegasus.foo.com' which typically resulted when someone who happened to have a machine called pegasus started to run a web server...” What he means is: in the early days, plenty of people just grabbed whatever machine was at hand — one called pegasus, say — and ran the Web on it, so visitors had to use pegasus.foo.com. That name meant nothing; users could not remember it, and they could not tell what it was for. So he suggested giving it an alias, www, so that one glance told you it was a web server. Why the three letters “www”? Because the existing convention was already to name hosts after their service abbreviation: He was simply fitting the Web into that existing naming scheme. www was not chosen because it was special, but because it followed the same pattern as ftp and mail. Why not just use the bare domain foo.com? He gives two reasons: “Typically early webmasters could not have comandeered the 'foo.com' address itself.” Meaning: the bare domain foo.com is the “root identity” of the domain, usually controlled by the sysadmin, and the early Web people webmasters had no permission to change the DNS records of the bare domain. All they could do was add a subdomain within the area they could control. “You don't have this flexibility of configuration if you point everyone at 'foo.com' itself for all services.” The other reason is flexibility: with a www alias, if the web service grew later on, you could easily point www at a bigger machine without affecting the bare domain or the other services. One Entertaining Detail At the end of his answer, Tim says he is “of two minds about it now.” Because by 1999, the Web had become the biggest service for many companies, and he thought maybe it was time to let the Web claim the bare domain. But he stresses at the same time: you can only pick one, you cannot run both as the main site, or your cache efficiency gets cut in half. That is also in line with the idea in our next post, about why today's networks no longer use and can just link to the bare domain instead.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<blockquote>\n<p>The cover image is a photograph of the world's first web server: the NeXT computer that Tim Berners-Lee used at CERN.</p>\n</blockquote>\n<p><code>www</code>, meaning the World Wide Web, is a pretty basic concept — even high school teachers have already covered it.</p>\n<p>But today, while reading a few blog sites, I suddenly noticed something I had never seriously thought about before: some URLs are <code>www.blahblah.com</code>, while others are just <code>blahblah.com</code>.</p>\n<p>Before I looked into it, I assumed every website had a <code>www</code>, and that the browser simply decided those few letters could be left off, so it hid them from us. Today I got curious and did some digging, and it turns out the browser hiding is not the whole story at all. Current technology made <code>www</code> unnecessary a long time ago, and most modern websites have stopped using it too — where it survives, it is mostly a leftover historical habit.</p>\n<p>So this post is mainly about the origin of the <code>www</code> prefix; a later one will cover why it disappeared afterward, and how we use domain names today.</p>\n<p><strong>First, the technical side: why does <code>www</code> exist at all?</strong></p>\n<p>The earliest Internet differed from today's Internet in plenty of infrastructure-level ways, so many of the protocols and features from back then were designed with very different intentions. If we are curious about why <code>www</code> exists, we probably need to start from the very beginning of the Internet.</p>\n<h2>The Early Internet — 1969</h2>\n<p>When the earliest Internet — or rather its predecessor, ARPANET (1969) — came into being, it connected the computers of a few universities and research institutes. Each machine had a hostname, registered directly in the centrally maintained HOSTS.TXT (which DNS would later replace).<br>\n<img src=\"https://img.markxu.icu/img20260926235052374.png\" alt=\"A sketch of the earliest Internet\"></p>\n<p>The intention back then was simple: connect a few machines so they could talk to each other.</p>\n<p>Because there were so few machines, the concept of a domain name did not exist yet, and there was no need for an extra layer of indirection. A simple “machine name → address” mapping was enough.</p>\n<p>For the specifics of this period, see this article, <a href=\"https://www.cnblogs.com/xusheng/articles/2420494.html\">Internet History Ramblings, Part 7 — ARPANET</a> <em>(the other posts in that series have nothing to do with this one, but they are genuinely fun — I highly recommend reading more of them)</em></p>\n<h2>The Arrival of DNS (Domain Names) — 1983</h2>\n<p>As more and more machines joined, the network had already become something entirely different from what it once was.</p>\n<p>On January 1, 1983, every networked host switched from NCP to TCP/IP, an event also known as <a href=\"https://psu.pb.unizin.org/ist110/chapter/1-4-history-of-the-internet/\">TCP/IP Flag Day</a>.</p>\n<p>All machines dropped support for the old protocol and supported TCP/IP instead, turning <strong>the previous “single network, flat, centralized” architecture into a “multi-network, layered, delegable” one.</strong> That also met the demand of a growing number of machines joining the network.</p>\n<p>As more and more machines joined, the HOSTS.TXT file, previously maintained by hand at a central location, began to balloon. Back when storage was not the cheap, bottomless resource it is today, having every machine store a copy of HOST.TXT became increasingly unrealistic. Even harder to swallow: whenever something on the network changed, an administrator had to recompile the HOST.TXT file and push it to every machine over FTP, which usually happened once or twice a week. That delay, too, was unacceptable.</p>\n<p>Later, on November 23, 1983, <a href=\"https://awards.acm.org/award-recipients/mockapetris_3342151\">Paul Mockapetris</a> designed DNS and submitted RFC 882 and RFC 883 (later replaced in 1987 by <a href=\"https://datatracker.ietf.org/doc/html/rfc1034\">RFC 1034</a> / RFC 1035).</p>\n<p><img src=\"https://img.markxu.icu/img20261001130353957.png\" alt=\"Paul Mockapetris\"></p>\n<p>Its core ideas can be summed up in a few points:</p>\n<ul>\n<li>A consistent namespace: do not stuff network numbers, addresses, or routing information into names</li>\n<li>Distributed maintenance: the full database cannot be stored centrally; it has to be sharded + cached</li>\n<li>Delegated authority: whoever owns the data controls the update/cache policy</li>\n<li>A general-purpose directory service: not just host addresses, but mailboxes and future unknown record types too</li>\n</ul>\n<p>That guaranteed a decentralized distributed system (each organization could manage the naming of its own machines) that made updating the network easy.</p>\n<h2>The Prerequisite for <code>www</code></h2>\n<p>We said earlier that every organization could manage its own machines, and that by allocating domain names for itself, an organization could divide up responsibilities very conveniently.</p>\n<p>Although port technology had existed since 1980, allowing one machine to run multiple services, early machines were expensive and short on resources — you could not have a single machine handle both web service and mail service. So the early approach was to run different services on different machines, like this:</p>\n<pre><code class=\"language-text\">ftp.example.com    file transfer\nmail.example.com   mail\nnews.example.com   newsgroups\n</code></pre>\n<p>That came with a number of benefits at the same time:</p>\n<ol>\n<li>Clear management boundaries — mail was the sysadmin's problem, the Web belonged to the newly formed network team; each side ran its own machines and no one had to take the blame for the other.</li>\n<li>Fault isolation — if the mail service went down, it could not affect the Web, and vice versa.</li>\n<li>The DNS design philosophy — the domain name system was a “hostname → IP” mapping from the very start, not a “service name → IP” mapping. You give each machine a name, and you call it whatever service it runs.</li>\n</ol>\n<p><strong>So <code>www</code> is essentially just a third-level domain, with no special privileges whatsoever.</strong></p>\n<h2>The Arrival of <code>www</code> — 1989</h2>\n<p>In 1989, Tim (the person on the cover) proposed the concept of the World Wide Web at CERN. The point was not to lay down a website prefix and call on everyone to use <code>www</code> as the domain for web service; it was to propose the URL/HTTP/HTML hypertext system, so that documents could link to each other across machines and be published across organizations.</p>\n<p>For instance, CERN's first web server used <a href=\"info.cern.ch\">info.cern.ch</a>, not <a href=\"http://www.cern.ch\">www.cern.ch</a>.</p>\n<p>By the end of 1990, Tim had successfully launched the first web server and browser at CERN, proving that the ideas behind the proposal were workable. He wrote the relevant code directly on a NeXT computer and kept that server running in his office. To stop anyone from accidentally shutting the server down, he stuck a label written in red ink onto the computer: “This machine is a server. DO NOT POWER IT DOWN!”</p>\n<p><img src=\"https://img.markxu.icu/img20261001142450998.png\" alt=\"This machine is a server. DO NOT POWER IT DOWN!\"></p>\n<p>But when the Web joined an existing network as a new service, administrators usually gave it its own hostname. ftp/mail/news and the like were already named after their services, so the Web carried on the pattern with www.</p>\n<h2>A Little of What Came After — 1990s</h2>\n<p>The popularity of the www naming may also have had something to do with the release of the Mosaic browser. In 1993, NCSA released Mosaic, the first browser that let the general public look at images and click links. NCSA's own web server was called <a href=\"http://www.ncsa.uiuc.edu\">www.ncsa.uiuc.edu</a>. The bookmarks and sample links built into Mosaic all pointed at that address, so the first thing users around the world saw when they encountered the Web was www.</p>\n<p><img src=\"https://img.markxu.icu/img20261001150413894.png\" alt=\"The Mosaic browser\"></p>\n<p>Then came Yahoo (1994), Amazon (1994), and eBay (1995), the first wave of commercial internet companies, and their new URLs followed the <code>www</code> prefix as well. That is how it became familiar to the general public.</p>\n<h2>Why <code>www</code>?</h2>\n<p>So why, out of all these names, <code>www</code>?</p>\n<p>Someone once <a href=\"https://www.w3.org/Provider/Style/www.html\">emailed Tim to ask exactly this</a>; you can click the link to see Tim's explanation.</p>\n<p>His explanation is quite interesting:</p>\n<ol>\n<li>www was something he “suggested,” not something he “mandated.”</li>\n</ol>\n<blockquote>\n<p>“It is a convention. I suggested it early on, in this guide...”</p>\n</blockquote>\n<p>He suggested it in a Style Guide (a guide written for webmasters) — that is, he wrote an article saying “you could do it this way.”</p>\n<ol start=\"2\">\n<li>Why not use the machine's own name?</li>\n</ol>\n<blockquote>\n<p>“An alias was a better alternative to '<a href=\"http://pegasus.foo.com\">pegasus.foo.com</a>' which typically resulted when someone who happened to have a machine called pegasus started to run a web server...”</p>\n</blockquote>\n<p>What he means is: in the early days, plenty of people just grabbed whatever machine was at hand — one called pegasus, say — and ran the Web on it, so visitors had to use <a href=\"http://pegasus.foo.com\">pegasus.foo.com</a>. That name meant nothing; users could not remember it, and they could not tell what it was for.</p>\n<p>So he suggested giving it an alias, www, so that one glance told you it was a web server.</p>\n<ol start=\"3\">\n<li>Why the three letters “www”?</li>\n</ol>\n<p>Because the existing convention was already to name hosts after their service abbreviation:<br>\nHe was simply fitting the Web into that existing naming scheme. www was not chosen because it was special, but because it followed the same pattern as ftp and mail.</p>\n<ol start=\"4\">\n<li>Why not just use the bare domain <a href=\"http://foo.com\">foo.com</a>?</li>\n</ol>\n<p>He gives two reasons:</p>\n<blockquote>\n<p>“Typically early webmasters could not have comandeered the '<a href=\"http://foo.com\">foo.com</a>' address itself.”</p>\n</blockquote>\n<p>Meaning: the bare domain <a href=\"http://foo.com\">foo.com</a> is the “root identity” of the domain, usually controlled by the sysadmin, and the early Web people (webmasters) had no permission to change the DNS records of the bare domain. All they could do was add a subdomain within the area they could control.</p>\n<blockquote>\n<p>“You don't have this flexibility of configuration if you point everyone at '<a href=\"http://foo.com\">foo.com</a>' itself for all services.”</p>\n</blockquote>\n<p>The other reason is flexibility: with a www alias, if the web service grew later on, you could easily point www at a bigger machine without affecting the bare domain or the other services.</p>\n<h3>One Entertaining Detail</h3>\n<p>At the end of his answer, Tim says he is “of two minds about it now.”</p>\n<p>Because by 1999, the Web had become the biggest service for many companies, and he thought maybe it was time to let the Web claim the bare domain. But he stresses at the same time: you can only pick one, you cannot run both as the main site, or your cache efficiency gets cut in half.</p>\n<p>That is also in line with the idea in our next post, about why today's networks no longer use <code>www</code> and can just link to the bare domain instead.</p>\n",
      "summary": "[AI-translated] I always assumed the www in the address bar was just something the browser hid from us. The more I looked into it, the more I found that it connects to the division of services on the early Internet, the constraints of DNS, and the way websites are deployed today.",
      "date_published": "2026-09-26T00:00:00.000Z",
      "image": "https://img.markxu.icu/img20260926225709745.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/FingerBlock-A-game-Demo-by-AI-en",
      "url": "https://markxu.icu/blog/FingerBlock-A-game-Demo-by-AI-en",
      "title": "FingerBlock: A Game Demo Built with AI",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. A while back, I happened to notice that a hand-tracking library I had starred on GitHub a long time ago had shipped an update. I have long since forgotten why I starred it — probably because I had just heard about hand tracking, thought it sounded cool, and hit the star button. I had never actually looked at it, or even read any of its code. But this chance glance got me thinking: so many years have passed, AI has come this far, hand tracking must be pretty impressive by now too. So I went to read up on it, found Google's MediaPipe, and quickly thought back to the motion games I played on the Xbox 360 as a kid. If I could build a game with this, it would be pretty neat. It was just a passing thought, but it kept coming back to me before I fell asleep. Then a concept for a rhythm game in the vein of Beat Saber slipped down the chimney of my mind like Santa Claus and left this gift behind: you face your laptop's camera, blocks in the game slide along tracks toward a judgment grid, and you hit them with your fingers. The controls are simple and easy to grasp, and it seemed reasonably easy to build, too. So I turned to AI, describing the requirements in my spare moments between practice problems, and five or six conversations later the first version existed. The AI overlord's power really does get more terrifying by the day. The project code is already on GitHub: MarkXuJQ/finger-block. This article records how I settled on the game's basic design, which technologies I picked, and a bit of the optimization work. The Basic Technical Structure of the Project FingerBlock is a browser-side Vite + React project, since I am fairly familiar with that workflow. Rendering uses React Three Fiber and Three.js, which I used to imitate a bit of Beat Saber's sense of space. Interface icons come from Lucide, simple and efficient. Next, on to the core of the game: Hand Tracking: MediaPipe Hand tracking uses the Hand Landmarker from Google's MediaPipe Tasks Vision. It provides hand keypoint detection, and I used it to turn the detection results into the input FingerBlock needs. The flow in goes roughly like this: After the camera starts, the MediaPipe WASM files and the hand model are loaded on demand. It tries the GPU delegate first and falls back to the CPU delegate on failure. It tracks at most two hands and reads the index fingertip keypoint of each hand, which is the eighth landmark. The camera feed shown to the player is mirrored, so the code swaps the left and right hand labels to keep physical left/right consistent with on-screen left/right. converts video coordinates into viewport coordinates, and maps them onto the 3×3 or 4×4 judgment grid. The coordinates get simple smoothing to reduce the jitter from frame-by-frame camera detection. At that point I figured that since I already had a way to take input from hand gestures through the camera, I might as well add touch control too, so the game could be played on phones as well. So I had AI fill in that feature too. It is worth mentioning, though, that on phones and tablets there is often noticeable lag because performance is not quite sufficient, so this part could probably be improved gradually later. Here is the general idea: The camera is not the only input method. The project later added a mode, where the mouse and touch can both operate directly inside the judgment area. Touch uses and pointer capture to track multiple fingers, and even when no new fires, a continuous presence loop keeps a Hold alive. That way, even after the browser has been granted camera permission, the player can still switch to touch and finish a round. Audio Analysis: Moving Audio Processing into a Worker From a Fixed Chart to Automatic Generation After finishing the hand-tracking feature, I had AI build a small sample with one fixed chart first, to test whether my idea was workable. The result was quite good: it caught my index finger position nicely, and the detection was reasonably accurate. Once the fixed chart worked end to end, the content production problem showed up. I do not own the rights to any music, and I do not have the ability or the time to hand-craft charts for a large number of songs. Doing it like other rhythm games, where someone has to manually mark timestamps, positions, and block types, would make scaling the game very expensive. But playing with just a fixed chart and no music at all would be pretty boring. Then I thought of the Steam game Dead as Disco . I have never actually played it, but I had seen gameplay videos of it. It made me realize that I could try doing what that game does: build a set of music analysis, then generate charts automatically from the analysis results, and let players upload whatever music they like. Would that not be lovely? The Implementation Music import supports whatever the browser can decode: MP3, WAV, M4A/MP4, and OGG, with a limit of 80 MB per file and 12 minutes of duration. That should be enough for the vast majority of music — and anyway, playing a rhythm game alone for 12 minutes is tiring enough. The file is then decoded in the current browser through the Web Audio API's . The processing in is more complete than simply reading the BPM: First mix multiple channels down to mono and resample everything to 22,050 Hz; Compute overall RMS energy with a frame of roughly 50 milliseconds; Estimate low-frequency energy from the result of a 180 Hz low-pass; Generate a normalized energy curve and a waveform preview over time windows; Find transients from the energy difference between the current frame and the preceding few frames — the moments that likely correspond to drum hits or clear attacks; Analyze BPM and beat candidates with the Beatroot algorithm from ; Split the song into three energy sections: quiet, normal, and peak; Compute a confidence score from the beat match rate and transient coverage. The music-tempo I used is a lightweight MIT-licensed BPM/beat analysis library. It is well suited to producing quick candidate results inside a browser Worker, but it cannot possibly get everything right for music with a weak beat, free tempo, or long stretches of silence, so the interface keeps the ability to manually adjust the BPM, the first beat, and the playback speed 0.5×, 1×, or 2× . In most cases, of course, it still locks onto most of the rhythm. Automatic Chart Generation: Rule Constraints uses a pseudo-random number generator with a fixed seed. The seed is derived from the song identifier, grid size, difficulty, BPM, and first beat together, so the same piece of music under the same set of settings will reliably produce the same chart, and only after you change the parameters do you get a new result. Maybe that buys a certain stability. The generator reads beats, energy, and transients from the audio analysis results, then uses the easy, normal, and hard configurations to decide the sampling step, density, chord probability, Hold probability, and maximum travel distance. Block colors are tied to musical features as well: downbeats or strong transients produce red blocks, ordinary blocks are blue, and some low-intensity off-beat events are marked as yellow optional blocks. This keeps the game playable while also reinforcing the music's rhythm visually. But the genuinely tricky part is that “random content still has to be playable with two hands.” A person has at most two index fingers, and wildly random blocks are certainly not something a human can clear, so I added several kinds of constraints to the project. At Most Two Fingers Occupied at Once First, the two index fingers are treated as the maximum number of inputs available at once, and the generator uses to constrain the number of blocks within overlapping judgment windows. Even if two blocks do not share the exact same timestamp, as long as they fall in the same hit window they are treated as competing for the same two fingers. Double hits are allowed on high-energy downbeats, but the generator also checks whether there are already enough blocks nearby, to avoid accidentally creating a third target that needs handling at the same time. No Holds in the Center Cells Tracks that approach from a distance in 3D make the central area an important visual reference. If a long-lasting block sits in the middle, it can cover the depth outline and also get in the way of the player watching the blocks that follow. So first defines a single center cell for the 3×3 grid and four center cells for the 4×4 grid, and rejects any Hold located in those cells during validation. This rule exists in both the generation stage and the final chart validation stage: the generator uses to avoid the center, while the validator prevents future hand-written or external charts from bypassing the generator and writing illegal Holds. That way the rule is not just a convention people “hope to follow” but a constraint enforced at the data layer. A Hold Restricts the Other Hand A Hold is not a long block that is judged only once at its start. After hitting the start, the player has to keep the same hand inside the corresponding cell; the project scores the sustain every 0.25 seconds and allows a brief tracking grace period of about 0.18 seconds. If the hand is released partway through, the points already earned are kept, and no extra Miss is recorded. Because one hand is occupied by the Hold, the other hand has less room to move. The generator therefore: Only generates Holds in non-quiet sections where the duration is long enough and the energy is not low; Reduces the free hand's generation density to a fraction of the normal state during a Hold; Places the free hand's notes on whole beats where possible and reserves longer beat gaps; Limits the free hand's travel distance and prefers the opposite side of the grid from where the Hold is; Leaves a recovery gap after the Hold ends, then gradually restores travel distance and density. On normal difficulty a Hold lasts about two beats; easy is longer, and hard is shorter but denser. Double Holds appear only with low probability in sufficiently long melodic sections, and they are placed on opposite sides of the grid so each hand has a clear target. also prevents new notes from being generated in the same cell before the Hold ends. The Chart Still Has to Be Validated Everything generated goes through : it checks the version, grid size, difficulty, song information, note ordering, note IDs, whether color and role match, whether Hold durations are valid, and whether notes in the same cell overlap. The generator is responsible for creating content, and the validator is responsible for making sure the content honors the data contract, which makes problems easier to spot when the rules are changed later. Game Sound Effects: Building a Drum Kit from a Few Samples In the age of AI, implementing the logic has become easy, but assets are still a hard problem. Fortunately there is a huge amount of CC0 open-source material available online. Since most music can sit on top of a kick drum and music that cannot take a drum is not really suited to a rhythm game anyway 😄 , it was natural to use drum kit sounds for the hit effects. Every sound source used is recorded in along with its origin, license, date obtained, processing applied, and SHA-256 checksum. The current hit sounds come from five recordings on Freesound marked CC0: KEVOY's side stick and rimshot, TheEndOfACycle's closed hat and sub kick, and quatricise's shaker. The project used FFmpeg to trim them, fade them out, limit peaks, convert the sample rate, and encode them as Vorbis OGG; is a low-passed, low-gain low thud. At runtime the six OGG files are not treated as six unrelated cues but layered like a drum kit: A normal Hit plays the side stick; Good adds a light rim on top of it; Perfect layers in the closed hat as well; Accent stacks an extra low kick; Bonus adds the shaker; Miss uses the separate low thud. This design gives the different judgments a shared tonal anchor while still distinguishing feedback through the number of layers and the timbre. The music channel and the effects channel also each go through a GainNode on the AudioContext, so hit sounds do not drown out the music the user imported. Final Tweaks and a Bit of Simple Design Once the core logic worked, the remaining work was mostly repeated playtesting and fine-tuning: block movement speed, the sense of depth on the tracks, the position of the judgment grid, the smoothness of the camera cursor, the flash and shards on a hit, the score and combo on the HUD, the density at each difficulty, and the relative volume of music and effects. Clock, Judgment, and Feedback At runtime the game does not use a plain as its only clock. Music playback uses Web Audio's and , records the start time and pause position within the audio context, and the game logic then computes block positions and judgments from that music timeline. The judgment window for ordinary blocks is ±0.26 seconds: within ±0.07 seconds is Perfect, within ±0.15 seconds is Good, and anything else still inside the overall window is Hit. The base scores are 1000, 650, and 350 respectively, and red Accent blocks get a 1.2× base score bonus. Yellow Bonus blocks are not quite the same as ordinary notes you can miss; they play the role of an optional reward. Hold sustain scoring is also written as a deterministic function: every 0.25 seconds it only consumes complete scoring intervals and clamps time at the end of the Hold. Even if the browser stalls for a few hundred milliseconds on some frame, you will not get a different sustain score just because of a different number of animation frames. Hit feedback is generated in one place by , and the Three.js scene plays the flashes, expanding rings, wireframes, and shard effects that strengthen the sense of impact; the results page also plots a cumulative accuracy curve and the timing of Misses. The project also freezes the music clock and the Hold compensation logic when the page is hidden, so that after switching to the background and coming back, blocks do not all skip past the judgment area because the page was paused. Of course, this part is pretty rough, because I feel the idea has largely been realized, and unless something special comes up I probably will not keep optimizing it. Final Thoughts A Playable Demo in a Day and a Half From seeing the hand-tracking library update to finishing a runnable FingerBlock demo, the whole process took about a day and a half. And I was not sitting in front of the computer writing code the entire time: I spent a good chunk of it doing practice problems and watching lectures, and used my lunch break to hunt for open-source libraries and assets, study audio analysis methods, and keep playtesting and adjusting the generation rules. To be fair, AI really did speed up getting this idea off the ground by a large margin. From scaffolding the project and tuning the audio-track parsing to stuffing in all those constraint rules, a lot of the tedious boilerplate now just requires me to think the requirements through clearly and hand them to AI, and it produces the whole thing in one go. All I have to do is check and verify. AI Is Powerful, but This Quick Experiment Was Not All AI I will not credit AI with the whole of this quick FingerBlock demo. As I wrote in an earlier post, When Open Source Is Only a Prompt: I Found a Ridiculous New Way to Open Source, a few good prompts, a clear tech stack, and well-chosen links to assets are the real key to getting the most out of AI and improving efficiency. AI can take on more and more of the concrete execution work, but that does not mean we should hand over our basic thinking and planning as well. Only by keeping those foundational skills can we judge faster whether an idea is feasible, find suitable open-source resources more accurately, and understand what the AI-generated code actually solved and what problems it left behind. Maybe that is the technical constant that has stayed unchanged from Web coding to today's vibe coding. September 23 Uh oh. I get the feeling that if I did this again now, AI could do more, and do it better. This is a message I saw in a group chat, and these games really are playable. Everything below was generated with a single-sentence prompt to Claude Opus 5.5 QQ Speed: CrossFire: Transport Ship: A Pelican Riding a Bicycle: September 25 Today while scrolling videos online I came across this website. It feels like it shares something with my own approach, which was a nice surprise. That said, it requires a little bit of music theory knowledge, and of course the play style is more free-form. And it is genuinely pretty cool. Video: Website:",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>A while back, I happened to notice that a hand-tracking library I had starred on GitHub a long time ago had shipped an update. I have long since forgotten why I starred it — probably because I had just heard about hand tracking, thought it sounded cool, and hit the star button. I had never actually looked at it, or even read any of its code.</p>\n<p>But this chance glance got me thinking: so many years have passed, AI has come this far, hand tracking must be pretty impressive by now too. So I went to read up on it, found Google's <a href=\"https://github.com/google-ai-edge/mediapipe\">MediaPipe</a>, and quickly thought back to the motion games I played on the Xbox 360 as a kid. If I could build a game with this, it would be pretty neat.</p>\n<p>It was just a passing thought, but it kept coming back to me before I fell asleep. Then a concept for a rhythm game in the vein of <em>Beat Saber</em> slipped down the chimney of my mind like Santa Claus and left this gift behind: you face your laptop's camera, blocks in the game slide along tracks toward a judgment grid, and you hit them with your fingers. The controls are simple and easy to grasp, and it seemed reasonably easy to build, too.</p>\n<p>So I turned to AI, describing the requirements in my spare moments between practice problems, and five or six conversations later the first version existed. (The AI overlord's power really does get more terrifying by the day.)</p>\n<p><WebsiteCard\n  url=\"https://finger-block.vercel.app/\"\n  title=\"FingerBlock\"\n  description=\"Click to open it and give it a try\"\n/></p>\n<p>The project code is already on <a href=\"https://github.com/MarkXuJQ/finger-block\">GitHub: MarkXuJQ/finger-block</a>. This article records how I settled on the game's basic design, which technologies I picked, and a bit of the optimization work.</p>\n<h2>The Basic Technical Structure of the Project</h2>\n<ul>\n<li>FingerBlock is a browser-side <a href=\"https://vite.dev/\">Vite</a> + <a href=\"https://react.dev/\">React</a> project, since I am fairly familiar with that workflow.</li>\n<li>Rendering uses <a href=\"https://github.com/pmndrs/react-three-fiber\">React Three Fiber</a> and <a href=\"https://threejs.org/\">Three.js</a>, which I used to imitate a bit of Beat Saber's sense of space.</li>\n<li>Interface icons come from <a href=\"https://lucide.dev/\">Lucide</a>, simple and efficient.</li>\n</ul>\n<p>Next, on to the core of the game:</p>\n<h2>Hand Tracking: MediaPipe</h2>\n<p>Hand tracking uses the Hand Landmarker from Google's <a href=\"https://github.com/google-ai-edge/mediapipe\">MediaPipe Tasks Vision</a>. It provides hand keypoint detection, and I used it to turn the detection results into the input FingerBlock needs.</p>\n<blockquote>\n<p>The flow in <code>useHandTracking.ts</code> goes roughly like this:</p>\n<ol>\n<li>After the camera starts, the MediaPipe WASM files and the hand model are loaded on demand.</li>\n<li>It tries the GPU delegate first and falls back to the CPU delegate on failure.</li>\n<li>It tracks at most two hands and reads the index fingertip keypoint of each hand, which is the eighth landmark.</li>\n<li>The camera feed shown to the player is mirrored, so the code swaps the left and right hand labels to keep physical left/right consistent with on-screen left/right.</li>\n<li><code>videoLandmarkToViewport</code> converts video coordinates into viewport coordinates, and <code>getLaneFromPoint</code> maps them onto the 3×3 or 4×4 judgment grid.</li>\n<li>The coordinates get simple smoothing to reduce the jitter from frame-by-frame camera detection.</li>\n</ol>\n</blockquote>\n<p>At that point I figured that since I already had a way to take input from hand gestures through the camera, I might as well add touch control too, so the game could be played on phones as well. So I had AI fill in that feature too. It is worth mentioning, though, that on phones and tablets there is often noticeable lag because performance is not quite sufficient, so this part could probably be improved gradually later. Here is the general idea:</p>\n<blockquote>\n<p>The camera is not the only input method. The project later added a <code>pointer</code> mode, where the mouse and touch can both operate directly inside the judgment area. Touch uses <code>pointerId</code> and pointer capture to track multiple fingers, and even when no new <code>pointermove</code> fires, a continuous presence loop keeps a Hold alive. That way, even after the browser has been granted camera permission, the player can still switch to touch and finish a round.</p>\n</blockquote>\n<h2>Audio Analysis: Moving Audio Processing into a Worker</h2>\n<h3>From a Fixed Chart to Automatic Generation</h3>\n<p>After finishing the hand-tracking feature, I had AI build a small sample with one fixed chart first, to test whether my idea was workable. The result was quite good: it caught my index finger position nicely, and the detection was reasonably accurate.</p>\n<p>Once the fixed chart worked end to end, the content production problem showed up.</p>\n<p>I do not own the rights to any music, and I do not have the ability or the time to hand-craft charts for a large number of songs. Doing it like other rhythm games, where someone has to manually mark timestamps, positions, and block types, would make scaling the game very expensive. But playing with just a fixed chart and no music at all would be pretty boring.</p>\n<p><img src=\"https://img.markxu.icu/img20260921125520686.png\" alt=\"Dead as Disco\"></p>\n<p>Then I thought of the Steam game <a href=\"https://store.steampowered.com/app/3404260/Dead_as_Disco/\"><em>Dead as Disco</em></a>. I have never actually played it, but I had seen gameplay videos of it. It made me realize that I could try doing what that game does: build a set of music analysis, then generate charts automatically from the analysis results, and let players upload whatever music they like. Would that not be lovely?</p>\n<h3>The Implementation</h3>\n<p>Music import supports whatever the browser can decode: MP3, WAV, M4A/MP4, and OGG, with a limit of 80 MB per file and 12 minutes of duration. That should be enough for the vast majority of music — and anyway, playing a rhythm game alone for 12 minutes is tiring enough. The file is then decoded in the current browser through the Web Audio API's <code>decodeAudioData()</code>.</p>\n<blockquote>\n<p>The processing in <code>analysis-core.ts</code> is more complete than simply reading the BPM:</p>\n<ul>\n<li>First mix multiple channels down to mono and resample everything to 22,050 Hz;</li>\n<li>Compute overall RMS energy with a frame of roughly 50 milliseconds;</li>\n<li>Estimate low-frequency energy from the result of a 180 Hz low-pass;</li>\n<li>Generate a normalized energy curve and a waveform preview over time windows;</li>\n<li>Find transients from the energy difference between the current frame and the preceding few frames — the moments that likely correspond to drum hits or clear attacks;</li>\n<li>Analyze BPM and beat candidates with the Beatroot algorithm from <code>music-tempo</code>;</li>\n<li>Split the song into three energy sections: quiet, normal, and peak;</li>\n<li>Compute a confidence score from the beat match rate and transient coverage.</li>\n</ul>\n</blockquote>\n<p>The <a href=\"https://github.com/killercrush/music-tempo\">music-tempo</a> I used is a lightweight MIT-licensed BPM/beat analysis library. It is well suited to producing quick candidate results inside a browser Worker, but it cannot possibly get everything right for music with a weak beat, free tempo, or long stretches of silence, so the interface keeps the ability to manually adjust the BPM, the first beat, and the playback speed (0.5×, 1×, or 2×). In most cases, of course, it still locks onto most of the rhythm.</p>\n<h2>Automatic Chart Generation: Rule Constraints</h2>\n<p><code>chart-generator.ts</code> uses a pseudo-random number generator with a fixed seed. The seed is derived from the song identifier, grid size, difficulty, BPM, and first beat together, so the same piece of music under the same set of settings will reliably produce the same chart, and only after you change the parameters do you get a new result. Maybe that buys a certain stability.</p>\n<p>The generator reads beats, energy, and transients from the audio analysis results, then uses the easy, normal, and hard configurations to decide the sampling step, density, chord probability, Hold probability, and maximum travel distance.</p>\n<p>Block colors are tied to musical features as well: downbeats or strong transients produce red <code>accent</code> blocks, ordinary blocks are blue, and some low-intensity off-beat events are marked as yellow optional <code>bonus</code> blocks. This keeps the game playable while also reinforcing the music's rhythm visually.</p>\n<p>But the genuinely tricky part is that “random content still has to be playable with two hands.” A person has at most two index fingers, and wildly random blocks are certainly not something a human can clear, so I added several kinds of constraints to the project.</p>\n<h3>At Most Two Fingers Occupied at Once</h3>\n<p>First, the two index fingers are treated as the maximum number of inputs available at once, and the generator uses <code>MAX_SIMULTANEOUS_NOTES = 2</code> to constrain the number of blocks within overlapping judgment windows. Even if two blocks do not share the exact same timestamp, as long as they fall in the same hit window they are treated as competing for the same two fingers.</p>\n<p>Double hits are allowed on high-energy downbeats, but the generator also checks whether there are already enough blocks nearby, to avoid accidentally creating a third target that needs handling at the same time.</p>\n<h3>No Holds in the Center Cells</h3>\n<p>Tracks that approach from a distance in 3D make the central area an important visual reference. If a long-lasting block sits in the middle, it can cover the depth outline and also get in the way of the player watching the blocks that follow. So <code>lane-rules.ts</code> first defines a single center cell for the 3×3 grid and four center cells for the 4×4 grid, and <code>beatmap.ts</code> rejects any Hold located in those cells during validation.</p>\n<p>This rule exists in both the generation stage and the final chart validation stage: the generator uses <code>excludeCenter</code> to avoid the center, while the validator prevents future hand-written or external charts from bypassing the generator and writing illegal Holds. That way the rule is not just a convention people “hope to follow” but a constraint enforced at the data layer.</p>\n<h3>A Hold Restricts the Other Hand</h3>\n<p>A Hold is not a long block that is judged only once at its start. After hitting the start, the player has to keep the same hand inside the corresponding cell; the project scores the sustain every 0.25 seconds and allows a brief tracking grace period of about 0.18 seconds. If the hand is released partway through, the points already earned are kept, and no extra Miss is recorded.</p>\n<blockquote>\n<p>Because one hand is occupied by the Hold, the other hand has less room to move. The generator therefore:</p>\n<ul>\n<li>Only generates Holds in non-quiet sections where the duration is long enough and the energy is not low;</li>\n<li>Reduces the free hand's generation density to a fraction of the normal state during a Hold;</li>\n<li>Places the free hand's notes on whole beats where possible and reserves longer beat gaps;</li>\n<li>Limits the free hand's travel distance and prefers the opposite side of the grid from where the Hold is;</li>\n<li>Leaves a recovery gap after the Hold ends, then gradually restores travel distance and density.</li>\n</ul>\n</blockquote>\n<p>On normal difficulty a Hold lasts about two beats; easy is longer, and hard is shorter but denser. Double Holds appear only with low probability in sufficiently long melodic sections, and they are placed on opposite sides of the grid so each hand has a clear target. <code>laneBlockedUntil</code> also prevents new notes from being generated in the same cell before the Hold ends.</p>\n<h3>The Chart Still Has to Be Validated</h3>\n<p>Everything generated goes through <code>validateBeatmap()</code>: it checks the version, grid size, difficulty, song information, note ordering, note IDs, whether color and role match, whether Hold durations are valid, and whether notes in the same cell overlap. The generator is responsible for creating content, and the validator is responsible for making sure the content honors the data contract, which makes problems easier to spot when the rules are changed later.</p>\n<h2>Game Sound Effects: Building a Drum Kit from a Few Samples</h2>\n<p>In the age of AI, implementing the logic has become easy, but assets are still a hard problem. Fortunately there is a huge amount of CC0 open-source material available online. Since most music can sit on top of a kick drum (and music that cannot take a drum is not really suited to a rhythm game anyway 😄), it was natural to use drum kit sounds for the hit effects. Every sound source used is recorded in <code>public/assets/ATTRIBUTIONS.md</code> along with its origin, license, date obtained, processing applied, and SHA-256 checksum.</p>\n<blockquote>\n<p>The current hit sounds come from five recordings on <a href=\"https://freesound.org/\">Freesound</a> marked CC0: KEVOY's side stick and rimshot, TheEndOfACycle's closed hat and sub kick, and quatricise's shaker. The project used FFmpeg to trim them, fade them out, limit peaks, convert the sample rate, and encode them as Vorbis OGG; <code>miss.ogg</code> is a low-passed, low-gain low thud.</p>\n<p>At runtime the six OGG files are not treated as six unrelated cues but layered like a drum kit:</p>\n<ul>\n<li>A normal Hit plays the side stick;</li>\n<li>Good adds a light rim on top of it;</li>\n<li>Perfect layers in the closed hat as well;</li>\n<li>Accent stacks an extra low kick;</li>\n<li>Bonus adds the shaker;</li>\n<li>Miss uses the separate low thud.</li>\n</ul>\n</blockquote>\n<p>This design gives the different judgments a shared tonal anchor while still distinguishing feedback through the number of layers and the timbre. The music channel and the effects channel also each go through a GainNode on the AudioContext, so hit sounds do not drown out the music the user imported.</p>\n<h2>Final Tweaks and a Bit of Simple Design</h2>\n<p>Once the core logic worked, the remaining work was mostly repeated playtesting and fine-tuning: block movement speed, the sense of depth on the tracks, the position of the judgment grid, the smoothness of the camera cursor, the flash and shards on a hit, the score and combo on the HUD, the density at each difficulty, and the relative volume of music and effects.</p>\n<h3>Clock, Judgment, and Feedback</h3>\n<p>At runtime the game does not use a plain <code>setInterval</code> as its only clock. Music playback uses Web Audio's <code>AudioContext</code> and <code>AudioBufferSourceNode</code>, <code>MusicClock</code> records the start time and pause position within the audio context, and the game logic then computes block positions and judgments from that music timeline.</p>\n<blockquote>\n<p>The judgment window for ordinary blocks is ±0.26 seconds: within ±0.07 seconds is Perfect, within ±0.15 seconds is Good, and anything else still inside the overall window is Hit. The base scores are 1000, 650, and 350 respectively, and red Accent blocks get a 1.2× base score bonus. Yellow Bonus blocks are not quite the same as ordinary notes you can miss; they play the role of an optional reward.</p>\n<p>Hold sustain scoring is also written as a deterministic function: every 0.25 seconds it only consumes complete scoring intervals and clamps time at the end of the Hold. Even if the browser stalls for a few hundred milliseconds on some frame, you will not get a different sustain score just because of a different number of animation frames.</p>\n</blockquote>\n<p>Hit feedback is generated in one place by <code>judgement-events.ts</code>, and the Three.js scene plays the flashes, expanding rings, wireframes, and shard effects that strengthen the sense of impact; the results page also plots a cumulative accuracy curve and the timing of Misses. The project also freezes the music clock and the Hold compensation logic when the page is hidden, so that after switching to the background and coming back, blocks do not all skip past the judgment area because the page was paused.</p>\n<p>Of course, this part is pretty rough, because I feel the idea has largely been realized, and unless something special comes up I probably will not keep optimizing it.</p>\n<h2>Final Thoughts</h2>\n<h3>A Playable Demo in a Day and a Half</h3>\n<p>From seeing the hand-tracking library update to finishing a runnable FingerBlock demo, the whole process took about a day and a half. And I was not sitting in front of the computer writing code the entire time: I spent a good chunk of it doing practice problems and watching lectures, and used my lunch break to hunt for open-source libraries and assets, study audio analysis methods, and keep playtesting and adjusting the generation rules.</p>\n<p>To be fair, AI really did speed up getting this idea off the ground by a large margin. From scaffolding the project and tuning the audio-track parsing to stuffing in all those constraint rules, a lot of the tedious boilerplate now just requires me to think the requirements through clearly and hand them to AI, and it produces the whole thing in one go. All I have to do is check and verify.</p>\n<h3>AI Is Powerful, but This Quick Experiment Was Not All AI</h3>\n<p>I will not credit AI with the whole of this quick FingerBlock demo.</p>\n<p>As I wrote in an earlier post, <a href=\"https://markxu.icu/blog/prompt-only-open-source-cn\">When Open Source Is Only a Prompt: I Found a Ridiculous New Way to Open Source</a>, a few good prompts, a clear tech stack, and well-chosen links to assets are the real key to getting the most out of AI and improving efficiency.</p>\n<p>AI can take on more and more of the concrete execution work, but that does not mean we should hand over our basic thinking and planning as well. Only by keeping those foundational skills can we judge faster whether an idea is feasible, find suitable open-source resources more accurately, and understand what the AI-generated code actually solved and what problems it left behind. <strong>Maybe that is the technical constant that has stayed unchanged from Web coding to today's vibe coding.</strong></p>\n<hr>\n<p>September 23</p>\n<p>Uh oh. I get the feeling that if I did this again now, AI could do more, and do it better. This is a message I saw in a group chat, and these games really are playable.</p>\n<blockquote>\n<p>Everything below was generated with a single-sentence prompt to Claude Opus 5.5</p>\n<p>QQ Speed: <a href=\"https://lf3-static.bytednsdoc.com/obj/eden-cn/nulojnulwlo/qqfeiche3d/index.html\">https://lf3-static.bytednsdoc.com/obj/eden-cn/nulojnulwlo/qqfeiche3d/index.html</a></p>\n<p>CrossFire: Transport Ship: <a href=\"https://lf3-static.bytednsdoc.com/obj/eden-cn/nulojnulwlo/cf-transport-ship/transport-ship.html\">https://lf3-static.bytednsdoc.com/obj/eden-cn/nulojnulwlo/cf-transport-ship/transport-ship.html</a></p>\n<p>A Pelican Riding a Bicycle: <a href=\"https://lf3-static.bytednsdoc.com/obj/eden-cn/nulojnulwlo/pelican-bike/index.html\">https://lf3-static.bytednsdoc.com/obj/eden-cn/nulojnulwlo/pelican-bike/index.html</a></p>\n</blockquote>\n<hr>\n<p>September 25</p>\n<p>Today while scrolling videos online I came across this website. It feels like it shares something with my own approach, which was a nice surprise. That said, it requires a little bit of music theory knowledge, and of course the play style is more free-form. And it is genuinely pretty cool.</p>\n<p>Video:</p>\n<p><VideoEmbed\n  url=\"https://www.bilibili.com/video/BV16WYZ6mEb3/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click&vd_source=a1d2d12b1c9f3d1bf63ac9d24e2ff565\"\n  title=\"yesterday performance video\"\n/></p>\n<p>Website:<br>\n<WebsiteCard\n  url=\"https://gesture-synth-weld.vercel.app\"\n  title=\"gesture-synth-weld\"\n  description=\"A website I found while scrolling videos\"\n/></p>\n",
      "summary": "[AI-translated] A chance update notice from a hand-tracking library gave me the idea, and a day and a half later I had a simple rhythm game demo.",
      "date_published": "2026-09-19T00:00:00.000Z",
      "date_modified": "2026-09-26T00:00:00.000Z",
      "image": "https://markxu.icu/images/fingerblock-preview.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/rss-feed-beautification-en",
      "url": "https://markxu.icu/blog/rss-feed-beautification-en",
      "title": "The Story of Beautifying My RSS Feed Page",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. TLDR: Back in March, while I was building this site piece by piece, I got to the RSS subscription part and added a styling scheme to the XML file. I was quite pleased with it. Then, just two days later, the page that had been rendering just fine was suddenly down to nothing but the XML document tree again. Later I asked about it in Zhilu's blog community group chat, and Zhilu pointed me to Mayx's “XML Beautification After Google Killed XSLT”. After reading it, I rethought how RSS, Atom, and browser rendering relate to one another, and that is how the current version came to be. Why Beautify RSS at All Every blog should have a subscription entry point. Blog posts usually live on a personal site. You can of course read a web page at your own pace, but if someone wants to follow what you publish over the long term, they have to open your site every so often. If the reader uses an RSS reader, though, new posts appear in their subscription list automatically, and there is no need to open the site every day just to check for updates. Sounds great, does it not? But for someone who is not familiar with feeds, opening one and seeing a bare XML file would probably leave them baffled. So I figured: since a feed is meant for people to look at, I might as well make it look like a page while I am at it. And as for which of the two images below is the more intuitive one, that needs no explanation. The First Version This happened in early March, around the time the site first went live—the same stretch when I had just started adding subscription features. Looking back now, the Git history records it pretty clearly: February 28: added the feed dependency package and created the script generate-rss.js to automatically generate RSS, Atom, and JSON Feed files from the posts under the content/posts directory March 1: hooked up both RSS and Atom first, so posts could automatically produce feed files. Evening of March 1: added XSLT to the RSS feed for the first time. March 2: tried using to convert post content into HTML, and added cover images, summaries, and subscription links along the way. Afternoon of March 2: unified the feeds on Atom it felt like the easiest to implement and the most broadly compatible option, with JSON Feed as a possible follow-up added on 2026.10.4 , and moved the files into the directory. March 4: added a subscription page with cards, a background image, and a pop-up preview, so opening in a browser no longer gave you dry, bare XML. My thinking at the time was the same as most blogs that style their feeds: The script reads the posts under and generates an Atom file; the Atom file references at the top; after the browser receives the XML, XSLT transforms it into an HTML page with cover images, summaries, and dates. The result was simple and effective, so I merged it into the site quickly. I thought the whole thing was finally done. I was pretty happy the first time I saw it working. Click the RSS button below, open the feed link, and instead of a dense forest of XML you got a page that looked a lot like the style of my blog. And right about then, the plot twist I had coming arrived anyway. The Styling Scheme Suddenly Stops Working A few days later, I opened the feed link again, ready to admire my handiwork and see what else could be polished, only to find that the page I had built was gone. All that was left in the browser was the bare XML file. At first I assumed it was my own fault—maybe while I kept updating the site's content I had accidentally touched the script, or maybe, being up late, I had carelessly changed something in the script and broken the styled page. Then I wondered whether vibe-coding certain pages had caused Vercel to get the Atom MIME type wrong. Following my own experience and the possibilities the AI suggested, I checked the stylesheet path, the response headers, and the build output, and re-confirmed whether the XML file still had the instruction. Each piece looked right on its own, but put together, it just would not work. The styled page rendered in my local dev environment, but not in Chrome online. After that I adjusted it many times I even ran an ablation experiment , pushed several commits, and nothing worked 😭. That was when I decided to ask the veterans in the group chat. Help from the Veterans So I went to Zhilu's blog community group chat and asked. My description was simple: the styling on my RSS feed seems to have stopped working—do any of you know where the problem might be? In less than five minutes, Zhilu dropped a link to Mayx's article: “XML Beautification After Google Killed XSLT”, saying that Mayx had just written something about exactly this and I should take a look. That article answered my question outright: Chrome plans to deprecate XSLT. So the reason my styling suddenly stopped working was not me—it was that the browser no longer supported it after an update. Mayx's post explains the specifics far better than I could, so there is no need for me to belabor it. I will quote it here: From the Chrome deprecation document above, you can see that the person behind this is Mason Freed, who filed an Issue in the WHATWG. Because very few people use XSLT, and the libraries that implement it are old and prone to vulnerabilities, he proposed removing XSLT from the web standards. In that Issue, you can see that plenty of people object—after all, this feature is still very useful for bloggers who want to style their own feeds. To fight Google, someone even built a website: And although XSLT's share of users may not be high, the total number should still be fairly large. Beyond styling blog feeds with XSLT, some even use XSLT as a blog framework, and others pointed out that some government websites use XSLT too. But Freed seems to have been prepared for this. He built a Polyfill library that lets XSLT work properly through WASM. To make the library easier for everyone to use, I went ahead and sent a PR to CDNJS, so it can be referenced from a CDN from now on. The catch is that using the library requires adding a snippet of JS to the feed. My blog's Atom feed uses the jekyll-feed plugin, where the format is all hard-coded, so I could not use it… It is just that there is no way to stop Google now… and other browsers have said they will follow suit, so it seems the only thing we can do is adapt. And that post did more than explain the cause—it also proposed two alternative routes: pure CSS, and mixing XHTML into the XML. Pure CSS can arrange the tags into something nicer looking, but it cannot really manipulate the document content, and it is not easy to give posts proper hyperlinks; mixing in XHTML fills those gaps, but XML is still XML, its DOM behavior is not entirely the same as HTML's, and it also triggers warnings like in validators. After reading it, I figured: if XSLT is going to lose support in the end and the tide cannot be turned, there is nothing I can do about it. So I might as well write my own generation script and produce standalone HTML pages myself. How the Current Version Works The site now uses Atom as its primary feed format, with the feed addresses under the directory. The generation script handles a few things: Recursively read posts. Parse the language, slug, date, summary, and cover image. Generate Atom for each language. Also generate standalone static HTML pages, and copy the Atom, HTML, and XSL files into dist together. This approach may look a little inelegant, but at least it separates browser preview from RSS subscription. Even if every browser drops XSLT support in the future, static HTML pages like and will still be reachable, and RSS readers can still subscribe to the corresponding Atom XML. Of course, I did not delete the files entirely—I thought I would keep them around, since it was once an extremely efficient transformation method on the internet. You can also see what the XSL styling looks like here. A Few Reflections This bug hunt left a deep impression on me, and I am grateful for the veterans' generous sharing. It also shows that when a new feature changes and the AI has no relevant experience from its training, it cannot quickly pinpoint the cause of the bug. Maybe this is also a bit of proof that blogs still have some value in the AI era: only if people keep sharing on the internet can AI get enough information, and only then can it better help more people. At the same time, I regret the disappearance of XSLT as a technology. On a site made by a blogger I follow, you can see that many people are still holding on, wanting to preserve the tech stack they are used to. But which way the wheel of history will turn, we can only wait and see.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>TLDR:</p>\n<p>Back in March, while I was building this site piece by piece, I got to the RSS subscription part and added a styling scheme to the XML file. I was quite pleased with it. Then, just two days later, the page that had been rendering just fine was suddenly down to nothing but the XML document tree again. Later I asked about it in Zhilu's blog community group chat, and Zhilu pointed me to <a href=\"https://mayx.eu.org/2026/02/08/xslt.html\">Mayx's “XML Beautification After Google Killed XSLT”</a>. After reading it, I rethought how RSS, Atom, and browser rendering relate to one another, and that is how the current version came to be.</p>\n<h2>Why Beautify RSS at All</h2>\n<p><strong>Every blog should have a subscription entry point.</strong></p>\n<p>Blog posts usually live on a personal site. You can of course read a web page at your own pace, but if someone wants to follow what you publish over the long term, they have to open your site every so often. If the reader uses an RSS reader, though, new posts appear in their subscription list automatically, and there is no need to open the site every day just to check for updates.</p>\n<p>Sounds great, does it not?</p>\n<p>But for someone who is not familiar with feeds, opening one and seeing a bare XML file would probably leave them baffled. So I figured: since a feed is meant for people to look at, I might as well make it look like a page while I am at it.</p>\n<p>And as for which of the two images below is the more intuitive one, that needs no explanation.</p>\n<div class=\"img-grid-2\">\n  <img src=\"https://img.markxu.icu/imgSnipaste_2026-09-13_16-40-08.png\" alt=\"Before the makeover\" />\n  <img src=\"https://img.markxu.icu/imgSnipaste_2026-09-13_16-26-53.png\" alt=\"After the makeover\" />\n</div>\n<h2>The First Version</h2>\n<p>This happened in early March, around the time the site first went live—the same stretch when I had just started adding subscription features. Looking back now, the Git history records it pretty clearly:</p>\n<ul>\n<li>February 28: added the feed dependency package and created the script generate-rss.js to automatically generate RSS, Atom, and JSON Feed files from the posts under the content/posts directory</li>\n<li>March 1: hooked up both RSS and Atom first, so posts could automatically produce feed files.</li>\n<li>Evening of March 1: added XSLT to the RSS feed for the first time.</li>\n<li>March 2: tried using <code>markdown-it</code> to convert post content into HTML, and added cover images, summaries, and subscription links along the way.</li>\n<li>Afternoon of March 2: unified the feeds on Atom (it felt like the easiest to implement and the most broadly compatible option, with JSON Feed as a possible follow-up (added on 2026.10.4)), and moved the files into the <code>/feeds/</code> directory.</li>\n<li>March 4: added a subscription page with cards, a background image, and a pop-up preview, so opening <code>/feeds/</code> in a browser no longer gave you dry, bare XML.</li>\n</ul>\n<p>My thinking at the time was the same as most blogs that style their feeds:</p>\n<p>The script reads the posts under <code>content/posts</code> and generates an Atom file; the Atom file references <code>atom.xsl</code> at the top; after the browser receives the XML, XSLT transforms it into an HTML page with cover images, summaries, and dates.</p>\n<p>The result was simple and effective, so I merged it into the site quickly.</p>\n<p><strong>I thought the whole thing was finally done.</strong></p>\n<p>I was pretty happy the first time I saw it working. Click the RSS button below, open the feed link, and instead of a dense forest of XML you got a page that looked a lot like the style of my blog.</p>\n<p>And right about then, the plot twist I had coming arrived anyway.</p>\n<h3>The Styling Scheme Suddenly Stops Working</h3>\n<p>A few days later, I opened the feed link again, ready to admire my handiwork and see what else could be polished, only to find that the page I had built was gone.</p>\n<p>All that was left in the browser was the bare XML file. At first I assumed it was my own fault—maybe while I kept updating the site's content I had accidentally touched the script, or maybe, being up late, I had carelessly changed something in the script and broken the styled page. Then I wondered whether vibe-coding certain pages had caused Vercel to get the Atom MIME type wrong.</p>\n<p>Following my own experience and the possibilities the AI suggested, I checked the stylesheet path, the response headers, and the build output, and re-confirmed whether the XML file still had the <code>xml-stylesheet</code> instruction. Each piece looked right on its own, but put together, it just would not work. The styled page rendered in my local dev environment, but not in Chrome online.</p>\n<p>After that I adjusted it many times (I even ran an ablation experiment), pushed several commits, and nothing worked 😭. That was when I decided to ask the veterans in the group chat.</p>\n<h2>Help from the Veterans</h2>\n<p>So I went to Zhilu's blog community group chat and asked. My description was simple: the styling on my RSS feed seems to have stopped working—do any of you know where the problem might be?</p>\n<p>In less than five minutes, Zhilu dropped a link to Mayx's article: <a href=\"https://mayx.eu.org/2026/02/08/xslt.html\">“XML Beautification After Google Killed XSLT”</a>, saying that Mayx had just written something about exactly this and I should take a look.</p>\n<p>That article answered my question outright: Chrome plans to deprecate XSLT. So the reason my styling suddenly stopped working was not me—it was that the browser no longer supported it after an update.</p>\n<p>Mayx's post explains the specifics far better than I could, so there is no need for me to belabor it. I will quote it here:</p>\n<blockquote>\n<p>From the Chrome deprecation document above, you can see that the person behind this is <a href=\"https://github.com/mfreed7\">Mason Freed</a>, who filed an <a href=\"https://github.com/whatwg/html/issues/11523\">Issue</a> in the WHATWG. Because very few people use XSLT, and the libraries that implement it are old and prone to vulnerabilities, he proposed removing XSLT from the web standards. In that Issue, you can see that plenty of people object—after all, this feature is still very useful for bloggers who want to style their own feeds. To fight Google, someone even built a website: <a href=\"https://xslt.rip/\">https://xslt.rip</a>.</p>\n<p>And although XSLT's share of users may not be high, the total number should still be fairly large. Beyond styling blog feeds with XSLT, <a href=\"https://github.com/vgr-land/vgr-xslt-blog-framework\">some even use XSLT as a blog framework</a>, and others pointed out that <a href=\"https://github.com/whatwg/html/issues/11582\">some government websites use XSLT too</a>.</p>\n<p>But Freed seems to have been prepared for this. He built a <a href=\"https://github.com/mfreed7/xslt_polyfill\">Polyfill library</a> that lets XSLT work properly through WASM. To make the library easier for everyone to use, I went ahead and sent a <a href=\"https://github.com/cdnjs/packages/pull/2118\">PR</a> to CDNJS, so it can be referenced from a CDN from now on. The catch is that using the library requires adding a snippet of JS to the feed. My blog's Atom feed uses the <a href=\"https://github.com/jekyll/jekyll-feed\">jekyll-feed</a> plugin, where the format is all hard-coded, so I could not use it…</p>\n<p>It is just that there is no way to stop Google now… and other browsers have said they will follow suit, so it seems the only thing we can do is adapt.</p>\n</blockquote>\n<p>And that post did more than explain the cause—it also proposed two alternative routes: pure CSS, and mixing XHTML into the XML.</p>\n<p>Pure CSS can arrange the tags into something nicer looking, but it cannot really manipulate the document content, and it is not easy to give posts proper hyperlinks; mixing in XHTML fills those gaps, but XML is still XML, its DOM behavior is not entirely the same as HTML's, and it also triggers warnings like <code>Misplaced XHTML content</code> in validators.</p>\n<p>After reading it, I figured: if XSLT is going to lose support in the end and the tide cannot be turned, there is nothing I can do about it. So I might as well write my own generation script and produce standalone HTML pages myself.</p>\n<h2>How the Current Version Works</h2>\n<p>The site now uses Atom as its primary feed format, with the feed addresses under the <code>/feeds/</code> directory. The generation script <code>generate-rss.js</code> handles a few things:</p>\n<ol>\n<li>Recursively read posts.</li>\n<li>Parse the language, slug, date, summary, and cover image.</li>\n<li>Generate Atom for each language.</li>\n<li>Also generate standalone static HTML pages, and copy the Atom, HTML, and XSL files into dist together.</li>\n</ol>\n<p>This approach may look a little inelegant, but at least it separates browser preview from RSS subscription. Even if every browser drops XSLT support in the future, static HTML pages like <code>/feeds/zh/</code> and <code>/feeds/en/</code> will still be reachable, and RSS readers can still subscribe to the corresponding Atom XML. Of course, I did not delete the <code>.xsl</code> files entirely—I thought I would keep them around, since it was once an extremely efficient transformation method on the internet.</p>\n<p>You can also see what the XSL styling looks like <a href=\"https://markxu.icu/feeds/zh/atom.xml\">here</a>.</p>\n<h2>A Few Reflections</h2>\n<p>This bug hunt left a deep impression on me, and I am grateful for the veterans' generous sharing. It also shows that when a new feature changes and the AI has no relevant experience from its training, it cannot quickly pinpoint the cause of the bug. Maybe this is also a bit of proof that blogs still have some value in the AI era: only if people keep sharing on the internet can AI get enough information, and only then can it better help more people.</p>\n<p>At the same time, I regret the disappearance of XSLT as a technology. On <a href=\"https://xslt.rip/\">https://xslt.rip</a>, a site made by a blogger I follow, you can see that many people are still holding on, wanting to preserve the tech stack they are used to. But which way the wheel of history will turn, we can only wait and see.</p>\n",
      "summary": "[AI-translated] A record of how the RSS feed on my personal site evolved: working, beautified, suddenly broken, and finally fixed.",
      "date_published": "2026-09-13T00:00:00.000Z",
      "image": "https://img.markxu.icu/img16cadbcd5c793.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/why-im-not-bullish-on-humanoid-robots-en",
      "url": "https://markxu.icu/blog/why-im-not-bullish-on-humanoid-robots-en",
      "title": "Why I Am Not Bullish on Humanoid Robots",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. TLDR: I do not deny the technical value of humanoid robots in control, hardware, and artificial intelligence, and I do not think the field will fail outright. What I am not bullish on is something else: “human-like” does not automatically mean “general-purpose,” and “general-purpose” does not automatically mean “useful.” Until the problems of reliability, cost, energy, data, and maintenance are solved, humanoid robots are more likely to become research platforms, industrial testbeds, performance props, and geek toys first — rather than spreading the way personal computers or smartphones did, much less becoming a “new quality workforce” that replaces blue-collar workers . The Future Is Already Here? Starting with “YangBOT” at the 2025 Spring Festival Gala, humanoid robots walked into the public eye once again. The nimble moves of Unitree's H1 robots made plenty of people feel that the future had already arrived, and the media of the moment collectively lost its mind on the spot, leaving everyone with the impression that the direction of future technology finally had a clear signpost again. Since then, on the one hand I have been amazed by how agile and how much fun humanoid robots are; on the other, I have been doubtful: is it really true, as these outlets claim, that humanoid robots are the future? Can embodied AI really be treated as roughly synonymous with humanoid robots? And the recent smooth running of the humanoid robot games brought the topic back to the center of public discussion. I think I should state my judgment and my position clearly right now, and put it on the record so I can look back later . If it succeeds, I want to see what I am overlooking today; if it fails, I want to see whether the reasons match my judgment. Defining the Scope First, let me set out the scope of the discussion. I am not rejecting humanoid robots on technical grounds. It of course brings together some of the most advanced work in control, mechanics, materials, sensors, and artificial intelligence. What I am not bullish on is this: that humanoid robots can shoulder what some people call “the next generation of general-purpose productivity.” The usual chain of reasoning — “human-like” = “general-purpose” = “practical” — is especially full of logical holes, and also of the stories capital likes to tell. Let me now walk through this step by step. The Technical Hurdles I want to start by trying to explain why domestic robots have progressed so fast in recent years. The Evolutionary Path The first robot to hold the public's attention online for any length of time was probably Boston Dynamics'. As early as 2004, when the internet was just taking off, the BigDog project was already underway. As YouTube grew, everyone marveled at what this mechanical dog could do. The control technology, whole-body dynamics modeling, trajectory optimization, and contact-force solving behind it — those names are a moat built over decades by the smartest engineers and expensive hardware together. Boston Dynamics' robots amazed people again and again, and the technology and algorithms behind them were no less astonishing. That story did not change until 2020. The RSL group at ETH Zurich published that famous ANYmal reinforcement-learning off-road paper in Science Robotics , showing a path of training policies in simulation and then transferring them to real robots to handle complex terrain. Training continuously in an RL simulation environment produced policies that ran more stably on complex terrain than Boston Dynamics' hand-tuned MPC. No hand-crafted models at all, and the code was open-sourced too. Later tools — , , plus NVIDIA Isaac Gym and others — kept lowering the bar for training and reproduction, while manufacturers like Unitree drove down the hardware price of electric-drive robots. The wall MPC spent decades building was easily walked around with RL. Capabilities that once only a handful of top teams could touch began to be reproduced by more labs and companies. This was in fact a remarkable technical breakthrough. RL flipped the table, using the most cutting-edge techniques to finish in a short time work that was previously beyond imagination: many motions that are hard to model fully by hand can first be learned through massive trial and error in simulation, then combined with conventional control, state estimation, and safety constraints. The once towering technical barrier was, in part, simply bypassed. After that, domestic companies had a cheaper, faster-iterating route to robot motion control, and all kinds of robot dogs and humanoid robots gradually began to appear on the market. The Challenges That Remain Let us go from the easier problems to the harder ones. First, energy. There are of course battery-swapping solutions already, and they are passable, but that also means a robot's range and working hours are still limited. For a robot that performs for ten minutes this is not fatal, but for a production tool that has to work eight hours straight while carrying a load, it goes straight to cost. Second, structure. Many humanoid robots today use motor-driven joints, and those joints have to handle torque, heat dissipation, weight, impact, and service life all at once, each pulling against the others. I do not know much about this area and do not dare go into specific designs, but I do know that demonstrating a motion once and repeating it thousands of times a day are held to completely different engineering standards. Whether it can get up on its own after a fall, whether precision drifts after continuous operation, how long it takes to swap out a broken joint — these small problems add up, and eventually they all show up on the bill. Finally, the brain. Reinforcement learning has advanced a robot's locomotion and balance a great deal, but that is closer to the “cerebellum”: how to place a foot, how to stay stable, how to execute an already defined motion. What really determines whether a robot can be widely used is whether it can understand its environment, plan tasks, handle surprises, and know when it cannot do something. That is also where several mainstream research directions come in — world models here is a good, simple introduction — though not a short one! , long-horizon task planning, and semantic understanding and exception handling in open environments. In fact, there is a fair amount of skepticism about how well these directions will ultimately pan out. Many companies have started collecting data, hoping to score a breakthrough through the scaling law the way large language models did. But real-world data, unlike text, cannot simply be downloaded from the internet. A segment of robot data requires real hardware to execute it, needs the actions and outcomes annotated, and may come with wear, collisions, and human takeovers. In other words, a language model can be trained “for free” on text from the internet, while every round of “learning” for a robot model wears down hardware and consumes electricity and human labor. That cost structure makes it hard to simply replicate the explosive trajectory of large language models. More troublesome still is whether data collected from different embodiments, different camera positions, and different joint parameters can transfer to one another. This resembles the problems I know from medical imaging — and may be even messier. Medical imaging still has no clean answer the way language models do. So the “brain” is not just a question of whether the model is smart enough; it is also a question of where the data comes from, who bears the cost of mistakes, and whether capabilities can be reproduced reliably. Who Is Actually Buying the Robots — Market and Application Space Some robot companies already have decent revenue, and Unitree's numbers show it turned a profit long ago, but that does not directly prove that humanoid robots have a large end market. We first need to look at who is buying these robots and what they are using them for. Judging from news reports and public cases, humanoid robots genuinely and steadily deployed in production are still very few; most buyers are universities, research institutes, showrooms, and companies that need to build on top of them. I often see universities publicizing that their lab has a Unitree robot, and I often see robots at product launches, shopping malls, and sports meets, but I rarely see a factory publicly show how long one has run continuously, how much cost it has replaced, or how much stable output it has generated. Morgan Stanley's data says that 65% go to display, reception, and research, with only 35% actually reaching a production line. The materials Unitree filed with the Shanghai Stock Exchange are even more blunt: in the first three quarters of 2025, 73.6% of humanoid robot revenue came from research and education. And of the ones that do reach a production line, how many are there to tell a story? That is not something we can verify for now. The robot here is more a paper-production tool, an object of capital narrative, and a direction for hype. I prefer to think of it as an expensive Lego model, or more aptly, as something like DJI's RoboMaster: an excellent platform for teaching, research, and development that can also give rise to competitions, performances, and enthusiast culture — but that is a different thing from becoming a general production tool. Of course, pilot projects are a necessary stage before mass production. I am not saying “there is none now, so there will be none later.” I only think that when we discuss the market, we cannot count every robot sent into a lab, a showroom, or a pilot line as already-validated productivity in advance. At this point someone will say: no market today does not mean the potential market is small. Every device in society today is designed around human ergonomics, so the only truly general-purpose robot in the future can be a humanoid one. But that logic does not close the loop. Human-Like ≠ General-Purpose ≠ Useful There are two faulty inferences here: first, human-like ≠ general-purpose; second, general-purpose ≠ useful. The human hand is certainly very dexterous, but it was not designed for the modern industrial environment. There are many explanations for how the hand's shape evolved — grasping, throwing, and tool use may all have played a part; see this paper on the relationship between throwing and the evolution of the human hand. But society today has developed far faster than biological evolution. We invent different tools to do different things precisely to make up for what the hand does poorly. This is itself an example of “general-purpose ≠ useful.” You can just about turn a screw with your fingers, but a screwdriver tightens it more; a ratcheting screwdriver tightens it fast and tight; and on a production line people go straight to a torque-controlled power tool. The human hand is general-purpose, but what actually raises productivity tends to be the tools that look not general-purpose at all. The evolution of these tools is all accumulated industrial knowledge — why can't it carry over to robots? A wheeled base moves more efficiently, a robotic arm positions more stably, a fixture can fit a part directly, and a conveyor belt does not even need to “understand” what it is transporting. When a scenario is fixed enough, companies usually prefer to modify the environment and the process rather than buy a complex machine with two legs, ten fingers, and whole-body balance. Even when a system genuinely needs to handle many tasks, it does not have to imitate a human completely. Modular end effectors, quickly swappable fixtures, a mobile base plus a robotic arm, and then letting humans — who are already smart enough — handle the few exceptions: that may be a cheaper and more reliable transition. The greatest paradox of the humanoid robot is that it tries to use the most general-purpose body to solve the least general-purpose tasks. The real advantage of a humanoid form is that it can walk straight into environments designed for people without first changing the doors, the stairs, and the tools. But that advantage carries a hidden precondition: the cost of remodeling the environment must be higher than the cost of maintaining the complexity of a humanoid form. In homes, disaster zones, or old buildings, that precondition may hold; in factories and warehouses that can be replanned, it may not. So Where Is the Application Space? In my view, the applications where a humanoid form is irreplaceable may not be the assembly line that gets advertised most, but the scenarios where being human-like is itself valuable. If we really can build a robot human-like enough in the future — one that feels, looks, and talks like a person — then of course it can become an NPC in a virtual theme park or a companion robot. At that point the humanoid form is not there to barely accommodate a wrench; it is the product experience itself. Ordered from easy to hard, I think the progression looks more like this: The boxing robots in Real Steel — sports events The home-care robot in A.I. — emotional companion robots The virtual-park NPCs in Westworld — entertainment facilities The general-purpose robots doing all kinds of basic work in Detroit: Become Human — replacements for blue-collar workers And we are still a long way from the state of things in science fiction. So the current market and level of technology only prove that “we can already build increasingly agile humanoid robots”; they do not directly prove that “the more a robot looks like a human, the closer it is to a correct business path.” Why Is Everyone Bullish on Humanoid Robots? Next I want to talk about a few points that come up in today's discussion of humanoid robots. How Does the Emotion Arise? First, movies and other science fiction have made people unconsciously hold a lot of rose-colored fantasies about humanoid robots. Compared with a robotic arm, a humanoid robot is easier to film, to spread, and to understand. It only needs to take a couple of steps, wave, and show an expression on its head, and the audience can empathize with it immediately. As with the application spaces listed above, it is not that humanoid robots have no future; it is that we often mistake the imagining that science fiction has already done on their behalf for capabilities that real products have already achieved. The economy is sluggish right now, and everyone is eager to find a breakthrough that can pull investment along. Our materialist education keeps stressing the importance of productivity and technological progress , so people are naturally willing to support advanced productivity they can see and touch, hoping that one technological leap will improve their current situation. The humanoid robot happens to satisfy exactly this expectation: it offers the imaginative promise of artificial intelligence, the visual impact of a physical machine, and a link to manufacturing and supply chains. Capital, local governments, the media, and startups all have incentives to keep telling this story. Capital needs a big enough potential market, localities need a new industrial calling card, the media needs eye-catching footage, and startups must first convince others that the future is big enough before they can get the money to keep developing today's immature products. These incentives do not necessarily mean a scam, but they naturally tilt the discussion toward its most optimistic version. Rebutting the Most Common Argument The most famous argument, and the easiest to rebut: no technology was smooth sailing at the start. Some cite the automobile as an example, others the airplane, the mobile phone, and the mobile internet. All of them met with skepticism at first and later genuinely transformed human life. But this is not the first time humanoid robots have appeared in the public eye. In the 1980s, Heathkit brought out the HERO 1, and Tomy released “home robots” like the Omnibot. They could move and make sounds, and some could pick things up with a simple arm. They carried the same dream of home automation at the time, yet later never became part of the household appliance lineup. People remember only the products that succeeded. Plenty of technologies were once favored and later failed to achieve mass adoption; people simply skip over them when arguing, landing squarely in survivorship bias . Examples include hydrogen airships, the Concorde supersonic airliner, 3D televisions, Google Glass, and the metaverse of a few years ago. Not every one of them was a total technical failure — some even have niche uses to this day — but none delivered on the promise, made at the peak of their hype, that they were about to change everyone's life. These examples each match some of the current features of humanoid robots: the demos are stunning, the technology is genuinely advanced, and real demand can be found, but cost, supporting infrastructure, reliability, or social acceptance kept them from expanding as originally imagined. So “people once misjudged the automobile” does not prove that questioning humanoid robots today is wrong. To complete that argument, at least a few more specific questions need answers: What problem does it solve that nothing else can? How much cheaper is it than a human or specialized equipment? How many tasks can it still complete outside a carefully staged demo environment? And who bears the cost of failures, maintenance, and safety? How It Might Develop But I am not pessimistic enough to think humanoid robots will amount to nothing. I suspect it will end up closer to blockchain, which was equally hot a few years ago. Blockchain is a classic case of “over-promised, under-delivered, but leaving behind some real capability,” and it broadly matches the development path I have in mind for robots, so I will use it as the example. At the peak of the ICO boom in 2017, many projects could raise large sums with nothing but a white paper, as if every industry should be on-chain; in 2018–2019, after the bubble burst and a large number of projects went to zero, blockchain was in turn denounced wholesale as a scam. Today the speculators have left, but the underlying technology really is still developing in a few areas, such as stablecoins and cross-border settlement. In other words, a bubble and a technology are not an either-or choice. A field can contain real technological progress, inflated commercial promises, and a flood of trend-chasing projects all at once. When the bubble recedes, what remains may not be the product from the original story; it may be training frameworks, low-cost joints, sensors, control algorithms, supply chains, and a cohort of engineers who genuinely understand robot systems. A body of genuinely useful technology accumulates, and then gets taken apart and installed elsewhere. My guess is that humanoid robots will go through something similar: During the hype, every company has to make a humanoid robot, and every product launch has to have one walk a few steps. Once capital starts demanding revenue and repeat purchases, a batch of companies that cannot find a stable scenario exits, and the industry enters a disillusionment phase. The companies that remain stop emphasizing “general-purpose” and move into a few specific scenarios, honestly solving for one motion, one process step, or one hazardous environment. Parts of humanoid technology get spun out into robotic arms, exoskeletons, wheeled robots, and other specialized equipment. Parts of the humanoid form and its sensing technology can assist people with disabilities, much like the relationship between rockets and MRI. If that is how it ends up, I will not consider humanoid robots a failure. What fails is the overinflated narrative that “it will soon be a general-purpose laborer in every home and every factory,” not the technology accumulated over these years. If I Am Not Bullish on Humanoid Robots, What Am I Bullish On? What I am genuinely bullish on is the AI models, perception, and reasoning behind humanoid robots. A model that can understand the real world, recognize the relationships between objects, grasp human intent, and predict what an action will lead to — that is what could become the truly general part of embodied AI. It does not have to be installed in a humanoid body; it can control robotic arms, drones, cars, and wheeled bases, or even exist purely as an assistive system for people. Compared with replicating the human body, I am more bullish on these directions: Specialized robots designed for clearly defined tasks, such as warehouse, cleaning, inspection, agricultural, and surgical robots Modular systems made of a mobile base, a robotic arm, and interchangeable end effectors Semi-autonomous systems where humans take over remotely and the model gradually learns, rather than pursuing full autonomy from the start Vision-language-action models that can transfer across embodiments, world models, and robot data infrastructure None of these is as spectacular as a complete humanoid robot doing flips on stage, but they are more likely to enter the real world first. Because what users are buying is not a “feeling of intelligence,” but having a task completed more cheaply, more reliably, and more safely. What Do I Think Will Happen to Humanoid Robots? I think humanoid robots will not spread especially widely, but they will become a new kind of toy and a social culture. As prices fall, more schools, labs, and hobbyists will develop their own motions, appearances, and AI models, and bring their robots to fighting, soccer, racing, and obstacle-course competitions. Like RoboMaster, drones, and the early personal computer, they may first form a creative enthusiast ecosystem. At the same time, they will also enter a small number of scenarios where the environment cannot be remodeled but a human form is needed. In those places, even an expensive, slow robot already has value if it keeps humans out of hazardous environments. Some robots will exist in the form of rentals, performances, marketing, and companion products. They may be a decent business — just not the business of “replacing all labor.” Of course, I will also leave my future self a few criteria by which I can be proven wrong. If future humanoid robots can: Work for hours on end in a home or factory without pre-scripting, and rarely need human takeover; Learn new tasks after moving to a new site without collecting a large amount of new data; Still be more cost-effective than a human or specialized equipment once purchase, energy, maintenance, downtime, and safety costs are all included; Go from a demo at a launch event to a standard product that can be delivered at volume, maintained reliably, and has clear accountability; then I will admit that my current judgment underestimated the transferability of general models and the speed at which mass production lowers the cost of complex hardware. By then, being “human-like” may truly be more than the easiest form factor to market; it may be the most economical way to fit into the human world. But until those things happen, I am still unwilling to treat a smooth dance routine, a robot soccer match, or a slickly edited factory video as evidence that the next generation of productivity has arrived. Humanoid robots are certainly part of the future, but the future never takes only the shape most familiar from science fiction. The technologies that truly change life are sometimes precisely the machines that no longer look human, no longer belong on stage, and have already been working quietly for years. Update, September 19, 2026: This article received two comments on Zhihu, both arguing that it commits the straw man fallacy — that I set up an easy target that cannot hit back, and then attacked it. Looking at it now, there is indeed some of that. You can click this card to jump over and take a look.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>TLDR: I do not deny the technical value of humanoid robots in control, hardware, and artificial intelligence, and I do not think the field will fail outright. What I am not bullish on is something else: <strong>“human-like” does not automatically mean “general-purpose,” and “general-purpose” does not automatically mean “useful.”</strong> Until the problems of reliability, cost, energy, data, and maintenance are solved, humanoid robots are more likely to become research platforms, industrial testbeds, performance props, and geek toys first — <strong>rather than spreading the way personal computers or smartphones did, much less becoming a “new quality workforce” that replaces blue-collar workers</strong>.</p>\n<h2>The Future Is Already Here?</h2>\n<p>Starting with “YangBOT” at the 2025 Spring Festival Gala, humanoid robots walked into the public eye once again. The nimble moves of Unitree's H1 robots made plenty of people feel that the future had already arrived, and the media of the moment collectively lost its mind on the spot, leaving everyone with the impression that the direction of future technology finally had a clear signpost again.</p>\n<p><img src=\"https://img.markxu.icu/img20260915194338349.png\" alt=\"“YangBOT” at the 2025 Spring Festival Gala\"></p>\n<p>Since then, on the one hand I have been amazed by how agile and how much fun humanoid robots are; on the other, I have been doubtful: is it really true, as these outlets claim, that humanoid robots are the future? Can embodied AI really be treated as roughly synonymous with humanoid robots? And the recent smooth running of the humanoid robot games brought the topic back to the center of public discussion.</p>\n<p>I think I should state my judgment and my position clearly right now, <strong>and put it on the record so I can look back later</strong>. If it succeeds, I want to see what I am overlooking today; if it fails, I want to see whether the reasons match my judgment.</p>\n<h2>Defining the Scope</h2>\n<p>First, let me set out the scope of the discussion.</p>\n<p>I am not rejecting humanoid robots on technical grounds. It of course brings together some of the most advanced work in control, mechanics, materials, sensors, and artificial intelligence. What I am not bullish on is this: that humanoid robots can shoulder what some people call “the next generation of general-purpose productivity.” The usual chain of reasoning — <strong>“human-like” = “general-purpose” = “practical”</strong> — is especially full of logical holes, and also of the stories capital likes to tell.</p>\n<p><img src=\"https://img.markxu.icu/imgbeedda696b2788.png\" alt=\"“Human-like” = “general-purpose” = “practical” does not hold up\"></p>\n<p>Let me now walk through this step by step.</p>\n<h2>The Technical Hurdles</h2>\n<p>I want to start by trying to explain why domestic robots have progressed so fast in recent years.</p>\n<h3>The Evolutionary Path</h3>\n<p>The first robot to hold the public's attention online for any length of time was probably Boston Dynamics'. As early as 2004, when the internet was just taking off, the BigDog project was already underway. As YouTube grew, everyone marveled at what this mechanical dog could do. The control technology, whole-body dynamics modeling, trajectory optimization, and contact-force solving behind it — those names are a moat built over decades by the smartest engineers and expensive hardware together. Boston Dynamics' robots amazed people again and again, and the technology and algorithms behind them were no less astonishing.</p>\n<p><img src=\"https://img.markxu.icu/img20260915194918828.png\" alt=\"BigDog\"></p>\n<p>That story did not change until 2020.</p>\n<p>The RSL group at ETH Zurich published that famous <a href=\"https://www.science.org/doi/10.1126/scirobotics.abc5986\">ANYmal reinforcement-learning off-road paper</a> in <em>Science Robotics</em>, showing a path of training policies in simulation and then transferring them to real robots to handle complex terrain.</p>\n<p><img src=\"https://img.markxu.icu/img20260915195616416.png\" alt=\"The figures are really beautiful\"></p>\n<p>Training continuously in an RL simulation environment produced policies that ran more stably on complex terrain than Boston Dynamics' hand-tuned MPC. No hand-crafted models at all, and the code was open-sourced too.</p>\n<p>Later tools — <code>rsl_rl</code>, <code>legged_gym</code>, plus NVIDIA Isaac Gym and others — kept lowering the bar for training and reproduction, while manufacturers like Unitree drove down the hardware price of electric-drive robots. The wall MPC spent decades building was easily walked around with RL. Capabilities that once only a handful of top teams could touch began to be reproduced by more labs and companies.</p>\n<p><img src=\"https://img.markxu.icu/imgjo2b13ed-fh26-4b7f-odr0-60q841b2-medium.webp\" alt=\"A running example\"></p>\n<p>This was in fact a remarkable technical breakthrough. RL flipped the table, using the most cutting-edge techniques to finish in a short time work that was previously beyond imagination: many motions that are hard to model fully by hand can first be learned through massive trial and error in simulation, then combined with conventional control, state estimation, and safety constraints. The once towering technical barrier was, in part, simply bypassed.</p>\n<p>After that, domestic companies had a cheaper, faster-iterating route to robot motion control, and all kinds of robot dogs and humanoid robots gradually began to appear on the market.</p>\n<h3>The Challenges That Remain</h3>\n<p>Let us go from the easier problems to the harder ones.</p>\n<p>First, energy. There are of course battery-swapping solutions already, and they are passable, but that also means a robot's range and working hours are still limited. For a robot that performs for ten minutes this is not fatal, but for a production tool that has to work eight hours straight while carrying a load, it goes straight to cost.</p>\n<p>Second, structure. Many humanoid robots today use motor-driven joints, and those joints have to handle <strong>torque, heat dissipation, weight, impact, and service life</strong> all at once, each pulling against the others. I do not know much about this area and do not dare go into specific designs, but I do know that demonstrating a motion once and repeating it thousands of times a day are held to completely different engineering standards. Whether it can get up on its own after a fall, whether precision drifts after continuous operation, how long it takes to swap out a broken joint — these small problems add up, and eventually they all show up on the bill.</p>\n<p>Finally, the brain. Reinforcement learning has advanced a robot's locomotion and balance a great deal, but that is closer to the “cerebellum”: how to place a foot, how to stay stable, how to execute an already defined motion. What really determines whether a robot can be widely used is whether it can understand its environment, plan tasks, handle surprises, and know when it cannot do something.</p>\n<p>That is also where several mainstream research directions come in — <a href=\"https://liyang.page/wm-tutorial/\">world models</a> (here is a good, simple introduction — though not a short one!), long-horizon task planning, and semantic understanding and exception handling in open environments. (In fact, there is a fair amount of skepticism about how well these directions will ultimately pan out.)</p>\n<p><img src=\"https://img.markxu.icu/img20260915200112273.png\" alt=\"Training A World\"></p>\n<p>Many companies have started collecting data, hoping to score a breakthrough through the scaling law the way large language models did. But real-world data, unlike text, cannot simply be downloaded from the internet. A segment of robot data requires real hardware to execute it, needs the actions and outcomes annotated, and may come with wear, collisions, and human takeovers.</p>\n<p>In other words, a language model can be trained “for free” on text from the internet, <strong>while every round of “learning” for a robot model wears down hardware and consumes electricity and human labor.</strong> That cost structure makes it hard to simply replicate the explosive trajectory of large language models.</p>\n<p>More troublesome still is whether data collected from different embodiments, different camera positions, and different joint parameters can transfer to one another. This resembles the problems I know from medical imaging — and may be even messier. Medical imaging still has no clean answer the way language models do.</p>\n<p>So the “brain” is not just a question of whether the model is smart enough; it is also a question of where the data comes from, who bears the cost of mistakes, and whether capabilities can be reproduced reliably.</p>\n<h2>Who Is Actually Buying the Robots — Market and Application Space</h2>\n<p>Some robot companies already have decent revenue, and Unitree's numbers show it turned a profit long ago, but that does not directly prove that humanoid robots have a large end market. We first need to look at who is buying these robots and what they are using them for.</p>\n<p>Judging from news reports and public cases, humanoid robots genuinely and steadily deployed in production are still very few; most buyers are universities, research institutes, showrooms, and companies that need to build on top of them. I often see universities publicizing that their lab has a Unitree robot, and I often see robots at product launches, shopping malls, and sports meets, but I rarely see a factory publicly show how long one has run continuously, how much cost it has replaced, or how much stable output it has generated.</p>\n<p><img src=\"https://img.markxu.icu/img20260915200554010.png\" alt=\"A Unitree robot\"></p>\n<p>Morgan Stanley's data says that 65% go to display, reception, and research, with only 35% actually reaching a production line. The materials Unitree filed with the Shanghai Stock Exchange are even more blunt: in the first three quarters of 2025, <strong>73.6% of humanoid robot revenue came from research and education.</strong> And of the ones that do reach a production line, how many are there to tell a story? That is not something we can verify for now.</p>\n<p>The robot here is more a paper-production tool, an object of capital narrative, and a direction for hype.</p>\n<p>I prefer to think of it as an expensive Lego model, or more aptly, as something like DJI's RoboMaster: an excellent platform for teaching, research, and development that can also give rise to competitions, performances, and enthusiast culture — but that is a different thing from becoming a general production tool.</p>\n<p><img src=\"https://img.markxu.icu/img20260915200647133.png\" alt=\"A RoboMaster competition\"></p>\n<p>Of course, pilot projects are a necessary stage before mass production. I am not saying “there is none now, so there will be none later.” I only think that when we discuss the market, we cannot count every robot sent into a lab, a showroom, or a pilot line as already-validated productivity in advance.</p>\n<p>At this point someone will say: no market today does not mean the potential market is small. Every device in society today is designed around human ergonomics, so the only truly general-purpose robot in the future can be a humanoid one.</p>\n<p>But that logic does not close the loop.</p>\n<h3>Human-Like ≠ General-Purpose ≠ Useful</h3>\n<p>There are two faulty inferences here: first, human-like ≠ general-purpose; second, general-purpose ≠ useful.</p>\n<p>The human hand is certainly very dexterous, but it was not designed for the modern industrial environment. There are many explanations for how the hand's shape evolved — grasping, throwing, and tool use may all have played a part; see this paper on <a href=\"https://onlinelibrary.wiley.com/doi/10.1046/j.1469-7580.2003.00185.x\">the relationship between throwing and the evolution of the human hand</a>. But society today has developed far faster than biological evolution. We invent different tools to do different things precisely to make up for what the hand does poorly.</p>\n<p>This is itself an example of “general-purpose ≠ useful.” You can just about turn a screw with your fingers, but a screwdriver tightens it more; a ratcheting screwdriver tightens it fast and tight; and on a production line people go straight to a torque-controlled power tool. The human hand is general-purpose, but what actually raises productivity tends to be the tools that look not general-purpose at all.</p>\n<p><img src=\"https://img.markxu.icu/img20260915200814210.png\" alt=\"An industrial robot\"></p>\n<p>The evolution of these tools is all accumulated industrial knowledge — why can't it carry over to robots? A wheeled base moves more efficiently, a robotic arm positions more stably, a fixture can fit a part directly, and a conveyor belt does not even need to “understand” what it is transporting. When a scenario is fixed enough, companies usually prefer to modify the environment and the process rather than buy a complex machine with two legs, ten fingers, and whole-body balance.</p>\n<p>Even when a system genuinely needs to handle many tasks, it does not have to imitate a human completely. Modular end effectors, quickly swappable fixtures, a mobile base plus a robotic arm, and then letting humans — who are already smart enough — handle the few exceptions: that may be a cheaper and more reliable transition.</p>\n<p>The greatest paradox of the humanoid robot is that it tries to use the most general-purpose body to solve the least general-purpose tasks.</p>\n<p>The real advantage of a humanoid form is that it can walk straight into environments designed for people without first changing the doors, the stairs, and the tools. But that advantage carries a hidden precondition: <strong>the cost of remodeling the environment must be higher than the cost of maintaining the complexity of a humanoid form.</strong> In homes, disaster zones, or old buildings, that precondition may hold; in factories and warehouses that can be replanned, it may not.</p>\n<h3>So Where Is the Application Space?</h3>\n<p>In my view, the applications where a humanoid form is irreplaceable may not be the assembly line that gets advertised most, but the scenarios where being human-like is itself valuable. If we really can build a robot human-like enough in the future — <strong>one that feels, looks, and talks like a person</strong> — then of course it can become an NPC in a virtual theme park or a companion robot. At that point the humanoid form is not there to barely accommodate a wrench; it is the product experience itself.</p>\n<p>Ordered from easy to hard, I think the progression looks more like this:</p>\n<ul>\n<li>The boxing robots in <em>Real Steel</em> — sports events</li>\n<li>The home-care robot in <em>A.I.</em> — emotional companion robots</li>\n<li>The virtual-park NPCs in <em>Westworld</em> — entertainment facilities</li>\n<li>The general-purpose robots doing all kinds of basic work in <em>Detroit: Become Human</em> — replacements for blue-collar workers</li>\n</ul>\n<p><img src=\"https://img.markxu.icu/img20260915200855629.png\" alt=\"A robot from Westworld\"></p>\n<p>And we are still a long way from the state of things in science fiction. So the current market and level of technology only prove that “we can already build increasingly agile humanoid robots”; they do not directly prove that “the more a robot looks like a human, the closer it is to a correct business path.”</p>\n<h2>Why Is Everyone Bullish on Humanoid Robots?</h2>\n<p>Next I want to talk about a few points that come up in today's discussion of humanoid robots.</p>\n<h3>How Does the Emotion Arise?</h3>\n<p>First, movies and other science fiction have made people unconsciously hold a lot of rose-colored fantasies about humanoid robots. Compared with a robotic arm, a humanoid robot is easier to film, to spread, and to understand. It only needs to take a couple of steps, wave, and show an expression on its head, and the audience can empathize with it immediately.</p>\n<p>As with the application spaces listed above, it is not that humanoid robots have no future; it is that we often mistake the imagining that science fiction has already done on their behalf for capabilities that real products have already achieved.</p>\n<p>The economy is sluggish right now, and everyone is eager to find a breakthrough that can pull investment along. Our <strong>materialist education keeps stressing the importance of productivity and technological progress</strong>, so people are naturally willing to support advanced productivity they can see and touch, hoping that one technological leap will improve their current situation. The humanoid robot happens to satisfy exactly this expectation: it offers the imaginative promise of artificial intelligence, the visual impact of a physical machine, and a link to manufacturing and supply chains.</p>\n<p><img src=\"https://img.markxu.icu/img20260915201013436.png\" alt=\"Hegel and Marx\"></p>\n<p>Capital, local governments, the media, and startups all have incentives to keep telling this story. Capital needs a big enough potential market, localities need a new industrial calling card, the media needs eye-catching footage, and startups must first convince others that the future is big enough before they can get the money to keep developing today's immature products. These incentives do not necessarily mean a scam, but they naturally tilt the discussion toward its most optimistic version.</p>\n<h3>Rebutting the Most Common Argument</h3>\n<p>The most famous argument, and the easiest to rebut: no technology was smooth sailing at the start.</p>\n<p>Some cite the automobile as an example, others the airplane, the mobile phone, and the mobile internet. All of them met with skepticism at first and later genuinely transformed human life.</p>\n<p><strong>But this is not the first time humanoid robots have appeared in the public eye.</strong></p>\n<p>In the 1980s, Heathkit brought out the HERO 1, and Tomy released “home robots” like the Omnibot. They could move and make sounds, and some could pick things up with a simple arm. They carried the same dream of home automation at the time, yet later never became part of the household appliance lineup.</p>\n<p><img src=\"https://img.markxu.icu/img20260915201142303.png\" alt=\"Omnibot\"></p>\n<p>People remember only the products that succeeded. Plenty of technologies were once favored and later failed to achieve mass adoption; people simply skip over them when arguing, landing squarely in <strong>survivorship bias</strong>.</p>\n<p>Examples include hydrogen airships, the Concorde supersonic airliner, 3D televisions, Google Glass, and the metaverse of a few years ago. Not every one of them was a total technical failure — some even have niche uses to this day — but none delivered on the promise, made at the peak of their hype, that they were about to change everyone's life.</p>\n<p><img src=\"https://img.markxu.icu/img20260915201224958.png\" alt=\"An airship\"></p>\n<p>These examples each match some of the current features of humanoid robots: the demos are stunning, the technology is genuinely advanced, and real demand can be found, but cost, supporting infrastructure, reliability, or social acceptance kept them from expanding as originally imagined.</p>\n<p>So “people once misjudged the automobile” does not prove that questioning humanoid robots today is wrong. To complete that argument, at least a few more specific questions need answers: What problem does it solve that nothing else can? How much cheaper is it than a human or specialized equipment? How many tasks can it still complete outside a carefully staged demo environment? And who bears the cost of failures, maintenance, and safety?</p>\n<h2>How It Might Develop</h2>\n<p>But I am not pessimistic enough to think humanoid robots will amount to nothing. I suspect it will end up closer to blockchain, which was equally hot a few years ago.</p>\n<p>Blockchain is a classic case of “over-promised, under-delivered, but leaving behind some real capability,” and it broadly matches the development path I have in mind for robots, so I will use it as the example.</p>\n<p>At the peak of the ICO boom in 2017, many projects could raise large sums with nothing but a white paper, as if every industry should be on-chain; in 2018–2019, after the bubble burst and a large number of projects went to zero, blockchain was in turn denounced wholesale as a scam. Today the speculators have left, but the underlying technology really is still developing in a few areas, such as stablecoins and cross-border settlement.</p>\n<p><img src=\"https://img.markxu.icu/img20260915201304479.png\" alt=\"Cryptocurrencies spawned by blockchain technology\"></p>\n<p>In other words, a bubble and a technology are not an either-or choice. <strong>A field can contain real technological progress, inflated commercial promises, and a flood of trend-chasing projects all at once.</strong> When the bubble recedes, what remains may not be the product from the original story; it may be training frameworks, low-cost joints, sensors, control algorithms, supply chains, and a cohort of engineers who genuinely understand robot systems. A body of genuinely useful technology accumulates, and then gets taken apart and installed elsewhere.</p>\n<p>My guess is that humanoid robots will go through something similar:</p>\n<ol>\n<li>During the hype, every company has to make a humanoid robot, and every product launch has to have one walk a few steps.</li>\n<li>Once capital starts demanding revenue and repeat purchases, a batch of companies that cannot find a stable scenario exits, and the industry enters a disillusionment phase.</li>\n<li>The companies that remain stop emphasizing “general-purpose” and move into a few specific scenarios, honestly solving for one motion, one process step, or one hazardous environment.</li>\n<li>Parts of humanoid technology get spun out into robotic arms, exoskeletons, wheeled robots, and other specialized equipment.</li>\n<li>Parts of the humanoid form and its sensing technology can assist people with disabilities, much like the relationship between rockets and MRI.</li>\n</ol>\n<p>If that is how it ends up, I will not consider humanoid robots a failure. What fails is the overinflated narrative that “it will soon be a general-purpose laborer in every home and every factory,” not the technology accumulated over these years.</p>\n<h2>If I Am Not Bullish on Humanoid Robots, What Am I Bullish On?</h2>\n<p>What I am genuinely bullish on is the AI models, perception, and reasoning behind humanoid robots.</p>\n<p>A model that can understand the real world, recognize the relationships between objects, grasp human intent, and predict what an action will lead to — that is what could become the truly general part of embodied AI. It does not have to be installed in a humanoid body; it can control robotic arms, drones, cars, and wheeled bases, or even exist purely as an assistive system for people.</p>\n<p><img src=\"https://img.markxu.icu/img20260915201419981.png\" alt=\"A surgical robot\"></p>\n<p>Compared with replicating the human body, I am more bullish on these directions:</p>\n<ul>\n<li>Specialized robots designed for clearly defined tasks, such as warehouse, cleaning, inspection, agricultural, and surgical robots</li>\n<li>Modular systems made of a mobile base, a robotic arm, and interchangeable end effectors</li>\n<li>Semi-autonomous systems where humans take over remotely and the model gradually learns, rather than pursuing full autonomy from the start</li>\n<li>Vision-language-action models that can transfer across embodiments, world models, and robot data infrastructure</li>\n</ul>\n<p>None of these is as spectacular as a complete humanoid robot doing flips on stage, but they are more likely to enter the real world first. Because what users are buying is not a “feeling of intelligence,” but having a task completed more cheaply, more reliably, and more safely.</p>\n<h2>What Do I Think Will Happen to Humanoid Robots?</h2>\n<p>I think humanoid robots will not spread especially widely, but they will become a new kind of toy and a social culture. As prices fall, more schools, labs, and hobbyists will develop their own motions, appearances, and AI models, and bring their robots to fighting, soccer, racing, and obstacle-course competitions. Like RoboMaster, drones, and the early personal computer, they may first form a creative enthusiast ecosystem.</p>\n<p><img src=\"https://img.markxu.icu/img20260915201547585.png\" alt=\"The UK premiere of Real Steel\"></p>\n<p>At the same time, they will also enter a small number of scenarios where the environment cannot be remodeled but a human form is needed. In those places, even an expensive, slow robot already has value if it keeps humans out of hazardous environments. Some robots will exist in the form of rentals, performances, marketing, and companion products. They may be a decent business — just not the business of “replacing all labor.”</p>\n<p>Of course, I will also leave my future self a few criteria by which I can be proven wrong. If future humanoid robots can:</p>\n<ol>\n<li>Work for hours on end in a home or factory without pre-scripting, and rarely need human takeover;</li>\n<li>Learn new tasks after moving to a new site without collecting a large amount of new data;</li>\n<li>Still be more cost-effective than a human or specialized equipment once purchase, energy, maintenance, downtime, and safety costs are all included;</li>\n<li>Go from a demo at a launch event to a standard product that can be delivered at volume, maintained reliably, and has clear accountability;</li>\n</ol>\n<p>then I will admit that my current judgment underestimated the transferability of general models and the speed at which mass production lowers the cost of complex hardware. By then, being “human-like” may truly be more than the easiest form factor to market; it may be the most economical way to fit into the human world.</p>\n<p><img src=\"https://img.markxu.icu/img20260915202135584.png\" alt=\"Robot games\"></p>\n<p>But until those things happen, I am still unwilling to treat a smooth dance routine, a robot soccer match, or a slickly edited factory video as evidence that the next generation of productivity has arrived.</p>\n<p>Humanoid robots are certainly part of the future, but the future never takes only the shape most familiar from science fiction. The technologies that truly change life are sometimes precisely the machines that no longer look human, no longer belong on stage, and have already been working quietly for years.</p>\n<hr>\n<p>Update, September 19, 2026:</p>\n<p>This article received two comments on Zhihu, both arguing that it commits the straw man fallacy — that I set up an easy target that cannot hit back, and then attacked it. Looking at it now, there is indeed some of that.</p>\n<p><WebsiteCard\n  url=\"https://zhuanlan.zhihu.com/p/2083297787320583911\"\n  title=\"Zhihu link\"\n  description=\"Jump over to Zhihu to see what everyone is saying in the comments\"\n/></p>\n<p>You can click this card to jump over and take a look.</p>\n",
      "summary": "[AI-translated] I have never been bullish on humanoid robots becoming the next generation of general-purpose productivity. I want to put my current judgment on the record: if I end up being proven wrong, I will at least be able to look back and see exactly which of my concerns the industry managed to overcome.",
      "date_published": "2026-08-31T00:00:00.000Z",
      "date_modified": "2026-09-19T00:00:00.000Z",
      "image": "https://img.markxu.icu/img20260915182504981.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/frontend-effects-resources-en",
      "url": "https://markxu.icu/blog/frontend-effects-resources-en",
      "title": "How Easy Is It to Build Web Effects Now? A Few Frontend Components and Animation Resources to Share",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. This is a short resource roundup with not much substance to it—just sharing some very cool website component resources I have come across recently. CanvasUI – Experimental Products This is a component library built on fairly new technology. It uses Chrome's recently introduced experimental HTML-in-Canvas feature, which lets DOM/HTML content take part in rendering and visual transforms inside a Canvas. This is still a relatively new browser capability, so compatibility deserves special attention. Fortunately, Canvas UI has thought about fallbacks itself and provides a degraded experience on browsers that do not support it: your site still works normally, but without the effects it simply will not look as good. It is especially suited to pages that need complex visuals such as 3D, liquid, glass, or shatter effects. Take a look at the previews on the site and decide whether they are the effect you want. Personally, I think it is fine to experiment and play with, but I would not strongly recommend putting it into production: for one, compatibility is quite limited, and for another, it is fairly performance-hungry and not very friendly to low-end devices. React Bits – Special Effects A collection of effect components centered on React, with implementations also available in JavaScript / TypeScript, CSS / Tailwind, and other variants. It has all kinds of cool effects, most of them focused on text effects and interactive background effects. The emphasis is on rather unusual visuals, and it is a bit flashy for its own sake—you can draw plenty of experience and inspiration from it, but I would not recommend using it in bulk. Aceternity – Practical and Grounded This is another React component library, but the overall style is more pragmatic, easy to use, and broadly applicable. Some templates require payment and do look good, but the free tier is plenty. Personally, I think handing those paid templates to today's AI and building the effects yourself would not necessarily turn out worse—it might actually fit your needs better. It is well worth exploring to pick up some interaction and design inspiration. Magic UI – As a Supplement This is still a widget library mainly for React. It is not very different from Aceternity above, but it includes some interaction effects the other one lacks. The site does not have much content, but it works well as a complement. Motion – Designing Your Own Animations More accurately, Motion is an animation library rather than a UI component library in the traditional sense. It provides animation capabilities in JavaScript, React, Vue, and other environments, and can handle page transitions, gestures, layout changes, and all kinds of interaction animations. My own site uses a few Motion effects, and they work quite well. If you do not just want to “find a ready-made component” but want to control the animation yourself, Motion is more worth studying than a plain component library—go explore it. anime.js – An Animation Dictionary The website itself is ridiculously cool. It is a web library focused mainly on animation effects, packed with all kinds of animation curves and a lot of design effects for interactive elements. You can use it to animate DOM, SVG, CSS properties, and various other objects, which makes it very suitable for studying animation timing, easing, and interaction patterns. Think of it as a dictionary of animation effects; I highly recommend it to anyone who wants to learn design. Animate UI – Small Touch-ups Animate UI has fewer components than the libraries above, but what is worth looking at is its micro-interactions, especially the animations for components like Icon and Button. For a project that already has a complete UI and just feels the page is “a bit lifeless,” effects like these are actually quite practical. Inspira UI – A Vue-Focused Component Library Everything above is a React component library—so what are we Vue users supposed to eat? Haha, well, here it comes. This is an animation library built mainly for Vue and Nuxt, and its effects are comprehensive—everything from simple UI touches to full-page effects. Most of what is inside is enough to match the React component libraries mentioned above. That said, this does not mean Vue users can only use Vue component libraries. React components obviously cannot be copied straight into a Vue project, but many visual effects do not actually depend on React itself—especially those built with CSS, SVG, Canvas, and plain JavaScript. Now that we have AI, porting a React component to Vue is usually much easier than before, so when I look for components I do not pay much attention to whether they are React or Vue. 21st.dev – My Personal Go-To ✨ This is the site I use the most myself. The animated title effect on my homepage and the CINEMA effect on the Movies page both started as inspiration from this site, blended with a few ideas of my own. The previews and categories on the site make the whole experience intuitive, it is convenient to use, and the effects are comprehensive. I highly recommend browsing around and playing with it. Final Thoughts In the past, seeing a cool effect usually meant studying its code from top to bottom and then figuring out how to jam it into your own project. With AI, it is a completely different world now and much simpler: find an effect you like, then tell AI how the component is implemented, what its dependencies are, and what you need, and have it make the changes for you. This has become a very practical workflow. So when I browse these sites now, I no longer really treat them as mere “component libraries.” Often I am just looking for a visual effect I like, and then I reimplement it myself. The component library supplies inspiration and reference; AI helps turn it into something that fits your own site. I have recommended a lot of component libraries with animation effects above, but for a web page, the most important thing is not how cool it looks, but whether using it matches user intuition and whether the experience is smooth. A site whose learning curve and device requirements are reasonable is the best kind of site. Animation should help users understand a page's structure, state, and feedback, not make them wait just to watch an animation. Too many visual effects can not only interfere with getting information but also add performance and device overhead.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>This is a short resource roundup with not much substance to it—just sharing some very cool website component resources I have come across recently.</p>\n<h2>CanvasUI – Experimental Products</h2>\n<p><WebsiteCard\n  url=\"https://canvasui.dev/\"\n  title=\"Canvas UI\"\n  description=\"A component library built on Chrome's experimental HTML-in-Canvas feature\"\n/></p>\n<p>This is a component library built on fairly new technology. It uses Chrome's recently introduced experimental HTML-in-Canvas feature, which lets DOM/HTML content take part in rendering and visual transforms inside a Canvas. This is still a relatively new browser capability, so compatibility deserves special attention. Fortunately, Canvas UI has thought about fallbacks itself and provides a degraded experience on browsers that do not support it: your site still works normally, but without the effects it simply will not look as good.</p>\n<p>It is especially suited to pages that need complex visuals such as 3D, liquid, glass, or shatter effects. Take a look at the previews on the site and decide whether they are the effect you want. Personally, I think it is fine to experiment and play with, but I would not strongly recommend putting it into production: for one, compatibility is quite limited, and for another, it is fairly performance-hungry and not very friendly to low-end devices.</p>\n<h2>React Bits – Special Effects</h2>\n<p><WebsiteCard\n  url=\"https://reactbits.dev/\"\n  title=\"React Bits\"\n  description=\"A bunch of small React effect components\"\n/></p>\n<p>A collection of effect components centered on React, with implementations also available in JavaScript / TypeScript, CSS / Tailwind, and other variants. It has all kinds of cool effects, most of them focused on text effects and interactive background effects. The emphasis is on rather unusual visuals, and it is a bit flashy for its own sake—you can draw plenty of experience and inspiration from it, but I would not recommend using it in bulk.</p>\n<h2>Aceternity – Practical and Grounded</h2>\n<p><WebsiteCard\n  url=\"https://ui.aceternity.com/\"\n  title=\"Aceternity\"\n  description=\"More small React components, but these are mostly practical\"\n/></p>\n<p>This is another React component library, but the overall style is more pragmatic, easy to use, and broadly applicable.</p>\n<p>Some templates require payment and do look good, but the free tier is plenty. Personally, I think handing those paid templates to today's AI and building the effects yourself would not necessarily turn out worse—it might actually fit your needs better. It is well worth exploring to pick up some interaction and design inspiration.</p>\n<h2>Magic UI – As a Supplement</h2>\n<p><WebsiteCard\n  url=\"https://magicui.design/\"\n  title=\"Magic UI\"\n  description=\"Another widget and template library, built with React, TypeScript, Tailwind CSS, and Motion\"\n/></p>\n<p>This is still a widget library mainly for React. It is not very different from Aceternity above, but it includes some interaction effects the other one lacks. The site does not have much content, but it works well as a complement.</p>\n<h2>Motion – Designing Your Own Animations</h2>\n<p><WebsiteCard\n  url=\"https://motion.dev/\"\n  title=\"Motion\"\n  description=\"An animation library for JS, React, and Vue\"\n/></p>\n<p>More accurately, Motion is an animation library rather than a UI component library in the traditional sense. It provides animation capabilities in JavaScript, React, Vue, and other environments, and can handle page transitions, gestures, layout changes, and all kinds of interaction animations.</p>\n<p>My own site uses a few Motion effects, and they work quite well. If you do not just want to “find a ready-made component” but want to control the animation yourself, Motion is more worth studying than a plain component library—go explore it.</p>\n<h2>anime.js – An Animation Dictionary</h2>\n<p><WebsiteCard\n  url=\"https://animejs.com/\"\n  title=\"anime JS\"\n  description=\"An animation library for vanilla JS and React\"\n/></p>\n<p>The website itself is ridiculously cool. It is a web library focused mainly on animation effects, packed with all kinds of animation curves and a lot of design effects for interactive elements.</p>\n<p>You can use it to animate DOM, SVG, CSS properties, and various other objects, which makes it very suitable for studying animation timing, easing, and interaction patterns. Think of it as a dictionary of animation effects; I highly recommend it to anyone who wants to learn design.</p>\n<h2>Animate UI – Small Touch-ups</h2>\n<p><WebsiteCard\n  url=\"https://animate-ui.com/\"\n  title=\"animate-ui\"\n  description=\"Yet another React component library\"\n/></p>\n<p>Animate UI has fewer components than the libraries above, but what is worth looking at is its micro-interactions, especially the animations for components like Icon and Button. For a project that already has a complete UI and just feels the page is “a bit lifeless,” effects like these are actually quite practical.</p>\n<h2>Inspira UI – A Vue-Focused Component Library</h2>\n<p><WebsiteCard\n  url=\"https://inspira-ui.com/\"\n  title=\"Inspira UI\"\n  description=\"Finally, an animation library made specifically for Vue and Nuxt\"\n/></p>\n<p>Everything above is a React component library—so what are we Vue users supposed to eat?</p>\n<p>Haha, well, here it comes.</p>\n<p>This is an animation library built mainly for Vue and Nuxt, and its effects are comprehensive—everything from simple UI touches to full-page effects. Most of what is inside is enough to match the React component libraries mentioned above.</p>\n<p>That said, this does not mean Vue users can only use Vue component libraries. React components obviously cannot be copied straight into a Vue project, but many visual effects do not actually depend on React itself—especially those built with CSS, SVG, Canvas, and plain JavaScript. Now that we have AI, porting a React component to Vue is usually much easier than before, so when I look for components I do not pay much attention to whether they are React or Vue.</p>\n<h2>21st.dev – My Personal Go-To ✨</h2>\n<p><WebsiteCard\n  url=\"https://21st.dev/\"\n  title=\"21st\"\n  description=\"The site I use most myself, with what feels like the most varied effects\"\n/></p>\n<p>This is the site I use the most myself. The animated title effect on my homepage and the CINEMA effect on the <a href=\"https://markxu.icu/movies\">Movies page</a> both started as inspiration from this site, blended with a few ideas of my own. The previews and categories on the site make the whole experience intuitive, it is convenient to use, and the effects are comprehensive. I highly recommend browsing around and playing with it.</p>\n<h2>Final Thoughts</h2>\n<p>In the past, seeing a cool effect usually meant studying its code from top to bottom and then figuring out how to jam it into your own project. With AI, it is a completely different world now and much simpler: <strong>find an effect you like, then tell AI how the component is implemented, what its dependencies are, and what you need, and have it make the changes for you. This has become a very practical workflow.</strong></p>\n<p>So when I browse these sites now, I no longer really treat them as mere “component libraries.” Often I am just looking for a visual effect I like, and then I reimplement it myself.</p>\n<p>The component library supplies inspiration and reference; AI helps turn it into something that fits your own site.</p>\n<p>I have recommended a lot of component libraries with animation effects above, but for a web page, the most important thing is not how cool it looks, but whether using it matches user intuition and whether the experience is smooth. A site whose learning curve and device requirements are reasonable is the best kind of site.</p>\n<p><strong>Animation should help users understand a page's structure, state, and feedback, not make them wait just to watch an animation. Too many visual effects can not only interfere with getting information but also add performance and device overhead.</strong></p>\n",
      "summary": "[AI-translated] Sharing some really cool frontend components I have come across online recently!",
      "date_published": "2026-08-29T00:00:00.000Z",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/queue-implementation-decision-tree-en",
      "url": "https://markxu.icu/blog/queue-implementation-decision-tree-en",
      "title": "Queue Implementation Approaches: From Constraints to a Decision Tree",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. Recently, while reviewing data structures, I realized that I keep getting it wrong when mapping a logical structure onto its physical counterpart—that is, when picking an implementation. I searched online for guidance, but nothing out there was especially easy to grasp, so I built a decision tree myself. Now I have a rough mental picture, and quite a few ideas have fallen into place. Key Constraints in the Decision Tree When drawing this tree, I fixed the following decision dimensions which are also high-frequency exam topics on the 408 : Storage method : sequential / linked Whether circular : solves both “false overflow” and “access at both ends” List type : singly / doubly linked Pointer setup : head pointer / tail pointer / both head and tail pointers Header node : whether to introduce a sentinel node to simplify boundary cases The single most important rule of thumb here is: A non-circular structure must have both a head and a tail pointer; only a circular structure can possibly get by with a single pointer. Performance Comparison of Each Implementation Implementation Empty check Full check Enqueue Dequeue Space overhead Recommendation -------------------------- ------------------- -------------------- ---- ---- -------- ---------- Sequential circular queue front == rear rear+1 %M == front O 1 O 1 Low ⭐⭐⭐ Non-circular singly linked + head & tail with header node front- next == NULL None O 1 O 1 Medium ⭐⭐⭐ Non-circular singly linked + head & tail no header node front == NULL None O 1 O 1 Medium ⭐⭐ Circular singly linked + tail pointer only ⭐ rear- next == rear None O 1 O 1 Lowest ⭐⭐⭐⭐⭐ Circular doubly linked + tail pointer only rear- next == rear None O 1 O 1 High ⭐⭐⭐ Circular doubly linked + head & tail front == NULL None O 1 O 1 Highest ⭐⭐ An Easily Overlooked Detail: The Header Node A header node exists not to “save time” or “save space,” but to eliminate the special case of an empty queue : Linked queue : once the queue is emptied, is liable to dangle, so keeping a header node is safer. Linked stack : the operations are uniform, is already clear enough, and skipping the header node is simpler. Circular linked queue : the closed-loop structure handles the boundary cases on its own, so a header node usually isn't needed. The Physical and Logical Relationship of a Stack A stack comes in two kinds: the sequential stack array-based and the linked stack singly linked list . Using a doubly linked list is completely unnecessary—it's overkill. And the best choice for a stack is unique, namely the case below: Operations happen at the head of the list Only a head pointer No header node Singly linked Non-circular",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>Recently, while reviewing data structures, I realized that I keep getting it wrong when mapping a logical structure onto its physical counterpart—that is, when picking an implementation. I searched online for guidance, but nothing out there was especially easy to grasp, so I built a decision tree myself. Now I have a rough mental picture, and quite a few ideas have fallen into place.</p>\n<p><img src=\"https://img.markxu.icu/imgqueue.png\" alt=\"Decision tree diagram\"></p>\n<h2>Key Constraints in the Decision Tree</h2>\n<p>When drawing this tree, I fixed the following decision dimensions (which are also high-frequency exam topics on the 408):</p>\n<ol>\n<li><strong>Storage method</strong>: sequential / linked</li>\n<li><strong>Whether circular</strong>: solves both “false overflow” and “access at both ends”</li>\n<li><strong>List type</strong>: singly / doubly linked</li>\n<li><strong>Pointer setup</strong>: head pointer / tail pointer / both head and tail pointers</li>\n<li><strong>Header node</strong>: whether to introduce a sentinel node to simplify boundary cases</li>\n</ol>\n<p>The single most important rule of thumb here is:</p>\n<blockquote>\n<p><strong>A non-circular structure must have both a head and a tail pointer; only a circular structure can possibly get by with a single pointer.</strong></p>\n</blockquote>\n<h2>Performance Comparison of Each Implementation</h2>\n<table>\n<thead>\n<tr>\n<th>Implementation</th>\n<th>Empty check</th>\n<th>Full check</th>\n<th>Enqueue</th>\n<th>Dequeue</th>\n<th>Space overhead</th>\n<th>Recommendation</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Sequential circular queue</td>\n<td>front == rear</td>\n<td>(rear+1)%M == front</td>\n<td>O(1)</td>\n<td>O(1)</td>\n<td>Low</td>\n<td>⭐⭐⭐</td>\n</tr>\n<tr>\n<td>Non-circular singly linked + head &amp; tail (with header node)</td>\n<td>front-&gt;next == NULL</td>\n<td>None</td>\n<td>O(1)</td>\n<td>O(1)</td>\n<td>Medium</td>\n<td>⭐⭐⭐</td>\n</tr>\n<tr>\n<td>Non-circular singly linked + head &amp; tail (no header node)</td>\n<td>front == NULL</td>\n<td>None</td>\n<td>O(1)</td>\n<td>O(1)</td>\n<td>Medium</td>\n<td>⭐⭐</td>\n</tr>\n<tr>\n<td>Circular singly linked + tail pointer only ⭐</td>\n<td>rear-&gt;next == rear</td>\n<td>None</td>\n<td>O(1)</td>\n<td>O(1)</td>\n<td>Lowest</td>\n<td>⭐⭐⭐⭐⭐</td>\n</tr>\n<tr>\n<td>Circular doubly linked + tail pointer only</td>\n<td>rear-&gt;next == rear</td>\n<td>None</td>\n<td>O(1)</td>\n<td>O(1)</td>\n<td>High</td>\n<td>⭐⭐⭐</td>\n</tr>\n<tr>\n<td>Circular doubly linked + head &amp; tail</td>\n<td>front == NULL</td>\n<td>None</td>\n<td>O(1)</td>\n<td>O(1)</td>\n<td>Highest</td>\n<td>⭐⭐</td>\n</tr>\n</tbody>\n</table>\n<h2>An Easily Overlooked Detail: The Header Node</h2>\n<p>A header node exists not to “save time” or “save space,” but to <strong>eliminate the special case of an empty queue</strong>:</p>\n<ul>\n<li><strong>Linked queue</strong>: once the queue is emptied, <code>rear</code> is liable to dangle, so keeping a header node is safer.</li>\n<li><strong>Linked stack</strong>: the operations are uniform, <code>top == NULL</code> is already clear enough, and skipping the header node is simpler.</li>\n<li><strong>Circular linked queue</strong>: the closed-loop structure handles the boundary cases on its own, so a header node usually isn't needed.</li>\n</ul>\n<h2>The Physical and Logical Relationship of a Stack</h2>\n<p>A stack comes in two kinds: the sequential stack (array-based) and the linked stack (singly linked list).</p>\n<p>Using a doubly linked list is completely unnecessary—it's overkill. And the best choice for a stack is unique, namely the case below:</p>\n<ul>\n<li>Operations happen at the head of the list</li>\n<li>Only a head pointer</li>\n<li>No header node</li>\n<li>Singly linked</li>\n<li>Non-circular</li>\n</ul>\n",
      "summary": "[AI-translated] While reviewing data structures, I realized I keep choosing the wrong way to implement a queue, so I built a decision tree to lay out the factors involved and summarize them.",
      "date_published": "2026-07-30T00:00:00.000Z",
      "image": "https://img.markxu.icu/imgqueue.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/Wuhan-food-walk-note1-en",
      "url": "https://markxu.icu/blog/Wuhan-food-walk-note1-en",
      "title": "A Wuhan Food-Crawl Diary 1",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. I had been in Wuhan for more than a month. Apart from going downtown for the IELTS exam or to deal with a few personal matters, I had not really taken the time to see the city simply as a tourist. Then, this week, I came across an interesting Wuhan-based creator. They used characters from my favorite anime, Bocchi the Rock! , to visit restaurants all over Wuhan, and the videos were edited in a very entertaining way I kept watching one more episode . The food was also exactly the kind I like. Since I had been worrying about having nowhere to go, I decided to follow the creator's route for a day. Route: The rest of this diary follows that route through the day. The Creator's Recommendation! Or perhaps I should say, the recommended creator, hehe ¬◡¬ ✧ This creator posts videos on Bilibili. I happened to scroll onto this one and discovered that Wuhan had a Michelin restaurant that was this affordable! Since I had just arrived in Wuhan and did not know where to begin exploring, I made up my mind to visit it on the weekend. Macau Chan Kwong Kee Roast Meat Restaurant This was the restaurant mentioned in the creator's video. I set out early, spent more than an hour on the metro, and arrived at the storefront. The restaurant is in central Wuhan, less than 200 meters along the road from the northwest gate of Zhongshan Park. There is no commercial street around it and no flashy sign—only a simple, plain storefront. A school stands across the road, so the restaurant feels like the small noodle shops often found outside primary schools: quietly existing there and tending to its modest business. Next came the ordering. I did not follow the creator's recommendations; treating it as my first visit, I ordered a few things I personally like: Chilled tofu pudding, double-combo rice, char siu pastry, pineapple ice, and lau nai wah—a Hong Kong-style French toast dessert. Neither the tofu pudding nor the pineapple ice was particularly surprising, but both were solid and satisfying. The tofu pudding had absolutely no beany smell. It carried a faint soybean fragrance, was smooth and tender, and came with osmanthus honey that was not cloying or harsh on the throat. It was cold, refreshing, and wonderfully thirst-quenching. As a soda, the pineapple ice was barely sweet; nearly all the sweetness came from the melted ice cream and pineapple. Its only flaw was that there was not enough of it to drink with abandon. The double-combo rice and char siu pastry were what really surprised me. The moment these two dishes entered my mouth, the only word that came to mind was: satisfaction. First, the double-combo rice. The fatty char siu was pleasantly springy, with a rich sauce aroma. A little honey drizzled over it added another layer of flavor. The fat was soft and bouncy rather than greasy enough to coat the mouth unpleasantly. The roast duck had skin as thin as paper; eaten together with the sauce beneath it, it perfectly compensated for the slightly dry texture of the duck meat itself. It was not flawless, though. As the photos show, the plate was covered with a glossy layer of oil mixed with the honey drizzle. By the second half of the meal, it became somewhat rich and difficult for someone like me—who had been eating relatively lightly recently—to digest. Now, the honey-glazed char siu pastry. As soon as it arrived, it gave off the buttery, flaky aroma of a freshly baked pastry. Once I bit in, that fragrance filled my nose. Unlike at some restaurants, the honey glaze did not contain undissolved sugar crystals that scraped against the teeth. Perhaps it was because the pastry had just been baked, or perhaps the honey water had been mixed to exactly the right concentration so that it would not crystallize. The meat filling had been cut into tiny pieces. Yet once I pressed it with my tongue, it did not feel like a collection of separate chunks. I suspect they used fatty meat from a cut with little connective tissue, so it did not interfere with the texture. It was deeply satisfying. I did not really enjoy the lau nai wah. To me, it was pure sugar-and-fat. After the double-combo rice and char siu pastry, I needed something lighter to cleanse my palate, and ordering it after the meal was a mistake that got in the way of enjoying it. I will keep that in mind next time. As the first Michelin restaurant of my life, it left me very satisfied. At around ¥100 per person, it was also affordable. I would like to return one day and try the creator's recommendations. Zhongshan Park After lunch I was incredibly full. My reservation at the provincial museum was not until 3 p.m., so there was still plenty of time. I decided to walk around the nearby Zhongshan Park. The trees in the park were extremely thick and tall, suggesting that Zhongshan Park had existed here for a very long time. I was surprised to find a fairly substantial amusement park as well: a small roller coaster, a pirate ship, and several other medium-sized rides, all built in 2023. The classic boating attraction was still there too. It was a case of “old trees growing new branches”—the park retained the steadiness of its history while also feeling lively and fresh. After a simple walk through Zhongshan Park to digest lunch, I set off for the Hubei Provincial Museum to finally meet the Sword of Goujian, King of Yue, and the Marquis Yi of Zeng's bronze bells—two treasures I had been looking forward to seeing. Hubei Provincial Museum My reservation was for 3 p.m., but I arrived at 2:20. To give every visitor the best possible experience, the museum only allows entry up to 20 minutes before the reserved time be sure to note this! . As soon as I entered, I hurried toward the South Hall, where the Sword of Goujian is displayed. Even after entering the museum, getting a glimpse of the real sword required a long wait. Only then could I look at it for a few seconds among the crowd surrounding the display case. The sword is 55.7 centimeters long; the blade is 4.6 centimeters wide; the hilt is 8.4 centimeters long; and it weighs 875 grams. Both sides of the blade bear dark diamond-shaped patterns. The front of the guard is set with blue glaze, while the back has a turquoise pattern. The pommel is a circular ring containing 11 concentric circles. The entire blade was treated with sulfurization, greatly improving its corrosion resistance and toughness. This shows that ancient Chinese metallurgists had reached a remarkably high level of skill in metal smelting and surface treatment. When unearthed, the sword still shone coldly and was extraordinarily sharp. It reflects the advanced sword-making technology of ancient China and is a rare treasure among ancient weapons. —Wikipedia The whole sword was only about as long as a forearm—much smaller than I had imagined. It looked a little like an ancient person's collectible figure although according to the information, it was still a practical weapon . Seeing the real object was different from seeing it on television. We could not make out the details, but the museum had a complete exhibition and introduction devoted to the sword, and I still learned a great deal. Next, I headed for the second national treasure: the bronze bells of Marquis Yi of Zeng. The set of bells belonging to Marquis Yi of Zeng is enormous and completely intact. The bell frame is tall, formed from two perpendicular wooden structures of different lengths. The longer side lies to the west of the tomb and is 7.48 meters long and 2.65 meters high; the side toward the south is 3.35 meters long and 2.73 meters high. Seven painted wooden beams run through the frame, reinforced at both ends with bronze fittings decorated with coiled dragons. Six cast-bronze warriors wearing swords and eight columns support the entire set of bells, forming upper, middle, and lower tiers. The frame and its hooks number 246 in total. The bronze figures wear long robes, belts around their waists, and solemn expressions. —Wikipedia The hall containing the bells was much larger. Its display panels explained the details of Marquis Yi, the excavation, and the other instruments used alongside the bells. There were also bronze objects of all sizes around them. Their shapes and details were beyond anything I had imagined. Even with today's technology and methods, I am not sure they could be perfectly reproduced. There were many more national treasures, but here I will simply record a few that impressed me most. Huahua Pizza After the museum, I went to another restaurant recommended by the creator mentioned at the beginning: Huahua Pizza. The decor was simple and attractive, bright and fresh, and the street outside was very clean. Since lunch had left me extremely full, I ordered an eight-inch Margherita pizza ¥20 and a serving of fried chicken, the restaurant's top seller. The Margherita pizza arrived first. The price was fairly reasonable. A street cart I often visit in Xiamen sells a nine-inch pizza for the same ¥20, although this restaurant has a much nicer environment. Overall, it tasted fine—it tasted like a Margherita pizza, and I am not sure what else a Margherita pizza is supposed to do... Let me mention a few disappointments. First, the basil had been baked until slightly bitter and had lost its fresh fragrance, and there was not much of it. I really like the aroma of basil, but I could barely taste it here. Second, although the pizza was made to order, the wheat aroma of the crust was not prominent enough and the crust was not chewy enough. That was a little disappointing. Next came the restaurant's best-selling boneless fried chicken, which the creator had recommended enthusiastically. Even with expectations that high, the plate still surprised me. The crust was not merely crisp; it was coated in enormous bread crumbs, and the crust alone could compete with packaged potato chips. The base seasoning of the meat was balanced: it did not overpower the chicken's own flavor, but you could still taste the care that had gone into seasoning it. More importantly, the chicken inside was full of juice. Dipped into the sauce provided, the entire portion was not greasy even when eaten by one person. If you want a satisfying meal on a small budget, this restaurant is genuinely worth visiting. Conclusion Following “氵皮ki酱” led me to two restaurants and showed me that Wuhan has not only a formidable history but also an incredibly rich food scene. My stomach was full, but my heart felt strangely empty—there were still so many recommended restaurants I had not had time to visit and so many sights I had not had time to see. But that is all right. This was only the beginning of one of my many weekends in Wuhan. My exam preparation continues, but whenever I have free time, I will keep exploring the city's different sides. After all, life is not only about the hardship immediately before us 11408 ; it also includes Wuhan's hidden restaurants and the next journey of discovery.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>I had been in Wuhan for more than a month. Apart from going downtown for the IELTS exam or to deal with a few personal matters, I had not really taken the time to see the city simply as a tourist. Then, this week, I came across an interesting Wuhan-based creator. They used characters from my favorite anime, <em>Bocchi the Rock!</em>, to visit restaurants all over Wuhan, and the videos were edited in a very entertaining way (I kept watching one more episode). The food was also exactly the kind I like. Since I had been worrying about having nowhere to go, I decided to follow the creator's route for a day.</p>\n<p>Route:<br>\n<PhotoRoute\n  route=\"wuhan1\"\n  title=\"A Day in Wuhan\"\n  desc=\"A Wuhan food-and-sightseeing route and its stops reconstructed from the locations in the photos.\"\n/><br>\nThe rest of this diary follows that route through the day.</p>\n<h2>The Creator's Recommendation!</h2>\n<p>Or perhaps I should say, the recommended creator, hehe (¬◡¬)✧</p>\n<p>This creator posts videos on Bilibili. I happened to scroll onto this one and discovered that Wuhan had a Michelin restaurant that was this affordable! Since I had just arrived in Wuhan and did not know where to begin exploring, I made up my mind to visit it on the weekend.</p>\n<p><VideoEmbed\n  url=\"https://www.bilibili.com/video/BV1ciTq6fEQ6/?spm_id_from=333.1365.list.card_archive.click&vd_source=a1d2d12b1c9f3d1bf63ac9d24e2ff565\"\n  title=\"氵皮ki酱's video\"\n/></p>\n<h2>Macau Chan Kwong Kee Roast Meat Restaurant</h2>\n<p>This was the restaurant mentioned in the creator's video. I set out early, spent more than an hour on the metro, and arrived at the storefront.</p>\n<p>The restaurant is in central Wuhan, less than 200 meters along the road from the northwest gate of Zhongshan Park. There is no commercial street around it and no flashy sign—only a simple, plain storefront. A school stands across the road, so the restaurant feels like the small noodle shops often found outside primary schools: quietly existing there and tending to its modest business.</p>\n<div class=\"photo-scroll\">\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_120004.jpg\" alt=\"Storefront\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_120113.jpg\" alt=\"Inside the restaurant\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_120339.jpg\" alt=\"Inside the restaurant\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_120347.jpg\" alt=\"A small Michelin achievement\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_120353.jpg\" alt=\"Michelin on the cup\" />\n</div>\n<p>Next came the ordering. I did not follow the creator's recommendations; treating it as my first visit, I ordered a few things I personally like:</p>\n<p>Chilled tofu pudding, double-combo rice, char siu pastry, pineapple ice, and lau nai wah—a Hong Kong-style French toast dessert.</p>\n<div class=\"photo-scroll\">\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_120758.jpg\" alt=\"Osmanthus honey chilled tofu pudding\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_121033.jpg\" alt=\"Fatty char siu and roast duck double-combo rice\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_121100.jpg\" alt=\"Honey-glazed char siu pastry\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_121406.jpg\" alt=\"A bitten char siu pastry\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_122136.jpg\" alt=\"Pineapple ice\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_123444.jpg\" alt=\"Lau nai wah, a Hong Kong-style French toast dessert\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_123627.jpg\" alt=\"Sliced lau nai wah\" />\n</div>\n<p>Neither the tofu pudding nor the pineapple ice was particularly surprising, but both were solid and satisfying.</p>\n<p>The tofu pudding had absolutely no beany smell. It carried a faint soybean fragrance, was smooth and tender, and came with osmanthus honey that was not cloying or harsh on the throat. It was cold, refreshing, and wonderfully thirst-quenching. As a soda, the pineapple ice was barely sweet; nearly all the sweetness came from the melted ice cream and pineapple. Its only flaw was that there was not enough of it to drink with abandon.</p>\n<p>The double-combo rice and char siu pastry were what really surprised me. The moment these two dishes entered my mouth, the only word that came to mind was: satisfaction.</p>\n<p>First, the double-combo rice. The fatty char siu was pleasantly springy, with a rich sauce aroma. A little honey drizzled over it added another layer of flavor. The fat was soft and bouncy rather than greasy enough to coat the mouth unpleasantly. The roast duck had skin as thin as paper; eaten together with the sauce beneath it, it perfectly compensated for the slightly dry texture of the duck meat itself. It was not flawless, though. As the photos show, the plate was covered with a glossy layer of oil mixed with the honey drizzle. By the second half of the meal, it became somewhat rich and difficult for someone like me—who had been eating relatively lightly recently—to digest.</p>\n<p>Now, the honey-glazed char siu pastry. As soon as it arrived, it gave off the buttery, flaky aroma of a freshly baked pastry. Once I bit in, that fragrance filled my nose. Unlike at some restaurants, the honey glaze did not contain undissolved sugar crystals that scraped against the teeth. Perhaps it was because the pastry had just been baked, or perhaps the honey water had been mixed to exactly the right concentration so that it would not crystallize. The meat filling had been cut into tiny pieces. Yet once I pressed it with my tongue, it did not feel like a collection of separate chunks. I suspect they used fatty meat from a cut with little connective tissue, so it did not interfere with the texture. It was deeply satisfying.</p>\n<p>I did not really enjoy the lau nai wah. To me, it was pure sugar-and-fat. After the double-combo rice and char siu pastry, I needed something lighter to cleanse my palate, and ordering it after the meal was a mistake that got in the way of enjoying it. I will keep that in mind next time.</p>\n<p>As the first Michelin restaurant of my life, it left me very satisfied. At around ¥100 per person, it was also affordable. I would like to return one day and try the creator's recommendations.</p>\n<h2>Zhongshan Park</h2>\n<p>After lunch I was incredibly full. My reservation at the provincial museum was not until 3 p.m., so there was still plenty of time. I decided to walk around the nearby Zhongshan Park.</p>\n<div class=\"photo-scroll\">\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_125238.jpg\" alt=\"Northwest gate of the park\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_125707.jpg\" alt=\"Amusement rides in the park\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_130817.jpg\" alt=\"Park facilities\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_130708.jpg\" alt=\"Boats\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_130342.jpg\" alt=\"Statue at the main gate\" />\n</div>\n<p><FloatImage\n  src=\"https://img.markxu.icu/imgIMG_20260719_125853.jpg\"\n  alt=\"A self-service book machine—what a great thing\"\n  side=\"left\"\n  width=\"40%\"\n/></p>\n<p>The trees in the park were extremely thick and tall, suggesting that Zhongshan Park had existed here for a very long time. I was surprised to find a fairly substantial amusement park as well: a small roller coaster, a pirate ship, and several other medium-sized rides, all built in 2023. The classic boating attraction was still there too. It was a case of “old trees growing new branches”—the park retained the steadiness of its history while also feeling lively and fresh.</p>\n<p>After a simple walk through Zhongshan Park to digest lunch, I set off for the Hubei Provincial Museum to finally meet the Sword of Goujian, King of Yue, and the Marquis Yi of Zeng's bronze bells—two treasures I had been looking forward to seeing.</p>\n<h2>Hubei Provincial Museum</h2>\n<p>My reservation was for 3 p.m., but I arrived at 2:20. To give every visitor the best possible experience, the museum <strong>only allows entry up to 20 minutes before the reserved time (be sure to note this!).</strong></p>\n<p>As soon as I entered, I hurried toward the South Hall, where the Sword of Goujian is displayed.</p>\n<p>Even after entering the museum, getting a glimpse of the real sword required a long wait. Only then could I look at it for a few seconds among the crowd surrounding the display case.</p>\n<p><img src=\"https://img.markxu.icu/imgIMG_20260719_150928.jpg\" alt=\"The Sword of Goujian\"></p>\n<AsideNote color=\"yellow\" width=\"large\">\nThe sword is 55.7 centimeters long; the blade is 4.6 centimeters wide; the hilt is 8.4 centimeters long; and it weighs 875 grams. Both sides of the blade bear dark diamond-shaped patterns. The front of the guard is set with blue glaze, while the back has a turquoise pattern. The pommel is a circular ring containing 11 concentric circles. The entire blade was treated with sulfurization, greatly improving its corrosion resistance and toughness. This shows that ancient Chinese metallurgists had reached a remarkably high level of skill in metal smelting and surface treatment. When unearthed, the sword still shone coldly and was extraordinarily sharp. It reflects the advanced sword-making technology of ancient China and is a rare treasure among ancient weapons. —Wikipedia\n</AsideNote>\n<p>The whole sword was only about as long as a forearm—much smaller than I had imagined. It looked a little like an ancient person's collectible figure (although according to the information, it was still a practical weapon). Seeing the real object was different from seeing it on television. We could not make out the details, but the museum had a complete exhibition and introduction devoted to the sword, and I still learned a great deal.</p>\n<p>Next, I headed for the second national treasure: the bronze bells of Marquis Yi of Zeng.</p>\n<p><img src=\"https://img.markxu.icu/imgIMG_20260719_152047.jpg\" alt=\"The bronze bells of Marquis Yi of Zeng\"></p>\n<AsideNote color=\"yellow\" width=\"large\">\nThe set of bells belonging to Marquis Yi of Zeng is enormous and completely intact. The bell frame is tall, formed from two perpendicular wooden structures of different lengths. The longer side lies to the west of the tomb and is 7.48 meters long and 2.65 meters high; the side toward the south is 3.35 meters long and 2.73 meters high. Seven painted wooden beams run through the frame, reinforced at both ends with bronze fittings decorated with coiled dragons. Six cast-bronze warriors wearing swords and eight columns support the entire set of bells, forming upper, middle, and lower tiers. The frame and its hooks number 246 in total. The bronze figures wear long robes, belts around their waists, and solemn expressions. —Wikipedia\n</AsideNote>\n<p>The hall containing the bells was much larger. Its display panels explained the details of Marquis Yi, the excavation, and the other instruments used alongside the bells.</p>\n<p>There were also bronze objects of all sizes around them. Their shapes and details were beyond anything I had imagined. Even with today's technology and methods, I am not sure they could be perfectly reproduced. There were many more national treasures, but here I will simply record a few that impressed me most.</p>\n<div class=\"photo-scroll-vertical\">\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_152607.jpg\" alt=\"The Zun basin of Marquis Yi of Zeng\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_153359.jpg\" alt=\"Bronze lei with coiled-dragon lid and animal-face pattern (léi)\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_154121.jpg\" alt=\"Lacquered leather armor from the Warring States period in Chu\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_155203.jpg\" alt=\"Gold-ringed white-jade gourd ornament\" />\n</div>\n<h2>Huahua Pizza</h2>\n<p>After the museum, I went to another restaurant recommended by the creator mentioned at the beginning: Huahua Pizza.</p>\n<div class=\"photo-scroll\">\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_172040.jpg\" alt=\"Storefront\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_172248.jpg\" alt=\"Inside the restaurant\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20260719_172252.jpg\" alt=\"Inside the restaurant\" />\n</div>\n<p>The decor was simple and attractive, bright and fresh, and the street outside was very clean.</p>\n<p>Since lunch had left me extremely full, I ordered an eight-inch Margherita pizza (¥20) and a serving of fried chicken, the restaurant's top seller.</p>\n<p><FloatImage\n  src=\"https://img.markxu.icu/imgIMG_20260719_173447.jpg\"\n  alt=\"Margherita pizza\"\n  side=\"left\"\n  width=\"45%\"\n/></p>\n<p>The Margherita pizza arrived first. The price was fairly reasonable. (A street cart I often visit in Xiamen sells a nine-inch pizza for the same ¥20, although this restaurant has a much nicer environment.) Overall, it tasted fine—it tasted like a Margherita pizza, and I am not sure what else a Margherita pizza is supposed to do... Let me mention a few disappointments. First, the basil had been baked until slightly bitter and had lost its fresh fragrance, and there was not much of it. I really like the aroma of basil, but I could barely taste it here. Second, although the pizza was made to order, the wheat aroma of the crust was not prominent enough and the crust was not chewy enough. That was a little disappointing.</p>\n<p><FloatImage\n  src=\"https://img.markxu.icu/imgIMG_20260719_174708.jpg\"\n  alt=\"Boneless fried chicken\"\n  side=\"right\"\n  width=\"55%\"\n/></p>\n<p>Next came the restaurant's best-selling boneless fried chicken, which the creator had recommended enthusiastically. Even with expectations that high, the plate still surprised me.</p>\n<p>The crust was not merely crisp; it was coated in enormous bread crumbs, and the crust alone could compete with packaged potato chips. The base seasoning of the meat was balanced: it did not overpower the chicken's own flavor, but you could still taste the care that had gone into seasoning it. More importantly, the chicken inside was full of juice. Dipped into the sauce provided, the entire portion was not greasy even when eaten by one person.</p>\n<p>If you want a satisfying meal on a small budget, this restaurant is genuinely worth visiting.</p>\n<h2>Conclusion</h2>\n<p>Following “氵皮ki酱” led me to two restaurants and showed me that Wuhan has not only a formidable history but also an incredibly rich food scene. My stomach was full, but my heart felt strangely empty—there were still so many recommended restaurants I had not had time to visit and so many sights I had not had time to see.</p>\n<p>But that is all right. This was only the beginning of one of my many weekends in Wuhan. My exam preparation continues, but whenever I have free time, I will keep exploring the city's different sides. After all, life is not only about the hardship immediately before us (11408); it also includes Wuhan's hidden restaurants and the next journey of discovery.</p>\n",
      "summary": "[AI-translated] While preparing in Wuhan for a second attempt at the postgraduate entrance examination, I took a day off on the weekend, followed a local creator's route around the city, visited the Hubei Provincial Museum at last, and ate plenty of good food.",
      "date_published": "2026-07-20T00:00:00.000Z",
      "image": "https://img.markxu.icu/img湖北省博.jpg",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/tcp-slow-start-cwnd-growth-en",
      "url": "https://markxu.icu/blog/tcp-slow-start-cwnd-growth-en",
      "title": "TCP Slow-Start Window Growth: A Three-Level Comparison of Textbooks, RFCs, and the Linux Kernel",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some technical wording may differ from the original; please refer to the cited RFCs and source code for authoritative details. While studying for my exam a few days ago, I came across this question: You can view it here: 2025 exam question The answer's explanation said: Since the current congestion window is smaller than the threshold, the connection is in the slow-start phase. For every ACK received, the congestion window increases by 1 MSS, so it grows from 2000 B to 3000 B. This small detail was different from my long-standing assumption that the congestion window doubles after every RTT. What was going on? As it turned out, I had simply failed to read the textbook carefully 🤪. TCP also uses cumulative acknowledgments, so would that not prevent the window from doubling? With this question in mind, I asked a very capable classmate. Their answer was: The sender grows according to the number of bytes acknowledged, not the number of ACKs. When one ACK acknowledges one MSS, you can also think of it as growing according to the ACK count. The exact implementation depends on the protocol design. Many networking questions describe an idealized situation. This introduced yet another explanation: growth according to acknowledged bytes? I became curious about how actually grows during TCP slow start, so I decided to investigate it myself. Starting with “Doubling Every RTT” Let us begin with the description most familiar from networking textbooks and exam questions. The initial congestion window is usually set to 1 MSS the latest RFCs allow more MSS . The slow-start algorithm says that for every acknowledgment of a new segment, increases by 1. In this way, after each transmission round—one round-trip time, or RTT— doubles, producing exponential growth as transmission rounds pass. To prevent from growing without limit, a slow-start threshold called is also set. When grows to , slow start stops and the congestion-avoidance algorithm takes over. It can be summarized like this: During slow start: for every ACK, . If every data segment in one RTT triggers its own ACK, roughly doubles by the end of that RTT. During congestion avoidance: growth slows to approximately 1 MSS per RTT. Assume that the threshold is 16. The example looks like this: Round Initial cwnd ACKs received during one RTT cwnd at the end of the RTT ----- ------------ ---------------------------- ------------------------------- 0 1 MSS 1 2 MSS 1 2 MSS 2 4 MSS 2 4 MSS 4 8 MSS 3 8 MSS 8 16 MSS The slow start algorithm is used when cwnd ssthresh. When cwnd and ssthresh are equal, the sender may use either slow start or congestion avoidance. During slow start, a TCP increments cwnd by at most SMSS bytes for each ACK received that cumulatively acknowledges new data. Slow start ends when cwnd exceeds ssthresh or, optionally, when it reaches it, as noted above or when congestion is observed. While traditionally TCP implementations have increased cwnd by precisely SMSS bytes upon receipt of an ACK covering new data, we RECOMMEND that TCP implementations increase cwnd, per: cwnd += min N, SMSS 2 where N is the number of previously unacknowledged bytes acknowledged in the incoming ACK. This adjustment is part of Appropriate Byte Counting RFC3465 and provides robustness against misbehaving receivers that may attempt to induce a sender to artificially inflate cwnd using a mechanism known as \"ACK Division\" SCWA99 . ACK Division consists of a receiver sending multiple ACKs for a single TCP data segment, each acknowledging only a portion of its data. A TCP that increments cwnd by SMSS for each such ACK will inappropriately inflate the amount of data injected into the network. The core meaning of Section 3.1 of RFC 5681 is: During slow start, for each ACK that cumulatively acknowledges new data, TCP increases by the number of acknowledged bytes, but by no more than the sender's MSS bytes. This can be broken down into three key phrases: : an ACK is the trigger. : the ACK must cumulatively acknowledge new data. : is the number of bytes newly acknowledged by the ACK, capped at the sender's MSS. Why Is It Designed This Way? The original text explains the reason as well: This adjustment is part of Appropriate Byte Counting ABC, RFC 3465 , which makes TCP more robust against misbehaving receivers. An abnormal receiver might use a mechanism called ACK Division to induce the sender to inflate . In ACK Division, the receiver sends multiple ACKs for one TCP data segment, with each ACK acknowledging only a portion of that segment. If a TCP implementation increases by SMSS for every such ACK, it will inappropriately increase the amount of data injected into the network. In short, RFC 5681 wants to prevent the receiver from tricking the sender into expanding its congestion window by “splitting ACKs.” Suppose the sender transmits one data segment of size . Normally, the receiver should acknowledge the segment with one ACK. During slow start, the sender then increases by 1 SMSS, which is fine. But if the receiver deliberately splits the acknowledgment into several small ACKs: If the sender still follows the rule “increase by 1 SMSS for every ACK,” the same data segment will trigger multiple window increases. Only one segment was successfully transmitted through the network, but the sender behaves as though it can inject a great deal more data. is therefore inflated abnormally. This is why RFC 5681 recommends: The meaning is: If an ACK acknowledges only a small amount of new data, increases only by that amount. If an ACK acknowledges a large amount of data, the increase for that ACK is still capped at . This preserves the normal slow-start behavior—one fully acknowledged MSS produces roughly one MSS of growth—while preventing ACK Division from maliciously inflating the window. However, this is only the rule described by RFC 5681 / RFC 3465 from a byte-counting perspective. When we later look at the Linux kernel implementation, we will see a different viewpoint: Linux does not directly increase the window according to the number of newly acknowledged bytes. It counts the number of “fully acknowledged segments.” That difference is the most interesting part of this article. Linux Kernel Implementation: Why Does It Look Different Again? The source is in the Linux kernel s ; search for . Here is the core function and the comment immediately before it: The comment contains a great deal of information. Here is a copy version so we can see it more clearly: Slow start is used when the congestion window is no greater than the slow-start threshold. Linux is based on RFC 2581 and handles stretch ACKs correctly. Linux does not strictly implement Appropriate Byte Counting ABC from RFC 3465. Instead, it uses another approach: a packet is counted as acknowledged only when it has been fully ACKed or SACKed. This defends against the ACK attacks described in the RFC. For a stretch ACK with degree N, Linux's slow start treats it as though N ordinary ACKs of degree 1 had arrived back to back. ABC, however, caps N at 2. When this growth would make cross , slow start stops at the threshold and returns the unused ACK count to congestion avoidance so that congestion avoidance can continue adjusting the window. Several terms need explanation first: : Selective Acknowledgment, which tells the sender which non-contiguous data blocks have already arrived. : the notation used in the kernel comment, meaning ACKed or SACKed. : an ACK that acknowledges multiple segments at once instead of only one. For example, a receiver using delayed acknowledgment may wait for two or more TCP segments before sending one ACK. : this stretch ACK acknowledges N complete segments at once. Is Not Measured in Bytes The most important detail is that in this Linux function does not mean “how many bytes were acknowledged.” It means “how many complete segments were acknowledged.” RFC 5681 / RFC 3465 tends to describe the rule in bytes: Here, is the number of bytes newly acknowledged by the ACK. But the implementation here is closer to: Here, is the number of newly acknowledged complete segments. Linux itself maintains in segment units, so does not add a byte count directly to the window; it adds the number of complete segments acknowledged. This explains why the kernel code looks different from the RFC formula: RFC discussions often describe growth in bytes. Linux's here is the number of fully acknowledged data segments. Therefore, means that the window grows by the number of complete segments. Under ideal conditions, this is equivalent to the textbook rule “for every ACK, .” If an ACK acknowledges exactly one complete MSS, then and the window increases by one segment. Why Does This Also Prevent ACK Division? Linux's defense is different from the RFC's. It does not treat a fraction of a segment as one effective “complete-segment acknowledgment.” Only when a packet is fully acknowledged does it count toward . Therefore, even if a malicious receiver splits one segment into multiple small ACKs, cannot become multiple complete segments. That is what the comment means when it says: Linux does not strictly implement ABC, but does something “better.” “Better” here can be understood as defending against ACK Division at the granularity of complete-segment acknowledgments rather than at byte granularity. This can provide higher throughput, avoids floating-point and byte-level calculations, and transitions more smoothly into congestion avoidance. The code walkthrough below will explain that transition line by line. Linux Is More Aggressive with Stretch ACKs Now consider a stretch ACK. Suppose one ACK acknowledges three complete segments at once. Linux treats it as though three ordinary ACKs arrived consecutively. RFC 3465's ABC, by contrast, limits a single increase during slow start; its experimental recommendation says that should not exceed . Therefore, under a stretch ACK, Linux is somewhat more aggressive than ABC. This also explains the comment: Linux treats a stretch ACK that acknowledges N segments as N ordinary ACKs arriving back to back, while ABC limits that number to 2. The Code in Detail: Reading Line by Line Let us return to the function: First, several variables: : the control block for the current TCP connection, containing the connection's congestion-control state. : the current sender congestion window, measured in complete segments. : the slow-start threshold. Slow start grows only until approximately this threshold, after which congestion avoidance takes over. : the hard upper limit for the congestion window, preventing from exceeding the maximum allowed by the system. : the number of complete segments newly acknowledged by this ACK. The first line: calculates how far the window can grow during slow start: In other words, during slow start the window really does increase according to , but it cannot grow beyond . The second line: calculates how much of this ACK's acknowledgment count has already been consumed by slow start. is the amount by which slow start actually increased the window this time. Subtracting that amount from leaves the acknowledgment count that slow start did not use. What Happens When Is Reached? Consider this example: Without the threshold, the window could grow from 8 to 13. But slow start can only grow as far as : So slow start consumes only 2 of the 5 acknowledged segments: The third line: actually updates in the TCP control block. The value is also clamped against , ensuring that the window does not exceed the system's configured upper limit. The last line: returns the part of that slow start did not consume. If slow start has not reached , the function usually returns 0, meaning that the entire acknowledgment count was used to grow the slow-start window. If this ACK crosses the slow-start threshold, the function returns the remaining so that the congestion-avoidance logic can use it. This prevents the portion of the same ACK that exceeds the slow-start threshold from being wasted; it is handed smoothly to congestion avoidance instead. The Linux code can therefore be summarized as: This is why the formulas can look different after reading the RFC and then the Linux source: RFC 5681 / RFC 3465 describe the rule mainly in terms of acknowledged bytes, while Linux implements this function in terms of complete segments. One Slow Start, Three Descriptions The following table summarizes the comparison: Dimension Textbook model RFC 5681 / RFC 3465 Linux kernel ------------------------------------ -------------------------------------------- ----------------------------------------------------------------------------- ------------------------------------------------ Core viewpoint Build intuition Define protocol boundaries Engineering implementation Growth basis Number of ACKs Bytes newly acknowledged by an ACK, Complete segments newly acknowledged, Slow-start formula Each ACK for a segment gives roughly Unit Usually abstracted in MSS Bytes Complete segments Stretch ACK acknowledging 3 segments Usually not discussed RFC 5681: at most ; RFC 3465 ABC can tune but not beyond Approximately segments ACK Division defense Not covered Limit growth with Count only fully acknowledged segments in Ideal one-ACK-per-packet case Roughly doubles within one RTT Roughly doubles within one RTT Roughly doubles within one RTT As we can see, the textbook formula and the Linux implementation are equivalent for the idealized slow-start case. But there are many details and design considerations behind that similarity. If not for a classmate's one-sentence question, I might never have learned about them. If you know more about this topic, feel free to share it in the comments! If you want to run an experiment yourself, you can refer to this Linux cwnd experiment. References RFC 5681: TCP Congestion Control RFC 3465: TCP Congestion Control with Appropriate Byte Counting Linux kernel: net/ipv4/tcp cong.c NJU Linux mirror Linux cwnd experiment Further reading: The evolution of TCP congestion control: from Reno to BBR An in-depth analysis of TCP BBR congestion control",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some technical wording may differ from the original; please refer to the cited RFCs and source code for authoritative details.</p>\n</blockquote>\n<p>While studying for my exam a few days ago, I came across this question:</p>\n<p><img src=\"https://img.markxu.icu/img2025408.png\" alt=\"2025 computer-networking exam question\"></p>\n<p>You can view it here: <a href=\"https://www.408os.cn/exercise?year=2025&amp;questionId=790\">2025 exam question</a></p>\n<p>The answer's explanation said:</p>\n<blockquote>\n<p>Since the current congestion window is smaller than the threshold, the connection is in the slow-start phase. For every ACK received, the congestion window increases by 1 MSS, so it grows from 2000 B to 3000 B.</p>\n</blockquote>\n<p>This small detail was different from my long-standing assumption that the congestion window doubles after every RTT. What was going on? (As it turned out, I had simply failed to read the textbook carefully 🤪.) TCP also uses cumulative acknowledgments, so would that not prevent the window from doubling?</p>\n<p>With this question in mind, I asked a very capable classmate. Their answer was:</p>\n<blockquote>\n<p>The sender grows <code>cwnd</code> according to the number of bytes acknowledged, not the number of ACKs. (When one ACK acknowledges one MSS, you can also think of it as growing <code>cwnd</code> according to the ACK count.) The exact implementation depends on the protocol design. Many networking questions describe an idealized situation.</p>\n</blockquote>\n<p>This introduced yet another explanation: growth according to acknowledged bytes? I became curious about how <code>cwnd</code> actually grows during TCP slow start, so I decided to investigate it myself.</p>\n<hr>\n<h2>Starting with “Doubling Every RTT”</h2>\n<p>Let us begin with the description most familiar from networking textbooks and exam questions.</p>\n<p>The initial congestion window <code>cwnd</code> is usually set to 1 MSS (the latest RFCs allow more MSS). The slow-start algorithm says that for every acknowledgment of a new segment, <code>cwnd</code> increases by 1. In this way, after each transmission round—one round-trip time, or RTT—<code>cwnd</code> doubles, producing exponential growth as transmission rounds pass. To prevent <code>cwnd</code> from growing without limit, a slow-start threshold called <code>ssthresh</code> is also set. When <code>cwnd</code> grows to <code>ssthresh</code>, slow start stops and the congestion-avoidance algorithm takes over.</p>\n<p>It can be summarized like this:</p>\n<ul>\n<li>During slow start: for every ACK, <code>cwnd += 1 MSS</code>.</li>\n<li>If every data segment in one RTT triggers its own ACK, <code>cwnd</code> roughly doubles by the end of that RTT.</li>\n<li>During congestion avoidance: growth slows to approximately 1 MSS per RTT.</li>\n</ul>\n<p>Assume that the threshold is 16. The example looks like this:</p>\n<!-- table-widths: 10%, 24%, 28%, 38% -->\n<table>\n<thead>\n<tr>\n<th>Round</th>\n<th>Initial cwnd</th>\n<th>ACKs received during one RTT</th>\n<th>cwnd at the end of the RTT</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1 MSS</td>\n<td>1</td>\n<td>2 MSS</td>\n</tr>\n<tr>\n<td>1</td>\n<td>2 MSS</td>\n<td>2</td>\n<td>4 MSS</td>\n</tr>\n<tr>\n<td>2</td>\n<td>4 MSS</td>\n<td>4</td>\n<td>8 MSS</td>\n</tr>\n<tr>\n<td>3</td>\n<td>8 MSS</td>\n<td>8</td>\n<td>16 MSS &lt;-(threshold reached)</td>\n</tr>\n<tr>\n<td>4</td>\n<td>16 MSS</td>\n<td>16</td>\n<td>17 MSS &lt;-(congestion avoidance)</td>\n</tr>\n</tbody>\n</table>\n<p>So <code>cwnd</code> grows exponentially with each RTT. Even though modern TCP uses cumulative acknowledgments, its growth trend is still approximately exponential.</p>\n<h2>RFC 5681: How the Standard Defines Slow Start</h2>\n<p>You can find the <a href=\"https://www.rfc-editor.org/info/rfc5681/#section-2:~:text=The%20slow%20start%20algorithm%20is%20used%20when,amount%20of%20data%20injected%20into%20the%20network.\">original text here</a>.</p>\n<blockquote>\n<p>The slow start algorithm is used when cwnd &lt; ssthresh, while the congestion avoidance algorithm is used when cwnd &gt; ssthresh. When cwnd and ssthresh are equal, the sender may use either slow start or congestion avoidance.</p>\n<p>During slow start, a TCP increments cwnd by at most SMSS bytes for each ACK received that cumulatively acknowledges new data. Slow start ends when cwnd exceeds ssthresh (or, optionally, when it reaches it, as noted above) or when congestion is observed. While traditionally TCP implementations have increased cwnd by precisely SMSS bytes upon receipt of an ACK covering new data, we RECOMMEND that TCP implementations increase cwnd, per:</p>\n<p>cwnd += min (N, SMSS) (2)</p>\n<p>where N is the number of previously unacknowledged bytes acknowledged in the incoming ACK. This adjustment is part of Appropriate Byte Counting [RFC3465] and provides robustness against misbehaving receivers that may attempt to induce a sender to artificially inflate cwnd using a mechanism known as &quot;ACK Division&quot; [SCWA99]. ACK Division consists of a receiver sending multiple ACKs for a single TCP data segment, each acknowledging only a portion of its data. A TCP that increments cwnd by SMSS for each such ACK will inappropriately inflate the amount of data injected into the network.</p>\n</blockquote>\n<p>The core meaning of Section 3.1 of RFC 5681 is:</p>\n<blockquote>\n<p>During slow start, for each ACK that cumulatively acknowledges new data, TCP increases <code>cwnd</code> by the number of acknowledged bytes, but by no more than <code>SMSS</code> (the sender's MSS) bytes.</p>\n</blockquote>\n<p>This can be broken down into three key phrases:</p>\n<ul>\n<li><code>for each ACK received</code>: an ACK is the trigger.</li>\n<li><code>cumulatively acknowledges new data</code>: the ACK must cumulatively acknowledge new data.</li>\n<li><code>cwnd += min (N, SMSS)</code>: <code>N</code> is the number of bytes newly acknowledged by the ACK, capped at the sender's MSS.</li>\n</ul>\n<h3>Why Is It Designed This Way?</h3>\n<p>The original text explains the reason as well:</p>\n<blockquote>\n<p>This adjustment is part of Appropriate Byte Counting (ABC, RFC 3465), which makes TCP more robust against misbehaving receivers. An abnormal receiver might use a mechanism called ACK Division to induce the sender to inflate <code>cwnd</code>. In ACK Division, the receiver sends multiple ACKs for one TCP data segment, with each ACK acknowledging only a portion of that segment. If a TCP implementation increases <code>cwnd</code> by SMSS for every such ACK, it will inappropriately increase the amount of data injected into the network.</p>\n</blockquote>\n<p>In short, RFC 5681 wants to prevent the receiver from tricking the sender into expanding its congestion window by “splitting ACKs.”</p>\n<p>Suppose the sender transmits one data segment of size <code>SMSS</code>. Normally, the receiver should acknowledge the segment with one ACK. During slow start, the sender then increases <code>cwnd</code> by 1 SMSS, which is fine.</p>\n<p>But if the receiver deliberately splits the acknowledgment into several small ACKs:</p>\n<pre><code class=\"language-text\">ACK 1: acknowledges the first 200 bytes\nACK 2: acknowledges the first 400 bytes\nACK 3: acknowledges the first 600 bytes\n...\n</code></pre>\n<p>If the sender still follows the rule “increase <code>cwnd</code> by 1 SMSS for every ACK,” the same data segment will trigger multiple window increases. Only one segment was successfully transmitted through the network, but the sender behaves as though it can inject a great deal more data. <code>cwnd</code> is therefore inflated abnormally.</p>\n<p>This is why RFC 5681 recommends:</p>\n<pre><code class=\"language-text\">cwnd += min(N, SMSS)\n</code></pre>\n<p>The meaning is:</p>\n<ul>\n<li>If an ACK acknowledges only a small amount of new data, <code>cwnd</code> increases only by that amount.</li>\n<li>If an ACK acknowledges a large amount of data, the increase for that ACK is still capped at <code>1 SMSS</code>.</li>\n</ul>\n<p>This preserves the normal slow-start behavior—one fully acknowledged MSS produces roughly one MSS of growth—while preventing ACK Division from maliciously inflating the window.</p>\n<p>However, this is only the rule described by RFC 5681 / RFC 3465 from a byte-counting perspective. When we later look at the Linux kernel implementation, we will see a different viewpoint: Linux does not directly increase the window according to the number of newly acknowledged bytes. It counts the number of “fully acknowledged segments.” That difference is the most interesting part of this article.</p>\n<hr>\n<h2>Linux Kernel Implementation: Why Does It Look Different Again?</h2>\n<p>The source is in <a href=\"https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_cong.c\">the Linux kernel's <code>net/ipv4/tcp_cong.c</code></a>; search for <code>tcp_slow_start()</code>.</p>\n<p>Here is the core function and the comment immediately before it:</p>\n<pre><code class=\"language-c\">/* Slow start is used when congestion window is no greater than the slow start\n * threshold. We base on RFC2581 and also handle stretch ACKs properly.\n * We do not implement RFC3465 Appropriate Byte Counting (ABC) per se but\n * something better;) a packet is only considered (s)acked in its entirety to\n * defend the ACK attacks described in the RFC. Slow start processes a stretch\n * ACK of degree N as if N acks of degree 1 are received back to back except\n * ABC caps N to 2. Slow start exits when cwnd grows over ssthresh and\n * returns the leftover acks to adjust cwnd in congestion avoidance mode.\n */\n__bpf_kfunc u32 tcp_slow_start(struct tcp_sock *tp, u32 acked)\n{\n\tu32 cwnd = min(tcp_snd_cwnd(tp) + acked, tp-&gt;snd_ssthresh);\n\n\tacked -= cwnd - tcp_snd_cwnd(tp);\n\ttcp_snd_cwnd_set(tp, min(cwnd, tp-&gt;snd_cwnd_clamp));\n\n\treturn acked;\n}\n</code></pre>\n<p>The comment contains a great deal of information. Here is a copy version so we can see it more clearly:</p>\n<blockquote>\n<p>Slow start is used when the congestion window is no greater than the slow-start threshold. Linux is based on RFC 2581 and handles stretch ACKs correctly.</p>\n<p>Linux does not strictly implement Appropriate Byte Counting (ABC) from RFC 3465. Instead, it uses another approach: a packet is counted as acknowledged only when it has been fully ACKed or SACKed. This defends against the ACK attacks described in the RFC.</p>\n<p>For a stretch ACK with degree N, Linux's slow start treats it as though N ordinary ACKs of degree 1 had arrived back to back. ABC, however, caps N at 2.</p>\n<p>When this growth would make <code>cwnd</code> cross <code>ssthresh</code>, slow start stops at the threshold and returns the unused ACK count to congestion avoidance so that congestion avoidance can continue adjusting the window.</p>\n</blockquote>\n<p>Several terms need explanation first:</p>\n<ul>\n<li><code>SACK</code>: Selective Acknowledgment, which tells the sender which non-contiguous data blocks have already arrived.</li>\n<li><code>(s)acked</code>: the notation used in the kernel comment, meaning ACKed or SACKed.</li>\n<li><code>stretch ACK</code>: an ACK that acknowledges multiple segments at once instead of only one. For example, a receiver using delayed acknowledgment may wait for two or more TCP segments before sending one ACK.</li>\n<li><code>degree N</code>: this stretch ACK acknowledges N complete segments at once.</li>\n</ul>\n<h3><code>acked</code> Is Not Measured in Bytes</h3>\n<p>The most important detail is that <code>acked</code> in this Linux function does not mean “how many bytes were acknowledged.” It means “how many complete segments were acknowledged.”</p>\n<p>RFC 5681 / RFC 3465 tends to describe the rule in bytes:</p>\n<pre><code class=\"language-text\">cwnd += min(N, SMSS)\n</code></pre>\n<p>Here, <code>N</code> is the number of bytes newly acknowledged by the ACK.</p>\n<p>But the implementation here is closer to:</p>\n<pre><code class=\"language-text\">cwnd += acked\n</code></pre>\n<p>Here, <code>acked</code> is the number of newly acknowledged complete segments. Linux itself maintains <code>snd_cwnd</code> in segment units, so <code>+ acked</code> does not add a byte count directly to the window; it adds the number of complete segments acknowledged.</p>\n<p>This explains why the kernel code looks different from the RFC formula:</p>\n<ul>\n<li>RFC discussions often describe <code>cwnd</code> growth in bytes.</li>\n<li>Linux's <code>acked</code> here is the number of fully acknowledged data segments.</li>\n<li>Therefore, <code>tcp_snd_cwnd(tp) + acked</code> means that the window grows by the number of complete segments.</li>\n</ul>\n<p>Under ideal conditions, this is equivalent to the textbook rule “for every ACK, <code>cwnd += 1 MSS</code>.” If an ACK acknowledges exactly one complete MSS, then <code>acked = 1</code> and the window increases by one segment.</p>\n<h3>Why Does This Also Prevent ACK Division?</h3>\n<p>Linux's defense is different from the RFC's. It does not treat a fraction of a segment as one effective “complete-segment acknowledgment.” Only when a packet is fully acknowledged does it count toward <code>acked</code>. Therefore, even if a malicious receiver splits one segment into multiple small ACKs, <code>acked</code> cannot become multiple complete segments.</p>\n<p>That is what the comment means when it says:</p>\n<blockquote>\n<p>Linux does not strictly implement ABC, but does something “better.”</p>\n</blockquote>\n<p>“Better” here can be understood as defending against ACK Division at the granularity of complete-segment acknowledgments rather than at byte granularity. This can provide higher throughput, avoids floating-point and byte-level calculations, and transitions more smoothly into congestion avoidance. The code walkthrough below will explain that transition line by line.</p>\n<h3>Linux Is More Aggressive with Stretch ACKs</h3>\n<p>Now consider a stretch ACK.</p>\n<p>Suppose one ACK acknowledges three complete segments at once. Linux treats it as though three ordinary ACKs arrived consecutively. RFC 3465's ABC, by contrast, limits a single increase during slow start; its experimental recommendation says that <code>L</code> should not exceed <code>2*SMSS</code>. Therefore, under a stretch ACK, Linux is somewhat more aggressive than ABC.</p>\n<p>This also explains the comment:</p>\n<blockquote>\n<p>Linux treats a stretch ACK that acknowledges N segments as N ordinary ACKs arriving back to back, while ABC limits that number to 2.</p>\n</blockquote>\n<h2>The Code in Detail: Reading <code>tcp_slow_start()</code> Line by Line</h2>\n<p>Let us return to the function:</p>\n<pre><code class=\"language-c\">__bpf_kfunc u32 tcp_slow_start(struct tcp_sock *tp, u32 acked)\n{\n\tu32 cwnd = min(tcp_snd_cwnd(tp) + acked, tp-&gt;snd_ssthresh);\n\n\tacked -= cwnd - tcp_snd_cwnd(tp);\n\ttcp_snd_cwnd_set(tp, min(cwnd, tp-&gt;snd_cwnd_clamp));\n\n\treturn acked;\n}\n</code></pre>\n<p>First, several variables:</p>\n<ul>\n<li><code>tp</code>: the control block for the current TCP connection, containing the connection's congestion-control state.</li>\n<li><code>tcp_snd_cwnd(tp)</code>: the current sender congestion window, measured in complete segments.</li>\n<li><code>tp-&gt;snd_ssthresh</code>: the slow-start threshold. Slow start grows only until approximately this threshold, after which congestion avoidance takes over.</li>\n<li><code>tp-&gt;snd_cwnd_clamp</code>: the hard upper limit for the congestion window, preventing <code>cwnd</code> from exceeding the maximum allowed by the system.</li>\n<li><code>acked</code>: the number of complete segments newly acknowledged by this ACK.</li>\n</ul>\n<p>The first line:</p>\n<pre><code class=\"language-c\">u32 cwnd = min(tcp_snd_cwnd(tp) + acked, tp-&gt;snd_ssthresh);\n</code></pre>\n<p>calculates how far the window can grow during slow start:</p>\n<pre><code class=\"language-text\">new cwnd = min(current cwnd + newly acknowledged complete segments, slow-start threshold)\n</code></pre>\n<p>In other words, during slow start the window really does increase according to <code>acked</code>, but it cannot grow beyond <code>ssthresh</code>.</p>\n<p>The second line:</p>\n<pre><code class=\"language-c\">acked -= cwnd - tcp_snd_cwnd(tp);\n</code></pre>\n<p>calculates how much of this ACK's acknowledgment count has already been consumed by slow start.</p>\n<p><code>cwnd - tcp_snd_cwnd(tp)</code> is the amount by which slow start actually increased the window this time. Subtracting that amount from <code>acked</code> leaves the acknowledgment count that slow start did not use.</p>\n<h3>What Happens When <code>ssthresh</code> Is Reached?</h3>\n<p>Consider this example:</p>\n<pre><code class=\"language-text\">current cwnd = 8\nssthresh = 10\nacked = 5\n</code></pre>\n<p>Without the threshold, the window could grow from 8 to 13.</p>\n<p>But slow start can only grow as far as <code>ssthresh = 10</code>:</p>\n<pre><code class=\"language-text\">actual new cwnd = min(8 + 5, 10) = 10\n</code></pre>\n<p>So slow start consumes only 2 of the 5 acknowledged segments:</p>\n<pre><code class=\"language-text\">actual growth = 10 - 8 = 2\nremaining acked = 5 - 2 = 3\n</code></pre>\n<p>The third line:</p>\n<pre><code class=\"language-c\">tcp_snd_cwnd_set(tp, min(cwnd, tp-&gt;snd_cwnd_clamp));\n</code></pre>\n<p>actually updates <code>snd_cwnd</code> in the TCP control block. The value is also clamped against <code>snd_cwnd_clamp</code>, ensuring that the window does not exceed the system's configured upper limit.</p>\n<p>The last line:</p>\n<pre><code class=\"language-c\">return acked;\n</code></pre>\n<p>returns the part of <code>acked</code> that slow start did not consume.</p>\n<p>If slow start has not reached <code>ssthresh</code>, the function usually returns 0, meaning that the entire acknowledgment count was used to grow the slow-start window.</p>\n<p>If this ACK crosses the slow-start threshold, the function returns the remaining <code>acked</code> so that the congestion-avoidance logic can use it. This prevents the portion of the same ACK that exceeds the slow-start threshold from being wasted; it is handed smoothly to congestion avoidance instead.</p>\n<p>The Linux code can therefore be summarized as:</p>\n<pre><code class=\"language-text\">During slow start:\n  cwnd grows by the number of newly acknowledged complete segments, acked;\n  but it cannot grow beyond ssthresh;\n  if this ACK reaches ssthresh, the leftover acked is passed to congestion avoidance.\n</code></pre>\n<p>This is why the formulas can look different after reading the RFC and then the Linux source: RFC 5681 / RFC 3465 describe the rule mainly in terms of acknowledged bytes, while Linux implements this function in terms of complete segments.</p>\n<hr>\n<h2>One Slow Start, Three Descriptions</h2>\n<p>The following table summarizes the comparison:</p>\n<!-- table-widths: 20%, 26%, 27%, 27% -->\n<table>\n<thead>\n<tr>\n<th>Dimension</th>\n<th>Textbook model</th>\n<th>RFC 5681 / RFC 3465</th>\n<th>Linux kernel</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Core viewpoint</td>\n<td>Build intuition</td>\n<td>Define protocol boundaries</td>\n<td>Engineering implementation</td>\n</tr>\n<tr>\n<td>Growth basis</td>\n<td>Number of ACKs</td>\n<td>Bytes newly acknowledged by an ACK,<code>N</code></td>\n<td>Complete segments newly acknowledged,<code>acked</code></td>\n</tr>\n<tr>\n<td>Slow-start formula</td>\n<td>Each ACK for a segment gives roughly<code>+1 MSS</code></td>\n<td><code>cwnd += min(N, SMSS)</code></td>\n<td><code>cwnd += acked</code></td>\n</tr>\n<tr>\n<td>Unit</td>\n<td>Usually abstracted in MSS</td>\n<td>Bytes</td>\n<td>Complete segments</td>\n</tr>\n<tr>\n<td>Stretch ACK acknowledging 3 segments</td>\n<td>Usually not discussed</td>\n<td>RFC 5681: at most<code>+1 SMSS</code>; RFC 3465 ABC can tune <code>L</code> but not beyond <code>2*SMSS</code></td>\n<td>Approximately<code>+3</code> segments</td>\n</tr>\n<tr>\n<td>ACK Division defense</td>\n<td>Not covered</td>\n<td>Limit growth with<code>min(N, SMSS)</code></td>\n<td>Count only fully acknowledged segments in<code>acked</code></td>\n</tr>\n<tr>\n<td>Ideal one-ACK-per-packet case</td>\n<td>Roughly doubles within one RTT</td>\n<td>Roughly doubles within one RTT</td>\n<td>Roughly doubles within one RTT</td>\n</tr>\n</tbody>\n</table>\n<p>As we can see, the textbook formula and the Linux implementation are equivalent for the idealized slow-start case. But there are many details and design considerations behind that similarity. If not for a classmate's one-sentence question, I might never have learned about them. If you know more about this topic, feel free to share it in the comments!</p>\n<p>If you want to run an experiment yourself, you can refer to this <a href=\"https://oneuptime.com/blog/post/2026-03-20-monitor-tcp-cwnd-real-time/view\">Linux cwnd experiment</a>.</p>\n<h2>References</h2>\n<ul>\n<li><a href=\"https://www.rfc-editor.org/rfc/rfc5681\">RFC 5681: TCP Congestion Control</a></li>\n<li><a href=\"https://www.rfc-editor.org/rfc/rfc3465\">RFC 3465: TCP Congestion Control with Appropriate Byte Counting</a></li>\n<li><a href=\"https://github.com/torvalds/linux/blob/master/net/ipv4/tcp_cong.c\">Linux kernel: net/ipv4/tcp_cong.c</a></li>\n<li><a href=\"https://git.nju.edu.cn/nju/linux\">NJU Linux mirror</a></li>\n<li><a href=\"https://oneuptime.com/blog/post/2026-03-20-monitor-tcp-cwnd-real-time/view\">Linux cwnd experiment</a></li>\n<li>Further reading:</li>\n<li><a href=\"https://blog.mickeyzzc.tech/posts/network/tcp-congestion-control-evolution/\">The evolution of TCP congestion control: from Reno to BBR</a></li>\n<li><a href=\"https://blog.csdn.net/rjc_lihui/article/details/138302213\">An in-depth analysis of TCP BBR congestion control</a></li>\n</ul>\n",
      "summary": "[AI-translated] TCP slow start is often simplified as “the congestion window doubles every RTT,” but textbooks, RFCs, and the Linux kernel describe the same mechanism from different layers. I compare the three views to clarify whether cwnd grows by ACKs, bytes, or complete segments.",
      "date_published": "2026-07-06T00:00:00.000Z",
      "image": "https://img.markxu.icu/img2025408.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/behind-localhost-en",
      "url": "https://markxu.icu/blog/behind-localhost-en",
      "title": "Behind localhost: A Network Journey That Never Leaves Home",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. Starting with a Familiar Address Type in a terminal and Vite cheerfully tells you that the service is running at ; click in a browser, and the page appears. Or, when working with Redis, you might run , which also connects to the local machine. We know this scene very well. Recently, however, while reviewing computer networks for the 408 exam, I started studying network layering again. That raised a natural question that is easy to overlook: If I am connecting to “myself,” does this still count as network communication? Does it really pass through the network protocol stack? If so, how many layers does it go through? A little investigation produced an answer that was slightly counterintuitive: yes, it counts, and it goes through quite a few layers. I wanted to write down the process from top to bottom and record what happens behind . In simple terms, it does not pass through a network card, switch, router, or cable the way an external request does. But inside the operating system, the application, transport, and network layers still do their jobs seriously. I used to think of as “one program directly chatting with another,” but it is more like a parcel that never leaves its residential compound: it never gets on the highway, but the shipping label, sorting, and delivery process still happen. One clarification first: is a name, not an IP address. It usually resolves to the IPv4 loopback address ; some systems may prefer the IPv6 address . To keep the route clear, the discussion below assumes the IPv4 loopback path through . Preface: Network Layering Before everything begins, let us briefly review the network layers from the 408 curriculum so that we have a framework for the analysis that follows. A short story first: The network layers are shown above. In 1977, the International Organization for Standardization ISO established a special committee to study a standard framework for network interconnection. It hoped to create a universal, open network-layering model so that any systems following the standard could communicate with one another. The reference model was formally published in 1984. It is the most standardized layering model, and almost any network can be fitted into it. The story of TCP/IP begins with the Advanced Research Projects Agency ARPA of the United States Department of Defense. In 1969, ARPANET was born. It was the predecessor of the modern Internet, initially intended to connect computers at universities and research institutions for resource sharing. In 1974, Vint Cerf and Bob Kahn published a paper on TCP. It introduced the concept of a “gateway”—which later evolved into the router—and solved the problem of connecting different networks. On January 1, 1983, ARPANET officially switched its core protocol from NCP to TCP/IP. This day is often regarded as the “birthday” of the modern Internet. Berkeley later integrated a free implementation of TCP/IP into Unix through BSD Unix. As Unix spread through universities and research institutions, TCP/IP quickly spread with it. Because TCP/IP was simpler, more efficient, and cheaper to implement, it naturally replaced the OSI approach and became today's dominant network-layering model. We will use the TCP/IP layers for the detailed discussion below. Before continuing, here is a quick summary of what the five TCP/IP layers do: The application layer is responsible for “what to say,” such as the HTTP, Redis, and MySQL protocols. The transport layer is responsible for “which process receives it and how it is delivered.” TCP and UDP live here, as do port numbers. The network layer is responsible for “which address to go to.” IP belongs here, so is a network-layer address. The link layer handles transmission over the same link. Ethernet frames and MAC addresses generally appear here. The physical layer turns bits into electrical, optical, or wireless signals and actually moves data through physical media. Based on that description, we can already analyze : is a port number belonging to the transport layer, while is an IP address belonging to the network layer. Once these two concepts are separated, many networking questions become much less confusing. TL;DR Layer What happens Key point --------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------ Application layer The browser or client sends an HTTP request to . This is where the data starts. Transport layer The operating system uses port to find the corresponding service process, such as your web server, and encapsulates the data in a TCP segment. The port number takes effect here. Network layer The operating system sees that the destination IP is , recognizes it as a loopback address , and hands it directly to the loopback interface instead of a physical network card. This is the crucial step: the data never leaves the machine. Link layer The loopback interface simulates a virtual link layer and pretends to send the data over a network, but in reality it only makes an in-memory turn. There is no real MAC-address exchange. Physical layer Skipped. No real hardware is involved, so the data is never turned into electrical or optical signals. This is the fundamental reason localhost is much faster than an external connection. So localhost actually completes most of the process; it simply skips the physical layer. Application Layer — No DNS Lookup Let us begin at the top and see what the first step of does. Experiment: External Host vs. Localhost In a terminal, I ran two commands: one accessing an external domain and one accessing my local service: I captured the traffic with tcpdump: When accessing the external domain, the terminal showed output like this: When accessing the local service, however, nothing appeared. That is because is not an ordinary domain name. Under the IETF standard RFC 6761 , it is reserved as a special hostname. When the operating system handles , it does not issue a DNS query; it reads the mapping directly from instead. You can verify this yourself: What Else Happens at the Application Layer? Once the address is resolved, curl has the destination . It then constructs an HTTP request: This request has exactly the same format as one sent to an external host. The header says , but that is merely a difference in content; the protocol behavior is identical. After constructing the request, curl writes it as a payload into the TCP socket associated with . The application layer's work is complete, and the data moves down to the next layer. Transport Layer — TCP Still Performs a Handshake Once the application layer writes the HTTP message into the TCP socket, the transport layer takes over. This differed from what I had initially imagined: Local communication still requires TCP's three-way handshake. TCP is a connection-oriented protocol. Whether the two endpoints are remote machines or two processes on the same machine, it still performs the complete handshake: the client sends SYN, the server replies with SYN-ACK, and the client sends ACK. Only after the connection is established does the HTTP payload begin to travel. The only difference is that the handshake does not cross any physical line. It completes almost instantly inside the operating-system kernel through the loopback interface . The RTT approaches zero, so there are no retransmissions and no congestion-control episode—but the protocol state machine, sequence numbers, acknowledgment numbers, and port numbers are all still present. We can verify this with tcpdump. Open a terminal and listen on port 6370 of the loopback interface: Then run in another terminal. The output looks like this: Let us connect the lines one by one. Please first adjust the code block above to use horizontal scrolling so that the line details are easier to read. The Second Line: IPv6 Attempt Rejected The server on port 6370 was not listening on IPv6, so it immediately returned . This means that my service was bound to IPv4 , not to IPv6 . The Third Line: TCP Three-Way Handshake IPv4 curl switches to IPv4 , using source port . = SYN the client starts the connection . = SYN-ACK the server accepts the connection . = ACK the client confirms it . Notice the timestamps: all three lines are at , so the handshake completed within the same millisecond. The lines in between contain the HTTP request and response. The Sixth Line: TCP's Four-Way Termination The timestamps show just how quickly the process runs. Network Layer — Routing Keeps It on This Machine After the application layer passes the HTTP message to the TCP socket and the transport layer encapsulates it in a TCP segment, the data enters the network layer. Two things happen here: the IP header is added, and the routing table decides where the packet should go. We can inspect how localhost appears at the network layer with: The route destination for is itself, and the interface is ; the flags include . The route destination for an external IP is its corresponding network segment. Here the interface is not the physical network card , but , because I had a proxy service enabled; the flags include . Also note the MTU Maximum Transmission Unit . The local upper limit is the macOS-configured value of 16,384, while the connection to the external network uses the standard Ethernet limit of 1,500. Link Layer — A Virtual “Pretend Transmission” Once the network layer has made its routing decision, the packet is marked as “destined for this host.” In a normal network flow, the link layer would next encapsulate it in an Ethernet frame, fill in the destination MAC address, and hand it to the physical layer. For , however, things become unusual here. No Real MAC-Address Exchange We can verify this with the command: There is no output. For a loopback address, the operating system does not send an ARP request asking, “Who owns 127.0.0.1? Tell me your MAC address.” By contrast, if you look up another device on the local network: What Does the Loopback Interface Do? The operating system contains a special virtual network interface called the loopback interface, usually on macOS and Linux. You can see that it exists with: The key information is: : identifies this as a loopback interface. : shows that the address is bound to it. : the maximum transmission unit is much larger than Ethernet's 1,500. When the network layer decides to send a packet to , what actually happens is this: it hands the packet to the virtual interface . Instead of calling a network-card driver, directly “mirrors” the packet into the receive queue of the operating system's network stack. From the receiver's point of view, it looks as though a packet has arrived from the network. This process is called loopback inside the operating system. At its core, it is an in-memory copy, with no hardware involved. Does the Link Layer Still Exist? Strictly speaking, the link layer is responsible for “transmitting frames over the same link.” For the loopback interface, the operating system simulates an extremely simple link layer. It adds a fake link-layer header on macOS and on Linux , but it does not add real MAC addresses or perform CSMA/CD Carrier Sense Multiple Access with Collision Detection . There is therefore no actual frame-transmission delay. What About the Physical Layer? It is skipped entirely. It is nice not to have to write this section at all ^∇^ The Answer: Localhost Passes Through Four of the Five TCP/IP Layers The only layer it skips is the physical layer. It is not as simple as “one program directly chats with another.” It still goes through the whole process of protocol encapsulation, port addressing, routing decisions, and interface scheduling. The only difference is that everything happens inside one machine—like a parcel that never leaves its residential compound: the label is attached, the parcel is sorted, and the delivery is signed for, but the courier never reaches the road.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<h2>Starting with a Familiar Address</h2>\n<p>Type <code>npm run dev</code> in a terminal and Vite cheerfully tells you that the service is running at <code>localhost:5173</code>; click in a browser, and the page appears. Or, when working with Redis, you might run <code>redis-cli -h 127.0.0.1 -p 6379</code>, which also connects to the local machine. We know this scene very well. Recently, however, while reviewing computer networks for the 408 exam, I started studying network layering again.</p>\n<p>That raised a natural question that is easy to overlook:</p>\n<p><strong>If I am connecting to “myself,” does this still count as network communication? Does it really pass through the network protocol stack? If so, how many layers does it go through?</strong></p>\n<p>A little investigation produced an answer that was slightly counterintuitive: yes, it counts, and it goes through quite a few layers. I wanted to write down the process from top to bottom and record what happens behind <code>localhost</code>.</p>\n<p>In simple terms, it does not pass through a network card, switch, router, or cable the way an external request does. But inside the operating system, the application, transport, and network layers still do their jobs seriously. I used to think of <code>localhost</code> as “one program directly chatting with another,” but it is more like a parcel that never leaves its residential compound: it never gets on the highway, but the shipping label, sorting, and delivery process still happen.</p>\n<p>One clarification first: <code>localhost</code> is a name, not an IP address. It usually resolves to the IPv4 loopback address <code>127.0.0.1</code>; some systems may prefer the IPv6 address <code>::1</code>. To keep the route clear, the discussion below assumes the IPv4 loopback path through <code>127.0.0.1</code>.</p>\n<h2>Preface: Network Layering</h2>\n<p>Before everything begins, let us briefly review the network layers from the 408 curriculum so that we have a framework for the analysis that follows.</p>\n<p><img src=\"https://img.markxu.icu/imgnetwork-layer.png\" alt=\"Network layers\"></p>\n<blockquote>\n<p>A short story first:</p>\n<p>The network layers are shown above. In 1977, the International Organization for Standardization (ISO) established a special committee to study a standard framework for network interconnection. It hoped to create a universal, open network-layering model so that any systems following the standard could communicate with one another. The reference model was formally published in 1984. It is the most standardized layering model, and almost any network can be fitted into it.</p>\n<p>The story of TCP/IP begins with the Advanced Research Projects Agency (ARPA) of the United States Department of Defense.</p>\n<ul>\n<li>In 1969, ARPANET was born. It was the predecessor of the modern Internet, initially intended to connect computers at universities and research institutions for resource sharing.</li>\n<li>In 1974, Vint Cerf and Bob Kahn published a paper on TCP. It introduced the concept of a “gateway”—which later evolved into the router—and solved the problem of connecting different networks.</li>\n<li>On January 1, 1983, ARPANET officially switched its core protocol from NCP to TCP/IP. This day is often regarded as the “birthday” of the modern Internet.</li>\n<li>Berkeley later integrated a free implementation of TCP/IP into Unix through BSD Unix. As Unix spread through universities and research institutions, TCP/IP quickly spread with it.</li>\n</ul>\n<p>Because TCP/IP was simpler, more efficient, and cheaper to implement, it naturally replaced the OSI approach and became today's dominant network-layering model. We will use the TCP/IP layers for the detailed discussion below.</p>\n</blockquote>\n<p>Before continuing, here is a quick summary of what the five TCP/IP layers do:</p>\n<ul>\n<li>The application layer is responsible for “what to say,” such as the HTTP, Redis, and MySQL protocols.</li>\n<li>The transport layer is responsible for “which process receives it and how it is delivered.” TCP and UDP live here, as do port numbers.</li>\n<li>The network layer is responsible for “which address to go to.” IP belongs here, so <code>127.0.0.1</code> is a network-layer address.</li>\n<li>The link layer handles transmission over the same link. Ethernet frames and MAC addresses generally appear here.</li>\n<li>The physical layer turns bits into electrical, optical, or wireless signals and actually moves data through physical media.</li>\n</ul>\n<p>Based on that description, we can already analyze <code>localhost:8080</code>: <code>8080</code> is a port number belonging to the transport layer, while <code>127.0.0.1</code> is an IP address belonging to the network layer. Once these two concepts are separated, many networking questions become much less confusing.</p>\n<h2>TL;DR</h2>\n<!-- table-widths: 16%, 56%, 28% -->\n<table>\n<thead>\n<tr>\n<th>Layer</th>\n<th>What happens</th>\n<th>Key point</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Application layer</strong></td>\n<td>The browser or client sends an HTTP request to <code>127.0.0.1:8080</code>.</td>\n<td>This is where the data starts.</td>\n</tr>\n<tr>\n<td><strong>Transport layer</strong></td>\n<td>The operating system uses port <code>8080</code> to find the corresponding service process, such as your web server, and encapsulates the data in a TCP segment.</td>\n<td>The port number takes effect here.</td>\n</tr>\n<tr>\n<td><strong>Network layer</strong></td>\n<td>The operating system sees that the destination IP is <code>127.0.0.1</code>, recognizes it as a <strong>loopback address</strong>, and hands it directly to the loopback interface instead of a physical network card.</td>\n<td>This is the crucial step: the data never leaves the machine.</td>\n</tr>\n<tr>\n<td><strong>Link layer</strong></td>\n<td>The loopback interface simulates a virtual link layer and pretends to send the data over a network, but in reality it only makes an in-memory turn.</td>\n<td>There is no real MAC-address exchange.</td>\n</tr>\n<tr>\n<td><strong>Physical layer</strong></td>\n<td><strong>Skipped.</strong> No real hardware is involved, so the data is never turned into electrical or optical signals.</td>\n<td>This is the fundamental reason localhost is much faster than an external connection.</td>\n</tr>\n</tbody>\n</table>\n<p><strong>So localhost actually completes most of the process; it simply skips the physical layer.</strong></p>\n<h2>Application Layer — No DNS Lookup</h2>\n<p>Let us begin at the top and see what the first step of <code>curl localhost:6370</code> does.</p>\n<h3>Experiment: External Host vs. Localhost</h3>\n<p>In a terminal, I ran two commands: one accessing an external domain and one accessing my local service:</p>\n<pre><code class=\"language-Shell\">curl -v http://markxu.icu\ncurl -v localhost:6370\n</code></pre>\n<p>I captured the traffic with tcpdump:</p>\n<pre><code class=\"language-Shell\">sudo tcpdump -i any port 53\n</code></pre>\n<p>When accessing the external domain, the terminal showed output like this:</p>\n<pre><code class=\"language-Shell\">12:26:00.381970 IP 198.18.0.1.56714 &gt; public1.114dns.com.domain: 53233+ A? markxu.icu. (28)\n12:26:00.382370 IP public1.114dns.com.domain &gt; 198.18.0.1.56714: 53233* 1/0/0 A 198.18.0.172 (44)\n12:26:00.387550 IP 192.168.20.80.49570 &gt; pdns.dnspod.cn.domain: 13415+ AAAA? markxu.icu. (28)\n12:26:00.387656 IP 192.168.20.80.63675 &gt; public1.alidns.com.domain: 13415+ AAAA? markxu.icu. (28)\n12:26:00.387763 IP 192.168.20.80.52301 &gt; public1.alidns.com.domain: 41468+ A? markxu.icu. (28)\n12:26:00.388014 IP 192.168.20.80.56763 &gt; pdns.dnspod.cn.domain: 41468+ A? markxu.icu. (28)\n</code></pre>\n<p>When accessing the local service, however, nothing appeared. That is because <code>localhost</code> is not an ordinary domain name. Under the IETF standard (RFC 6761), it is reserved as a special hostname. When the operating system handles <code>localhost</code>, it does not issue a DNS query; it reads the mapping directly from <code>/etc/hosts</code> instead.</p>\n<p>You can verify this yourself:</p>\n<pre><code class=\"language-Shell\">cat /etc/hosts | grep localhost\n</code></pre>\n<p><img src=\"https://img.markxu.icu/imggrep-localhost.png\" alt=\"localhost mapping\"></p>\n<h3>What Else Happens at the Application Layer?</h3>\n<p>Once the address is resolved, curl has the destination <code>127.0.0.1:6370</code>. It then constructs an HTTP request:</p>\n<pre><code class=\"language-Shell\">GET / HTTP/1.1\nHost: localhost:6370\nUser-Agent: curl/8.7.1\nAccept: */*\n</code></pre>\n<p>This request has exactly the same format as one sent to an external host. The <code>Host</code> header says <code>localhost:6370</code>, but that is merely a difference in content; the protocol behavior is identical.</p>\n<p>After constructing the request, curl writes it as a payload into the TCP socket associated with <code>127.0.0.1:6370</code>. The application layer's work is complete, and the data moves down to the next layer.</p>\n<h2>Transport Layer — TCP Still Performs a Handshake</h2>\n<p>Once the application layer writes the HTTP message into the TCP socket, the transport layer takes over. This differed from what I had initially imagined:</p>\n<p><strong>Local communication still requires TCP's three-way handshake.</strong></p>\n<p>TCP is a connection-oriented protocol. Whether the two endpoints are remote machines or two processes on the same machine, it still performs the complete handshake: the client sends SYN, the server replies with SYN-ACK, and the client sends ACK. Only after the connection is established does the HTTP payload begin to travel.</p>\n<p>The only difference is that the handshake does not cross any physical line. It completes almost instantly inside the operating-system kernel through the loopback interface <code>lo</code>. The RTT approaches zero, so there are no retransmissions and no congestion-control episode—but the protocol state machine, sequence numbers, acknowledgment numbers, and port numbers are all still present.</p>\n<p>We can verify this with tcpdump. Open a terminal and listen on port 6370 of the loopback interface:</p>\n<pre><code class=\"language-Shell\">sudo tcpdump -i lo0 port 6370\n</code></pre>\n<p>Then run <code>curl localhost</code> in another terminal. The output looks like this:</p>\n<pre><code class=\"language-Shell\">tcpdump: verbose output suppressed, use -v[v]... for full protocol decode\nlistening on lo0, link-type NULL (BSD loopback), snapshot length 524288 bytes\n14:17:36.505024 IP6 localhost.62464 &gt; localhost.6370: Flags [S], seq 3529818479, win 65535, options [mss 16324,nop,wscale 6,nop,nop,TS val 2294771787 ecr 0,sackOK,eol], length 0\n14:17:36.505080 IP6 localhost.6370 &gt; localhost.62464: Flags [R.], seq 0, ack 3529818480, win 0, length 0\n14:17:36.505206 IP localhost.62465 &gt; localhost.6370: Flags [S], seq 1917119642, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 3300697293 ecr 0,sackOK,eol], length 0\n14:17:36.505344 IP localhost.6370 &gt; localhost.62465: Flags [S.], seq 4137660183, ack 1917119643, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 4162405884 ecr 3300697293,sackOK,eol], length 0\n14:17:36.505365 IP localhost.62465 &gt; localhost.6370: Flags [.], ack 1, win 6380, options [nop,nop,TS val 3300697293 ecr 4162405884], length 0\n14:17:36.505379 IP localhost.6370 &gt; localhost.62465: Flags [.], ack 1, win 6380, options [nop,nop,TS val 4162405884 ecr 3300697293], length 0\n14:17:36.505607 IP localhost.62465 &gt; localhost.6370: Flags [P.], seq 1:78, ack 1, win 6380, options [nop,nop,TS val 3300697293 ecr 4162405884], length 77\n14:17:36.505631 IP localhost.6370 &gt; localhost.62465: Flags [.], ack 78, win 6379, options [nop,nop,TS val 4162405884 ecr 3300697293], length 0\n14:17:36.514104 IP localhost.6370 &gt; localhost.62465: Flags [P.], seq 1:1448, ack 78, win 6379, options [nop,nop,TS val 4162405893 ecr 3300697293], length 1447\n14:17:36.514168 IP localhost.62465 &gt; localhost.6370: Flags [.], ack 1448, win 6358, options [nop,nop,TS val 3300697302 ecr 4162405893], length 0\n14:17:36.514408 IP localhost.62465 &gt; localhost.6370: Flags [F.], seq 78, ack 1448, win 6358, options [nop,nop,TS val 3300697302 ecr 4162405893], length 0\n14:17:36.514434 IP localhost.6370 &gt; localhost.62465: Flags [.], ack 79, win 6379, options [nop,nop,TS val 4162405893 ecr 3300697302], length 0\n14:17:36.514744 IP localhost.6370 &gt; localhost.62465: Flags [F.], seq 1448, ack 79, win 6379, options [nop,nop,TS val 4162405894 ecr 3300697302], length 0\n14:17:36.514791 IP localhost.62465 &gt; localhost.6370: Flags [.], ack 1449, win 6358, options [nop,nop,TS val 3300697303 ecr 4162405894], length 0\n^C\n14 packets captured\n24 packets received by filter\n0 packets dropped by kernel\n</code></pre>\n<p>Let us connect the lines one by one.</p>\n<p><strong>Please first adjust the code block above to use horizontal scrolling so that the line details are easier to read.</strong></p>\n<h3>The Second Line: IPv6 Attempt (Rejected)</h3>\n<pre><code class=\"language-Shell\">14:17:36.505024 IP6 localhost.62464 &gt; localhost.6370: Flags [S], seq 3529818479, win 65535, options [mss 16324,nop,wscale 6,nop,nop,TS val 2294771787 ecr 0,sackOK,eol], length 0\n14:17:36.505080 IP6 localhost.6370 &gt; localhost.62464: Flags [R.], seq 0, ack 3529818480, win 0, length 0\n</code></pre>\n<p>The server on port 6370 was not listening on IPv6, so it immediately returned <code>[R.]</code>. This means that my service was bound to <code>127.0.0.1</code> (IPv4), not to <code>::1</code> (IPv6).</p>\n<h3>The Third Line: TCP Three-Way Handshake (IPv4)</h3>\n<pre><code class=\"language-Shell\">14:17:36.505206 IP localhost.62465 &gt; localhost.6370: Flags [S], seq 1917119642, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 3300697293 ecr 0,sackOK,eol], length 0\n14:17:36.505344 IP localhost.6370 &gt; localhost.62465: Flags [S.], seq 4137660183, ack 1917119643, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 4162405884 ecr 3300697293,sackOK,eol], length 0\n14:17:36.505365 IP localhost.62465 &gt; localhost.6370: Flags [.], ack 1, win 6380, options [nop,nop,TS val 3300697293 ecr 4162405884], length 0\n</code></pre>\n<ul>\n<li>curl switches to IPv4 (<code>127.0.0.1</code>), using source port <code>62465</code>.</li>\n<li><code>[S]</code> = SYN (the client starts the connection).</li>\n<li><code>[S.]</code> = SYN-ACK (the server accepts the connection).</li>\n<li><code>[.]</code> = ACK (the client confirms it).</li>\n</ul>\n<p>Notice the timestamps: all three lines are at <code>14:17:36.505</code>, so the handshake completed within the same millisecond.</p>\n<p>The lines in between contain the HTTP request and response.</p>\n<h3>The Sixth Line: TCP's Four-Way Termination</h3>\n<pre><code class=\"language-Shell\">14:17:36.514408 IP localhost.62465 &gt; localhost.6370: Flags [F.], seq 78, ack 1448, win 6358, options [nop,nop,TS val 3300697302 ecr 4162405893], length 0\n14:17:36.514434 IP localhost.6370 &gt; localhost.62465: Flags [.], ack 79, win 6379, options [nop,nop,TS val 4162405893 ecr 3300697302], length 0\n14:17:36.514744 IP localhost.6370 &gt; localhost.62465: Flags [F.], seq 1448, ack 79, win 6379, options [nop,nop,TS val 4162405894 ecr 3300697302], length 0\n14:17:36.514791 IP localhost.62465 &gt; localhost.6370: Flags [.], ack 1449, win 6358, options [nop,nop,TS val 3300697303 ecr 4162405894], length 0\n</code></pre>\n<p>The timestamps show just how quickly the process runs.</p>\n<h2>Network Layer — Routing Keeps It on This Machine</h2>\n<p>After the application layer passes the HTTP message to the TCP socket and the transport layer encapsulates it in a TCP segment, the data enters the network layer. Two things happen here: the IP header is added, and the routing table decides where the packet should go.</p>\n<p>We can inspect how localhost appears at the network layer with:</p>\n<pre><code class=\"language-Shell\">route -n get 127.0.0.1\n</code></pre>\n<p><img src=\"https://img.markxu.icu/imgnetworklayer2.png\" alt=\"Network layer\"></p>\n<ul>\n<li>The route destination for <code>127.0.0.1</code> is itself, and the interface is <code>lo0</code>; the flags include <code>LOCAL</code>.</li>\n<li>The route destination for an external IP is its corresponding network segment. Here the interface is not the physical network card <code>en0</code>, but <code>utun1024</code>, because I had a proxy service enabled; the flags include <code>GATEWAY</code>.</li>\n</ul>\n<p>Also note the MTU (Maximum Transmission Unit). The local upper limit is the macOS-configured value of 16,384, while the connection to the external network uses the standard Ethernet limit of 1,500.</p>\n<h2>Link Layer — A Virtual “Pretend Transmission”</h2>\n<p>Once the network layer has made its routing decision, the packet is marked as “destined for this host.” In a normal network flow, the link layer would next encapsulate it in an Ethernet frame, fill in the destination MAC address, and hand it to the physical layer.</p>\n<p>For <code>127.0.0.1</code>, however, things become unusual here.</p>\n<h3>No Real MAC-Address Exchange</h3>\n<p>We can verify this with the <code>arp</code> command:</p>\n<pre><code class=\"language-Shell\">arp -a | grep 127.0.0.1\n</code></pre>\n<p>There is no output. For a loopback address, the operating system does not send an ARP request asking, “Who owns 127.0.0.1? Tell me your MAC address.”</p>\n<p>By contrast, if you look up another device on the local network:</p>\n<pre><code class=\"language-Shell\">arp -a | grep 192.168\n</code></pre>\n<p><img src=\"https://img.markxu.icu/imgmac.png\" alt=\"Comparing MAC addresses\"></p>\n<h3>What Does the Loopback Interface Do?</h3>\n<p>The operating system contains a special virtual network interface called the loopback interface, usually <code>lo0</code> on macOS and Linux. You can see that it exists with:</p>\n<pre><code class=\"language-Shell\">ifconfig lo0\n</code></pre>\n<p><img src=\"https://img.markxu.icu/imglo0.png\" alt=\"lo0\"></p>\n<p>The key information is:</p>\n<ul>\n<li><code>LOOPBACK</code>: identifies this as a loopback interface.</li>\n<li><code>inet 127.0.0.1</code>: shows that the address is bound to it.</li>\n<li><code>mtu 16384</code>: the maximum transmission unit is much larger than Ethernet's 1,500.</li>\n</ul>\n<p>When the network layer decides to send a packet to <code>127.0.0.1</code>, what actually happens is this: it hands the packet to the virtual interface <code>lo0</code>. Instead of calling a network-card driver, <code>lo0</code> directly “mirrors” the packet into the receive queue of the operating system's network stack. From the receiver's point of view, it looks as though a packet has arrived from the network.</p>\n<p>This process is called loopback inside the operating system. At its core, it is an in-memory copy, with no hardware involved.</p>\n<h3>Does the Link Layer Still Exist?</h3>\n<p>Strictly speaking, the link layer is responsible for “transmitting frames over the same link.” For the loopback interface, the operating system simulates an extremely simple link layer. It adds a fake link-layer header (<code>NULL</code> on macOS and <code>LOOPBACK</code> on Linux), but it does not add real MAC addresses or perform CSMA/CD (Carrier Sense Multiple Access with Collision Detection). There is therefore no actual frame-transmission delay.</p>\n<h3>What About the Physical Layer?</h3>\n<p>It is skipped entirely. It is nice not to have to write this section at all (^∇^)</p>\n<h2>The Answer: Localhost Passes Through Four of the Five TCP/IP Layers</h2>\n<p>The only layer it skips is the physical layer.</p>\n<p>It is not as simple as “one program directly chats with another.” It still goes through the whole process of protocol encapsulation, port addressing, routing decisions, and interface scheduling. The only difference is that everything happens inside one machine—like a parcel that never leaves its residential compound: the label is attached, the parcel is sorted, and the delivery is signed for, but the courier never reaches the road.</p>\n",
      "summary": "[AI-translated] Does localhost really pass through the network protocol stack when we use it during development? How many layers of the network are involved?",
      "date_published": "2026-06-25T00:00:00.000Z",
      "image": "https://img.markxu.icu/imgnetwork-layer.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/anti-reward-hacking-college-en",
      "url": "https://markxu.icu/blog/anti-reward-hacking-college-en",
      "title": "Rejecting Reward Hacking: My Four Years at University",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. Over the past few days, I have said goodbye to one classmate after another. I stopped again and again at places I had passed countless times, looked around, and took many photos, reluctant to leave. I could not help looking back on my four years at university. Yet what kept returning to me was not only what I had done, but why I had made those choices at the time—and what might have been different if I could do it all again. To borrow a concept that has recently become popular: reject Reward Hacking, and refuse to turn your life into the maximization of a single metric. The previous few articles were all written around graduation—some were summaries from different angles, while others were simply scattered reflections. These four university years were undoubtedly the most vivid and consequential four of my twenty-two years of life. On the train leaving campus, I wrote this article to record the choices and experiences of those years, as a kind of private retrospective. Reward Hacking is originally a concept from reinforcement learning. It describes an agent exploiting loopholes in a reward function to obtain a higher reward in an unintended way. At university, that reward function easily becomes tied to GPA, class rank, recommendation-based postgraduate admission, scholarships, and competition awards. These things certainly matter, and in many contexts they genuinely demonstrate a student's excellence. But if they become the final objective and learning and growth themselves are forgotten, the result can easily turn into a real-life form of Reward Hacking. I did pay a small price over these four years for not having a higher GPA. If I could start again, perhaps I would strike a better balance. Even so, I want to record honestly what it felt like, in a meritocratic society where almost everyone is reminded to “optimize the metrics,” to rewrite my own reward function as: “Did I truly learn something? Do I understand society better? Am I closer to the person I wanted to become as a child?” This article is not meant to prove that I was right, because I still do not know the answer myself. It simply explains where I invested my time, how those choices shaped me, and leaves a trace of who I am now for my future self. Choosing to See the World Outside The 2022 gaokao—China's national college entrance examination—was certainly memorable. I received the worst ranking of my life. Students in my score range generally stayed within the province in order to attend a stronger university rather than studying farther away. After looking through many universities I had never previously heard of, I discussed the decision with my family. I still wanted to leave the province, see somewhere new, and experience life elsewhere. Fortunately, my family was very supportive. In the end, I went south to study in a city I had never visited before. Since I had decided to leave the province, I focused my university choices along China's southeast coast. We could submit up to ninety preferences that year; I filled in eighty-eight, divided into ambitious, realistic, and safe options. It was also the height of Zhang Xuefeng's popularity. Although the pandemic had hit the economy hard, people generally expected a rapid recovery and still made decisions according to pre-pandemic assumptions. Based on society's expectations at the time and my own interests, I selected several engineering disciplines related to computing and electronics. Predictably, every major that interested me became much more competitive than in previous years. None of my ambitious or realistic choices admitted me. Finally, at preference number fifty-four, the computer science program at Huaqiao University accepted me. Looking back, I do not regret that decision at all. Moving somewhere different from the environment in which I grew up, surrounded by classmates from cultural backgrounds unlike my hometown, was itself part of my university education. Under a more cautious evaluation system, perhaps I should have remained somewhere familiar and controllable, minimizing risk. But at the time, I felt that life should not be only about following an established path more efficiently. While I still had time and the cost of mistakes was low, I wanted to explore unfamiliar places. That was probably the first time I did not choose entirely according to the “optimal solution.” Trying to Pursue Something New When I arrived at university, most people around me still carried the mindset of high school. They wanted postgraduate recommendation places and high GPAs, and many senior students, parents, and teachers gave us the same advice. Indeed, a metric that can be quickly quantified and compared is the easiest way to display personal ability. Even after the gaokao, we had not escaped that logic. But after leaving high school, I wanted to try pursuing something new—something I truly wanted. I still cared about evaluation, of course. I simply did not want to keep living under the same reward function: maximize test scores in high school, maximize GPA and résumé value at university, then maximize salary and job level after graduation. Every step appears reasonable and efficient. But if we never stop to ask, “What are these metrics actually meant to serve?”, it is easy to surrender our real curiosity, sensitivity, and judgment while optimizing our way forward. Even in high school, I bought many university textbooks, including Chen Yuezen's General Biology and Fundamentals of Organic Chemistry , along with popular-science books about physics. I wanted to understand the knowledge I was studying and see whether there were deeper explanations, so I began teaching myself many subjects. These textbooks did not always help. University chemistry books, for example, had a fairly negative effect on my high-school grades. Their conclusions differed from the simplified conclusions taught in high school, and I would mix up the reasoning from the university texts. I overthought questions, worked more slowly, and often got them wrong. The signs were already there in high school. I paid little attention to “how to score more points on this particular test.” I simply believed that if I mastered the knowledge, the score would follow naturally. Only after the gaokao did I realize that, when it comes to examinations, understanding the subject is not enough; you must also understand why it is being tested. Only by mastering both can you score well. I had never considered the “why” behind the exam, so I wandered somewhat off course. Just after my first year began, my older cousin recommended csdiy and the Survival Guide for Shanghai Jiao Tong University. I began reading what people who had excelled under the gaokao evaluation system had written. Their ideas resonated deeply with me, and, with a somewhat adolescent sense of idealism, I wanted to become as ambitious and principled as they were. So I decided to spend less energy fighting over GPA points, learn more of what genuinely interested me, and explore some of society's overlooked corners. Writing this now, I cannot avoid wondering whether the decision “not to pursue GPA” reflected courage, or whether laziness was mixed into it as well. The important thing, however, is not the slogan but what I actually accomplished. Otherwise, simply rejecting one evaluation system only means falling into another, vaguer form of self-congratulation. Next, I want to talk about what I gained. Friendship: The People I Lived Alongside The most immediate benefit of not treating GPA as my only objective was my relationship with my classmates. I got along well with everyone in my class, and most of the time we had no direct conflicts of interest. We helped whenever we could and tried our best to share joy and experience it together. I gained several sincere friendships during university. Some classmates, partly influenced by me, began exploring their own interests, exercised with me, and attended symphony concerts. Together, we left many wonderful memories in the vast playground that was Xiamen. Through their experiences and hobbies, I also found my way into several circles, discovered more common interests, and spent a rich and comfortable university life. This mattered greatly to me. An intense evaluation system can easily turn everyone around us into an invisible object of comparison: his grade, her rank, who received an opportunity, and who earned one more bonus point. Over time, another person's success becomes a threat, while another person's failure becomes a source of reassurance. That is not a comfortable way to live. I am grateful that I did not remain trapped in that kind of relationship throughout university. My classmates were, first of all, people who shared their lives with me; they should not have been seen only as competitors blocking my path. Those relationships gave me people I could ask for help, joke with, eat with, and walk beside whenever I felt anxious. None of this fits neatly onto a résumé, yet it was one of the most genuine things I gained during those four years. Reading and Society: Widening My View of the World The second thing I gained was knowledge outside my major and the many books I read. I worked through part of Mankiw's Principles of Economics and tried reading the Selected Works of Mao Zedong and The Communist Manifesto . They made me recognize the distance between my own thinking and the ideals of a Communist Party member, while giving me a firmer position and a more flexible perspective. Because my classmates were interested in Japanese culture, I read several novels by Haruki Murakami and Yasunari Kawabata, along with The Chrysanthemum and the Sword , an American perspective on Japanese culture. Through them, I also watched a good deal of anime and became familiar with ACG culture. None of these things became a quantifiable achievement, but they genuinely made me less like a person who could only solve questions and complete assigned tasks. When meeting strangers, I could try to find shared topics and quickly establish common ground and interests. People need some apparently “useless” input before they can understand why others live as they do, why society operates as it does, and why they themselves become anxious, angry, or moved by public events. My third gain may have been contact with society. I rarely joined volunteer activities organized inside the university, nor did I participate in simple events just to accumulate volunteer hours and improve my postgraduate-recommendation prospects. Instead, I spent more time talking with people on the street, helping in local communities, and meeting people who were seriously building lives in Xiamen. I also met many peers who had not been as fortunate as I was in their educational opportunities but still worked hard in their own ways. Those experiences reduced much of my anxiety and internal exhaustion about society. Once I had seen specific people, many of the abstract, polarized stories endlessly fighting and belittling one another online became less absolute. Everyone was living in their own way: some fortunate, some struggling, some confused, some resilient. After meeting them, it became much harder to understand life as a simple ranking table, and I grew more confident that my chosen direction had something worthwhile in it. Contact with society was also good for my mental health. If someone understands themselves only through meritocracy's coordinate system, it is easy to remain forever “not good enough”: the university is not prestigious enough, the grades are not high enough, the project is not impressive enough, the family background is not distinguished enough, and the future is not certain enough. Real people are far more complex and abundant than those labels. As Chairman Mao once said: To struggle against Heaven is endless joy; to struggle against Earth is endless joy; to struggle with people is endless joy. Fitness: An Unexpected Gain I had always been overweight before university. Then, in the second semester of my second year of high school, I became desk mates with one of my best friends and suddenly lost around fifteen kilograms. He enjoyed working out, so I learned from him apparently I am quite accustomed to learning from the people around me, hehe . I have exercised ever since. Although I had trained for roughly four years by graduation, I still carried some extra fat and had not built an especially impressive physique. Yet the decision to exercise benefited me enormously and continues to make many parts of life easier. First, my physical condition improved, and I became less prone to illness and colds. Second, I gained strength: sometimes I could sit and play games for twenty-four hours straight, sleep once, and wake up refreshed rather than suffering terrible back pain. Most importantly, I learned about the body through training. Combined with what I had studied in high-school biology, I learned how different parts of daily life affect hormone secretion, developed reasonably good habits, understood what my body needed, avoided many overpriced pseudoscientific products, and maintained fairly good health. Fitness also introduced me to many friends in society. They came from different professions and carried very different experiences. I met students from different countries and regions through working out as well. From a utilitarian perspective, fitness can be packaged as discipline, image management, or a productivity tool. But a healthy body, a stable rhythm of life, genuine social connections, and moments spent training together are already part of one's quality of life. Their value is not that they let me stay awake longer in the next round of competition. They are about developing myself as fully as possible, caring for both mind and body. Competitions and Projects: Learning to Do Real Work Beyond the Results During my undergraduate years, I participated in several competitions. I learned the most from mathematical modeling competitions and from one Student Innovation Project. Beyond professional knowledge, the methods of teamwork left the deepest impression on me. Mathematical Modeling In a three-day mathematical modeling competition, we received a problem and had only three days to produce a complete paper. Whatever the circumstances, that was challenging. Even with AI, we still needed a basic understanding of the algorithms and had to analyze which methods suited the data before using AI-assisted programming to solve the actual problem. We disagreed many times along the way. I generally believed that problems like Questions A and B in the Mathematical Contest in Modeling, which required us to model and analyze real-world problems, were more challenging and interesting. I preferred those. But my teammates and I chose data-analysis problems in every official competition because we wanted the safer option, and I agreed that doing so gave us a much better chance of winning an award. As computer science students, we did not understand specialized physics as clearly as students in the relevant disciplines. Data-analysis questions overlapped more with our own knowledge: we usually applied several machine-learning algorithms, fit a plausible result, and wrote a paper around it. During competitions, we often stayed awake for two nights in a row, and emotional fluctuations were inevitable. As team leader, my main responsibilities were to keep everyone's emotions from damaging efficiency; complete the work according to our plan and division of responsibilities; determine the direction of our solution; use our familiarity with the relevant knowledge to develop an approach and convince my teammates why it best suited us; and, after mistakes, steer us back on course and prioritize solving the problem. I gradually figured these things out by reflecting on mistakes after making them. We abandoned our first mathematical modeling competition the day before submission. The main reason was that, as the person who had brought the team together, I had failed to lead. The other two classmates did not know each other well, so everyone felt awkward and hesitated to express their real thoughts. I understood similar lessons only after making each mistake once: the importance of communication, how to align progress quickly, how to change direction rapidly, and so on... Competitions are naturally driven strongly by metrics: awards, ratings, certificates, and bonus points in comprehensive assessments. But I later discovered that what remained was not the certificate. It was how to review a failure, move forward when a team disagreed, and help everyone continue trusting one another under enormous pressure. These abilities are not easily captured by a scoring rubric, but they are much closer to “doing real work.” Student Innovation Project: A First Taste of Research A senior student recommended the Student Innovation Project, saying I could learn a great deal from it. I contacted my undergraduate advisor on my own initiative, found a direction that both my teammates and I found reasonably interesting, finalized the project topic, and began a year-long research experience. At first, the work consisted mostly of documentation. It was my first time revising a project proposal over and over. Thanks to my advisor's patient guidance, I gradually learned how to communicate with a university supervisor—which required a very different relationship from talking with teachers in primary or secondary school—and how a standard formal document should be written. Throughout my two years of simple undergraduate research practice, I was deeply grateful to my advisor. In terms of teamwork, a year-long project with four students was considerably harder than a mathematical modeling contest. First, I needed to ensure that everyone genuinely learned knowledge they could use later. Second, as team leader, I should have selected the technology stack and direction promptly, organized offline activities so everyone could get to know one another, assigned responsibilities clearly, and checked progress regularly. I did none of those things well. Messages between us often went unanswered for one or two weeks, and our progress fell far behind that of other teams. That was the period when I felt most anxious: I feared that failing to complete the project would harm everyone, but I also did not want to give up so easily. After the midterm defense, I began reading The Mythical Man-Month , which our introductory computer science teacher had recommended in my first year. I started to recognize my management and communication problems and adjusted in time. Fortunately, we ultimately completed the project successfully. The project was also the first time I truly recognized how important communication is. Before the midterm, I was afraid to contact my advisor because my own progress was slow and I feared being criticized. Only later did I understand that an advisor's role is primarily to guide, and that I should proactively report progress and communicate. As mentioned earlier, I had good relationships with my classmates. They listened patiently to many of my complaints, quietly supported and comforted me, and even invited me out to clear my head. I remain deeply grateful to them. The Student Innovation Project may resemble a system especially vulnerable to Reward Hacking. Project-completion materials, defenses, and output requirements all leave plenty of room for manipulation. You can turn the project into a beautiful yet hollow set of documents, or treat it as an awkward but genuine exercise in research and collaboration. I would not claim that I did especially well—in many places, I performed poorly. But the experience at least taught me that enthusiasm alone cannot complete a real project. It requires communication, planning, and divided responsibility. It also requires admitting problems and reorganizing when progress has become terrible. The final result mattered far less than what I learned throughout the process. From what I observed, quite a few Student Innovation Projects were so-called “inherited family projects.” I personally watched one project complete its defense in the cohort before mine, then successfully reappear as a newly approved project in my own cohort—with the same group of teachers hearing the defense and approving the next version. Perhaps that is why people say these projects can be superficial. At the same time, I also found many students online who, like me, began without existing resources and continued working hard. Whether or not you start with those resources, I still recommend trying it. People truly grow a great deal in the process of doing something real. Renting an Apartment: Living Independently for the First Time To complete the innovation project, I assembled a desktop computer, but our dorm rooms had been converted from four occupants to six and did not allow desktop towers. When working on the project, I often became absorbed in debugging until three or four in the morning. To avoid disturbing my roommates and to have a more focused environment, I explained the situation to my family and rented an apartment for two years. It was not far from the university, but living independently was an experience I had never had before. Handling procedures, cleaning and organizing, and finally getting the chance to cook for myself were all novel experiences. These experiences were difficult to count within the university's evaluation system. Nobody gives you extra points for learning to cook, paying utility bills, cleaning a room, or organizing your own schedule. Yet I increasingly believe that independent living is itself part of university. It slowly changed me from someone whose life was arranged by others into someone responsible for maintaining my own order. Growth does not always happen in classrooms, laboratories, or competitions. Sometimes it happens after tidying a desk in the early hours of the morning and realizing that you really can keep your life running, one small piece at a time. No GPA Myth, but No Effortless Freedom Either If I wrote only about the benefits, this article would become self-defense—an attempt to force myself to believe my own story. The negative consequences genuinely frustrated me. Because I did not prioritize GPA, my final score was 3.97 out of 5.00, which looked less impressive beside the 4.0-plus GPAs of those ranked ahead of me. I missed the university's postgraduate-recommendation quota by only two places, with a very small difference in score. Had I improved a few minor-course grades, perhaps I would have gained another year to attempt something bolder. If I could do it again, I would certainly try to find a more delicate balance and avoid missing that opportunity. Yet I also understand deeply that, if time rewound, I would probably make the same choices I made then. This is why I am now more cautious when talking about “rejecting Reward Hacking.” Rejecting a single metric is not the same as ignoring the rules; pursuing genuine learning does not excuse us from responsibility for results. The evaluation system has problems, but it also controls access to many opportunities. My GPA was not exceptionally high, but my reasonably good ranking and learning experience did, in fact, give me many opportunities. My father often tells me: A person must understand the rules and make use of them. If you fail to understand the rules at all, you can pay an enormous price at a critical moment. So I do not want to summarize these four years with the attention-grabbing but irresponsible slogan, “I refused to compete, so I was free.” More accurately, I was constantly making immature tradeoffs between freedom and opportunity, interests and metrics, long-term growth and short-term admission tickets. I still endorse some of those choices; others return at night as regrets. I also regard those regrets as part of who I am now. They remind me that a genuinely good strategy for life cannot simply oppose an evaluation system for opposition's sake. It must understand the power of metrics while refusing to be completely ruled by them. As I learned in high school, you need to understand the knowledge itself and also why that knowledge appears on the exam. What Exists Beyond Meritocracy? These experiences almost summarize my university years: self-study, fitness, competitions, projects, renting an apartment, and countless hours “wasted” with friends that nevertheless became infinitely precious. They are quick to list, yet I vividly remember every detail, challenge, and stroke of luck. The train is almost at the station, so I should bring this article to an end as well. Even after writing it, I still do not have an elegant answer. Four years at university helped me understand meritocracy's appeal more deeply: it is clear, direct, and comparable. It tells you where you rank and how to work toward the next step. For many ordinary people, it is also a relatively fair channel for upward mobility. But I increasingly doubt whether it can serve as life's highest objective. When someone constantly confirms their value by being “better than others,” it becomes difficult to stop and ask: Does what I learned make me freer? Does what I create truly matter? Are my relationships with others more sincere? Am I learning to understand a larger world, or merely climbing inside an ever-smaller ranking table? Perhaps life needs an objective better than meritocracy. It should not deny effort, dismiss ability, or dress laziness up as enlightenment. But it should be broader than “beating other people.” It should have room for genuine learning, concrete love, understanding society, the joy of creation, physical health, and memories of living alongside the people around us. It should also make room for all those moments that no metric records but that truly help a person grow.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>Over the past few days, I have said goodbye to one classmate after another. I stopped again and again at places I had passed countless times, looked around, and took many photos, reluctant to leave. I could not help looking back on my four years at university. Yet what kept returning to me was not only what I had done, but why I had made those choices at the time—and what might have been different if I could do it all again. To borrow a concept that has recently become popular: <strong>reject Reward Hacking, and refuse to turn your life into the maximization of a single metric.</strong></p>\n<hr>\n<p>The previous few articles were all written around graduation—some were summaries from different angles, while others were simply scattered reflections. These four university years were undoubtedly the most vivid and consequential four of my twenty-two years of life. On the train leaving campus, I wrote this article to record the choices and experiences of those years, as a kind of private retrospective.</p>\n<p>Reward Hacking is originally a concept from reinforcement learning. It describes an agent exploiting loopholes in a reward function to obtain a higher reward in an unintended way. At university, that reward function easily becomes tied to GPA, class rank, recommendation-based postgraduate admission, scholarships, and competition awards. These things certainly matter, and in many contexts they genuinely demonstrate a student's excellence. But if they become the final objective and learning and growth themselves are forgotten, the result can easily turn into a real-life form of Reward Hacking.</p>\n<p>I did pay a small price over these four years for not having a higher GPA. If I could start again, perhaps I would strike a better balance. Even so, I want to record honestly what it felt like, in a meritocratic society where almost everyone is reminded to “optimize the metrics,” to rewrite my own reward function as: “Did I truly learn something? Do I understand society better? Am I closer to the person I wanted to become as a child?”</p>\n<p>This article is not meant to prove that I was right, because I still do not know the answer myself. It simply explains where I invested my time, how those choices shaped me, and leaves a trace of who I am now for my future self.</p>\n<h2>Choosing to See the World Outside</h2>\n<p>The 2022 gaokao—China's national college entrance examination—was certainly memorable.</p>\n<p>I received the worst ranking of my life. Students in my score range generally stayed within the province in order to attend a stronger university rather than studying farther away. After looking through many universities I had never previously heard of, I discussed the decision with my family. I still wanted to leave the province, see somewhere new, and experience life elsewhere. Fortunately, my family was very supportive. In the end, I went south to study in a city I had never visited before.</p>\n<p>Since I had decided to leave the province, I focused my university choices along China's southeast coast. We could submit up to ninety preferences that year; I filled in eighty-eight, divided into ambitious, realistic, and safe options. It was also the height of Zhang Xuefeng's popularity. Although the pandemic had hit the economy hard, people generally expected a rapid recovery and still made decisions according to pre-pandemic assumptions. Based on society's expectations at the time and my own interests, I selected several engineering disciplines related to computing and electronics.</p>\n<p>Predictably, every major that interested me became much more competitive than in previous years. None of my ambitious or realistic choices admitted me. Finally, at preference number fifty-four, the computer science program at Huaqiao University accepted me.</p>\n<p>Looking back, I do not regret that decision at all. Moving somewhere different from the environment in which I grew up, surrounded by classmates from cultural backgrounds unlike my hometown, was itself part of my university education. Under a more cautious evaluation system, perhaps I should have remained somewhere familiar and controllable, minimizing risk. But at the time, I felt that life should not be only about following an established path more efficiently. While I still had time and the cost of mistakes was low, I wanted to explore unfamiliar places.</p>\n<p>That was probably the first time I did not choose entirely according to the “optimal solution.”</p>\n<h2>Trying to Pursue Something New</h2>\n<p>When I arrived at university, most people around me still carried the mindset of high school. They wanted postgraduate recommendation places and high GPAs, and many senior students, parents, and teachers gave us the same advice. Indeed, <strong>a metric that can be quickly quantified and compared is the easiest way to display personal ability.</strong> Even after the gaokao, we had not escaped that logic.</p>\n<p>But after leaving high school, I wanted to try pursuing something new—something I truly wanted. I still cared about evaluation, of course. I simply did not want to keep living under the same reward function: maximize test scores in high school, maximize GPA and résumé value at university, then maximize salary and job level after graduation. Every step appears reasonable and efficient. But if we never stop to ask, “What are these metrics actually meant to serve?”, it is easy to surrender our real curiosity, sensitivity, and judgment while optimizing our way forward.</p>\n<p>Even in high school, I bought many university textbooks, including Chen Yuezen's <em>General Biology</em> and <em>Fundamentals of Organic Chemistry</em>, along with popular-science books about physics. I wanted to understand the knowledge I was studying and see whether there were deeper explanations, so I began teaching myself many subjects. These textbooks did not always help. University chemistry books, for example, had a fairly negative effect on my high-school grades. Their conclusions differed from the simplified conclusions taught in high school, and I would mix up the reasoning from the university texts. I overthought questions, worked more slowly, and often got them wrong.</p>\n<div class=\"photo-scroll-vertical\">\n    <img src=\"https://img.markxu.icu/img20260614135429925.png\" alt=\"Chen Yuezen's General Biology\" />\n    <img src=\"https://img.markxu.icu/img20260614135140509.png\" alt=\"Fundamentals of Organic Chemistry\" />\n    <img src=\"https://img.markxu.icu/img20260614135516213.png\" alt=\"Does God Play Dice?\" />\n</div>\n<p>The signs were already there in high school. I paid little attention to “how to score more points on this particular test.” I simply believed that if I mastered the knowledge, the score would follow naturally. Only after the gaokao did I realize that, when it comes to examinations, understanding the subject is not enough; you must also understand why it is being tested. Only by mastering both can you score well. I had never considered the “why” behind the exam, so I wandered somewhat off course.</p>\n<p>Just after my first year began, my older cousin recommended <a href=\"https://csdiy.wiki/en/\">csdiy</a> and the <a href=\"https://survivesjtu.gitbook.io/survivesjtumanual\">Survival Guide for Shanghai Jiao Tong University</a>. I began reading what people who had excelled under the gaokao evaluation system had written. Their ideas resonated deeply with me, and, with a somewhat adolescent sense of idealism, I wanted to become as ambitious and principled as they were. So I decided to spend less energy fighting over GPA points, learn more of what genuinely interested me, and explore some of society's overlooked corners.</p>\n<p>Writing this now, I cannot avoid wondering whether the decision “not to pursue GPA” reflected courage, or whether laziness was mixed into it as well. The important thing, however, is not the slogan but what I actually accomplished. Otherwise, <strong>simply rejecting one evaluation system only means falling into another, vaguer form of self-congratulation.</strong></p>\n<p>Next, I want to talk about what I gained.</p>\n<h2>Friendship: The People I Lived Alongside</h2>\n<p>The most immediate benefit of not treating GPA as my only objective was my relationship with my classmates.</p>\n<p>I got along well with everyone in my class, and most of the time we had no direct conflicts of interest. We helped whenever we could and tried our best to share joy and experience it together. I gained several sincere friendships during university. Some classmates, partly influenced by me, began exploring their own interests, exercised with me, and attended symphony concerts. Together, we left many wonderful memories in the vast playground that was Xiamen. Through their experiences and hobbies, I also found my way into several circles, discovered more common interests, and spent a rich and comfortable university life.</p>\n<p><img src=\"https://img.markxu.icu/imgb6fd11ce8b6196d81325726123b66c5f.jpg\" alt=\"Going to a symphony concert together\"></p>\n<p>This mattered greatly to me. An intense evaluation system can easily turn everyone around us into an invisible object of comparison: his grade, her rank, who received an opportunity, and who earned one more bonus point. Over time, another person's success becomes a threat, while another person's failure becomes a source of reassurance. That is not a comfortable way to live.</p>\n<p>I am grateful that I did not remain trapped in that kind of relationship throughout university. My classmates were, first of all, people who shared their lives with me; they should not have been seen only as competitors blocking my path. Those relationships gave me people I could ask for help, joke with, eat with, and walk beside whenever I felt anxious. None of this fits neatly onto a résumé, yet it was one of the most genuine things I gained during those four years.</p>\n<h2>Reading and Society: Widening My View of the World</h2>\n<p>The second thing I gained was knowledge outside my major and the many books I read.</p>\n<p>I worked through part of Mankiw's <em>Principles of Economics</em> and tried reading the <em>Selected Works of Mao Zedong</em> and <em>The Communist Manifesto</em>. They made me recognize the distance between my own thinking and the ideals of a Communist Party member, while giving me a firmer position and a more flexible perspective. Because my classmates were interested in Japanese culture, I read several novels by Haruki Murakami and Yasunari Kawabata, along with <em>The Chrysanthemum and the Sword</em>, an American perspective on Japanese culture. Through them, I also watched a good deal of anime and became familiar with ACG culture.</p>\n<p>None of these things became a quantifiable achievement, but they genuinely made me less like a person who could only solve questions and complete assigned tasks. When meeting strangers, I could try to find shared topics and quickly establish common ground and interests. People need some apparently “useless” input before they can understand why others live as they do, why society operates as it does, and why they themselves become anxious, angry, or moved by public events.</p>\n<p>My third gain may have been contact with society. I rarely joined volunteer activities organized inside the university, nor did I participate in simple events just to accumulate volunteer hours and improve my postgraduate-recommendation prospects. Instead, I spent more time talking with people on the street, helping in local communities, and meeting people who were seriously building lives in Xiamen. I also met many peers who had not been as fortunate as I was in their educational opportunities but still worked hard in their own ways.</p>\n<p>Those experiences reduced much of my anxiety and internal exhaustion about society. Once I had seen specific people, many of the abstract, polarized stories endlessly fighting and belittling one another online became less absolute. Everyone was living in their own way: some fortunate, some struggling, some confused, some resilient. After meeting them, it became much harder to understand life as a simple ranking table, and I grew more confident that my chosen direction had something worthwhile in it.</p>\n<p>Contact with society was also good for my mental health. If someone understands themselves only through meritocracy's coordinate system, it is easy to remain forever “not good enough”: the university is not prestigious enough, the grades are not high enough, the project is not impressive enough, the family background is not distinguished enough, and the future is not certain enough. Real people are far more complex and abundant than those labels. As Chairman Mao once said:</p>\n<blockquote>\n<p>To struggle against Heaven is endless joy; to struggle against Earth is endless joy; to struggle with people is endless joy.</p>\n</blockquote>\n<p><img src=\"https://img.markxu.icu/img20260616171705555.png\" alt=\"A quotation from Chairman Mao\"></p>\n<h2>Fitness: An Unexpected Gain</h2>\n<p>I had always been overweight before university. Then, in the second semester of my second year of high school, I became desk mates with one of my best friends and suddenly lost around fifteen kilograms. He enjoyed working out, so I learned from him <em>(apparently I am quite accustomed to learning from the people around me, hehe)</em>. I have exercised ever since.</p>\n<p>Although I had trained for roughly four years by graduation, I still carried some extra fat and had not built an especially impressive physique. Yet the decision to exercise benefited me enormously and continues to make many parts of life easier. First, my physical condition improved, and I became less prone to illness and colds. Second, I gained strength: sometimes I could sit and play games for twenty-four hours straight, sleep once, and wake up refreshed rather than suffering terrible back pain. Most importantly, I learned about the body through training. Combined with what I had studied in high-school biology, I learned how different parts of daily life affect hormone secretion, developed reasonably good habits, understood what my body needed, avoided many overpriced pseudoscientific products, and maintained fairly good health.</p>\n<div class=\"img-grid-2\">\n    <img src=\"https://img.markxu.icu/img1000253384.jpg\" alt=\"University gym\" />\n    <img src=\"https://img.markxu.icu/img1000253385.jpg\" alt=\"The gym environment\" />\n</div>\n<p>Fitness also introduced me to many friends in society. They came from different professions and carried very different experiences. I met students from different countries and regions through working out as well.</p>\n<p>From a utilitarian perspective, fitness can be packaged as discipline, image management, or a productivity tool. But a healthy body, a stable rhythm of life, genuine social connections, and moments spent training together are already part of one's quality of life. Their value is not that they let me stay awake longer in the next round of competition. They are about developing myself as fully as possible, caring for both mind and body.</p>\n<h2>Competitions and Projects: Learning to Do Real Work Beyond the Results</h2>\n<p>During my undergraduate years, I participated in several competitions. I learned the most from mathematical modeling competitions and from one Student Innovation Project. Beyond professional knowledge, the methods of teamwork left the deepest impression on me.</p>\n<h3>Mathematical Modeling</h3>\n<p>In a three-day mathematical modeling competition, we received a problem and had only three days to produce a complete paper. Whatever the circumstances, that was challenging. Even with AI, we still needed a basic understanding of the algorithms and had to analyze which methods suited the data before using AI-assisted programming to solve the actual problem.</p>\n<p>We disagreed many times along the way. I generally believed that problems like Questions A and B in the Mathematical Contest in Modeling, which required us to model and analyze real-world problems, were more challenging and interesting. I preferred those. But my teammates and I chose data-analysis problems in every official competition because we wanted the safer option, and I agreed that doing so gave us a much better chance of winning an award. As computer science students, we did not understand specialized physics as clearly as students in the relevant disciplines. Data-analysis questions overlapped more with our own knowledge: we usually applied several machine-learning algorithms, fit a plausible result, and wrote a paper around it.</p>\n<p>During competitions, we often stayed awake for two nights in a row, and emotional fluctuations were inevitable. As team leader, my main responsibilities were to keep everyone's emotions from damaging efficiency; complete the work according to our plan and division of responsibilities; determine the direction of our solution; use our familiarity with the relevant knowledge to develop an approach and convince my teammates why it best suited us; and, after mistakes, steer us back on course and prioritize solving the problem.</p>\n<p>I gradually figured these things out by reflecting on mistakes after making them. We abandoned our first mathematical modeling competition the day before submission. The main reason was that, as the person who had brought the team together, I had failed to lead. The other two classmates did not know each other well, so everyone felt awkward and hesitated to express their real thoughts. I understood similar lessons only after making each mistake once: the importance of communication, how to align progress quickly, how to change direction rapidly, and so on...</p>\n<p>Competitions are naturally driven strongly by metrics: awards, ratings, certificates, and bonus points in comprehensive assessments. But I later discovered that what remained was not the certificate. It was how to review a failure, move forward when a team disagreed, and help everyone continue trusting one another under enormous pressure. These abilities are not easily captured by a scoring rubric, but they are much closer to “doing real work.”</p>\n<h3>Student Innovation Project: A First Taste of Research</h3>\n<img   src=\"https://img.markxu.icu/img20260614160854380.png\"   alt=\"Student Innovation Project logo\"   style=\"width: min(100%, 360px); height: auto; display: block; margin: 1.5rem auto;\" />\n<p>A senior student recommended the <a href=\"https://github.com/MarkXuJQ/Pelvis-Fracture-Image-Segmentation\">Student Innovation Project</a>, saying I could learn a great deal from it. I contacted my undergraduate advisor on my own initiative, found a direction that both my teammates and I found reasonably interesting, finalized the project topic, and began a year-long research experience.</p>\n<p>At first, the work consisted mostly of documentation. It was my first time revising a project proposal over and over. Thanks to my advisor's patient guidance, I gradually learned how to communicate with a university supervisor—which required a very different relationship from talking with teachers in primary or secondary school—and how a standard formal document should be written. Throughout my two years of simple undergraduate research practice, I was deeply grateful to my advisor.</p>\n<p>In terms of teamwork, a year-long project with four students was considerably harder than a mathematical modeling contest. First, I needed to ensure that everyone genuinely learned knowledge they could use later. Second, as team leader, I should have selected the technology stack and direction promptly, organized offline activities so everyone could get to know one another, assigned responsibilities clearly, and checked progress regularly. I did none of those things well. Messages between us often went unanswered for one or two weeks, and our progress fell far behind that of other teams. That was the period when I felt most anxious: I feared that failing to complete the project would harm everyone, but I also did not want to give up so easily.</p>\n<p>After the midterm defense, I began reading <em>The Mythical Man-Month</em>, which our introductory computer science teacher had recommended in my first year. I started to recognize my management and communication problems and adjusted in time. Fortunately, we ultimately completed the project successfully.</p>\n<p>The project was also the first time I truly recognized how important communication is. Before the midterm, I was afraid to contact my advisor because my own progress was slow and I feared being criticized. Only later did I understand that an advisor's role is primarily to guide, and that I should proactively report progress and communicate.</p>\n<p>As mentioned earlier, I had good relationships with my classmates. They listened patiently to many of my complaints, quietly supported and comforted me, and even invited me out to clear my head. I remain deeply grateful to them.</p>\n<p>The Student Innovation Project may resemble a system especially vulnerable to Reward Hacking. Project-completion materials, defenses, and output requirements all leave plenty of room for manipulation. You can turn the project into a beautiful yet hollow set of documents, or treat it as an awkward but genuine exercise in research and collaboration. I would not claim that I did especially well—in many places, I performed poorly. But the experience at least taught me that enthusiasm alone cannot complete a real project. It requires communication, planning, and divided responsibility. It also requires admitting problems and reorganizing when progress has become terrible. The final result mattered far less than what I learned throughout the process.</p>\n<blockquote>\n<p>From what I observed, quite a few Student Innovation Projects were so-called “inherited family projects.” I personally watched one project complete its defense in the cohort before mine, then successfully reappear as a newly approved project in my own cohort—with the same group of teachers hearing the defense and approving the next version. Perhaps that is why people say these projects can be superficial. At the same time, I also found many students online who, like me, began without existing resources and continued working hard. Whether or not you start with those resources, I still recommend trying it. People truly grow a great deal in the process of doing something real.</p>\n</blockquote>\n<h2>Renting an Apartment: Living Independently for the First Time</h2>\n<p>To complete the innovation project, I assembled a desktop computer, but our dorm rooms had been converted from four occupants to six and did not allow desktop towers. When working on the project, I often became absorbed in debugging until three or four in the morning. To avoid disturbing my roommates and to have a more focused environment, I explained the situation to my family and rented an apartment for two years. It was not far from the university, but living independently was an experience I had never had before.</p>\n<p>Handling procedures, cleaning and organizing, and finally getting the chance to cook for myself were all novel experiences.</p>\n<div class=\"photo-scroll\">\n    <img src=\"https://img.markxu.icu/img8a5ffe6a937bf6209f6be61f55055631_720.jpg\" alt=\"My rented apartment\" />\n    <img src=\"https://img.markxu.icu/imga6b8f9e155aa93b80851e0c33adc3cd5_720.jpg\" alt=\"Stir-fried instant noodles I made\" />\n    <img src=\"https://img.markxu.icu/imgab9b3f561405b3cc78c74e9d2adec35d.jpg\" alt=\"Stewed tomato meatballs\" />\n    <img src=\"https://img.markxu.icu/img18459215118775f8ddc9e793cbe58f66_720.jpg\" alt=\"Protein-powder cookies\" />\n</div>\n<p>These experiences were difficult to count within the university's evaluation system. Nobody gives you extra points for learning to cook, paying utility bills, cleaning a room, or organizing your own schedule. Yet I increasingly believe that independent living is itself part of university. It slowly changed me from someone whose life was arranged by others into someone responsible for maintaining my own order.</p>\n<p>Growth does not always happen in classrooms, laboratories, or competitions. Sometimes it happens after tidying a desk in the early hours of the morning and realizing that you really can keep your life running, one small piece at a time.</p>\n<h2>No GPA Myth, but No Effortless Freedom Either</h2>\n<p>If I wrote only about the benefits, this article would become self-defense—an attempt to force myself to believe my own story.</p>\n<p>The negative consequences genuinely frustrated me. Because I did not prioritize GPA, my final score was 3.97 out of 5.00, which looked less impressive beside the 4.0-plus GPAs of those ranked ahead of me. I missed the university's postgraduate-recommendation quota by only two places, with a very small difference in score. Had I improved a few minor-course grades, perhaps I would have gained another year to attempt something bolder. If I could do it again, I would certainly try to find a more delicate balance and avoid missing that opportunity. Yet I also understand deeply that, if time rewound, I would probably make the same choices I made then.</p>\n<p>This is why I am now more cautious when talking about “rejecting Reward Hacking.” Rejecting a single metric is not the same as ignoring the rules; pursuing genuine learning does not excuse us from responsibility for results. The evaluation system has problems, but it also controls access to many opportunities. My GPA was not exceptionally high, but my reasonably good ranking and learning experience did, in fact, give me many opportunities. My father often tells me:</p>\n<blockquote>\n<p>A person must understand the rules and make use of them. If you fail to understand the rules at all, you can pay an enormous price at a critical moment.</p>\n</blockquote>\n<p>So I do not want to summarize these four years with the attention-grabbing but irresponsible slogan, “I refused to compete, so I was free.” More accurately, I was constantly making immature tradeoffs between freedom and opportunity, interests and metrics, long-term growth and short-term admission tickets. I still endorse some of those choices; others return at night as regrets.</p>\n<p>I also regard those regrets as part of who I am now. They remind me that a genuinely good strategy for life cannot simply oppose an evaluation system for opposition's sake. It must understand the power of metrics while refusing to be completely ruled by them. As I learned in high school, you need to understand the knowledge itself and also why that knowledge appears on the exam.</p>\n<h2>What Exists Beyond Meritocracy?</h2>\n<p>These experiences almost summarize my university years: self-study, fitness, competitions, projects, renting an apartment, and countless hours “wasted” with friends that nevertheless became infinitely precious. They are quick to list, yet I vividly remember every detail, challenge, and stroke of luck. The train is almost at the station, so I should bring this article to an end as well.</p>\n<p>Even after writing it, I still do not have an elegant answer. Four years at university helped me understand meritocracy's appeal more deeply: it is clear, direct, and comparable. It tells you where you rank and how to work toward the next step. For many ordinary people, it is also a relatively fair channel for upward mobility.</p>\n<p>But I increasingly doubt whether it can serve as life's highest objective. When someone constantly confirms their value by being “better than others,” it becomes difficult to stop and ask: Does what I learned make me freer? Does what I create truly matter? Are my relationships with others more sincere? Am I learning to understand a larger world, or merely climbing inside an ever-smaller ranking table?</p>\n<p>Perhaps life needs an objective better than meritocracy.</p>\n<p>It should not deny effort, dismiss ability, or dress laziness up as enlightenment. But it should be broader than “beating other people.” It should have room for genuine learning, concrete love, understanding society, the joy of creation, physical health, and memories of living alongside the people around us. It should also make room for all those moments that no metric records but that truly help a person grow.</p>\n",
      "summary": "[AI-translated] As I left university, I wanted to record both my memories of those four years and how my choices gradually taught me not to treat grades, rankings, and a résumé as life's only objectives.",
      "date_published": "2026-06-14T00:00:00.000Z",
      "date_modified": "2026-06-17T00:00:00.000Z",
      "image": "https://img.markxu.icu/imgb8e606e4235578a4b05bc4b87ae8945f_720.jpg",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/Amoy-food-walk-note1-en",
      "url": "https://markxu.icu/blog/Amoy-food-walk-note1-en",
      "title": "A Xiamen Food-Crawl Diary",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. My four years at university passed unbelievably quickly. It feels as though it was only yesterday that I first walked into the dorm as a freshman, met my roommates, and exchanged greetings with them. In the blink of an eye, it was already time to graduate. Xiamen is a beautiful city and an exceptionally comfortable place to live: its warm, humid climate; clean and orderly urban landscape; distinctive Minnan architecture; all kinds of greenery; bougainvillea blooming everywhere; and its broad, pristine bays... All of this made the city feel precious to me and made leaving difficult. My only regret was that, over those four years, I did not have more time and opportunities to wander through Xiamen and get to know it properly. To make up for that regret, I invited a classmate out for one more food crawl before graduation. We went looking for small shops with character and a personal touch. There were pleasant surprises, and there were disappointments too. All the photos were taken on a phone and left unedited, so please excuse the quality, haha. PuKu Fresh Doughnut Specialty Shop Opening hours: 10:30–20:00 Address: Near the Jimei Ancestral Temple, Jimei District Price: The mochi doughnuts we ordered were ¥15 each We had originally come at lunchtime for sour-cream curry, only to discover that the shop did not open until 14:30. With that plan foiled, we had no choice but to look elsewhere. On our way back, we suddenly noticed this inconspicuous little shop. It did not even have a large sign; we only stopped because a sidewalk board happened to mention doughnuts. The shop was spotless and almost minimalist, with mostly wooden furniture. It felt simple yet warm—an ideal place to sit for a while and take a couple of photos on a sunny day. As the saying goes, “Life is bitter, so have some mochi. Hello everyone, I am...” Ahem. We ordered two matcha mochi doughnuts and one preserved-plum guava mochi doughnut. The texture was genuinely excellent. The inside was remarkably elastic, while the shell was crisp. When I bit into it, I could feel the gluten pulling with a satisfying resilience, but it was calibrated perfectly and never became awkwardly chewy 🥲. The shell did not crumble everywhere like an ordinary pastry, either. Instead, it cracked naturally as the interior stretched and clung to the dough, making that combination of crispness and bounce unmistakable in every bite. The fillings deserve an even closer description 😋. On a hot day, the center felt wonderfully cool, with a rich texture like ice cream on the verge of melting, but none of the unpleasant iciness of frozen food. The matcha was intense without the usual tea astringency. The preserved-plum guava filling was more delicate, carrying the clean sweetness of ripe guava and tiny pieces of fruit. Pressing it against the roof of my mouth with my tongue revealed a surprisingly rich series of textures and flavors, although I could not really taste much of the preserved plum. The dough itself was only faintly sweet; almost all the sweetness came from the filling, where it served as a background note supporting the matcha and fruit aromas. Even after the filling was gone, the fragrance left in my mouth lasted long enough for me to enjoy the rest of the doughnut. Xiangkou Braised Beef Noodles Opening hours: 11:00–14:00, 17:00–21:00 Address: First floor of Xianghe Plaza, near Tuihua Park, Siming District Price: About ¥45 per person After finishing the mochi doughnuts, we immediately called a taxi into Xiamen Island and switched our plans to this beef-noodle shop. It was hidden beneath a very old, nearly abandoned shopping center. Even the lettering on the building looked so peculiar that, for a moment, I thought the world had glitched. The shop was clean as well, but this one felt a little warmer. It was small, with five or six two-person tables inside and another two outside—only seven or eight tables in total. Refinement seemed to be its whole philosophy. The food arrived quickly, around seven or eight minutes after we ordered. We each had a bowl of braised beef noodles for ¥39. The menu labeled it “mildly spicy,” but there was no heat at all, which suited someone from the “cannot handle spice planet” like me perfectly. The noodles came with a small side dish of cabbage pickled in-house with vinegar and xiaomila chilies. It was refreshing and crunchy. The bowl did not look large, but it was surprisingly deep, and the portion was just right for both of us. Extra noodles cost money, though the standard serving was already sufficient. The broth was full-bodied, led by savory saltiness, with a faint sweetness and an almost imperceptible sour-spicy note when tasted carefully. It was exactly the classic flavor of proper braised beef noodles. The noodles were smooth, springy, and resilient. More importantly, they held onto the broth, so it never felt as though the noodles and soup were two separate things. The bowl also included softened lettuce and a soy-braised soft-boiled egg. The real star, of course, was the beef. As the writing on the wall proclaimed, “We use only fresh yellow cattle.” The beef flavor was especially pronounced, and the pieces were large. They had been stewed with excellent timing: the fibers were tender, nothing lodged between my teeth, and the seasoning had penetrated fully. The best part was the soft, almost sticky section in the middle—perhaps the center of the beef shank, though I am not certain. It elevated the texture and softened what might otherwise have been a slightly lean and dry cut. The web of gelatin inside could practically be pressed apart on the tongue. Wild Strawberry Free Market Opening hours: 11:00–20:00 Address: 176 Lujiang Road, Siming District Price: Main dishes felt like about ¥40 on average; desserts about ¥30 After the beef noodles, both of us were far too full and had failed to transform into David Day 大胃袋, “Big Stomach” . We wandered around the park until roughly 3 p.m., then arrived at Wild Strawberry, a place some of our classmates often visited. It is close to the First Pier and the Twin Towers; walk less than 200 meters west after leaving and you will reach Zhongshan Road. Travelers with time to spare may want to take a look. The shop was not small—it occupied two fairly spacious floors—but the owner had created a distinctive atmosphere. Every table, chair, and even decorative object was different. The rooms were packed with things, like an exhibition assembled from finds at a second-hand market. The whole place carried the owner's unmistakable personal style. The menu covered everything from full meals to desserts. We were simply too full to order mains, so we each picked a dessert, and even those portions exceeded our expectations. The pistachio gelato had the texture you would expect from authentic gelato. It was not overly airy and did not vanish the instant it entered my mouth. Pistachio provided the dominant flavor, with a few crushed nuts, while the relatively low dairy-fat content kept it refreshing rather than greasy. The “truffle” was essentially a black-truffle chocolate cake. A light layer of cream covered the outside, dusted generously with cocoa powder. The chocolate was slightly bitter and the cream sweet; eaten together, they reached a reasonably good balance. Even after all the desserts we had already eaten, it did not feel excessively rich. The black-truffle flavor was not especially obvious, although there was indeed a lingering aroma reminiscent of truffle oil. The coconut soufflé surprised me the most. Soufflés seem to have become a new online craze recently, with little specialty shops appearing everywhere. This one did not have the factory-made texture of a mass-produced dessert; it had not even set completely. When we split it with a fork, a faint, warm egg aroma rose toward my nose. Instead of the hollow airiness of something packed with improvers, it felt soft, springy, and substantial. Dipping it in the salted cream on the side cut through the richness. There were also tart mango pieces and mildly sweet coconut chunks. I did not dare try the bayberry. Cocoa Protagonist: Xiamen Chocolate Museum Opening hours: 10:00–23:00 Address: 20 meters west of the intersection of Zhongshan Road Pedestrian Street and Taishan Road, Siming District Price: Free It will not be the most educational museum you ever visit, nor the most beautiful, but it may well be the best-smelling museum you have ever entered. The museum was small, with only two floors, but it was filled with sculptures made from chocolate, ranging from tiny and delicate to large and impressive. Thoughtful price tags even told visitors how much they would have to pay if they damaged one 😂. Staff kept feeding us little snacks along the route. It is not worth making a special journey for, but since it sits on Zhongshan Road not far from Wild Strawberry, it makes a pleasant stop along the way. Tutou Bar Not Recommended Opening hours: 14:30–20:30 Address: Near the Jimei Ancestral Temple, Jimei School Village Price: About ¥80 per person At the very end of the day, we returned to the sour-cream curry we had originally planned to eat. We doubled back and arrived at this little shop. It was tiny and tucked inside an alley; anyone not deliberately searching for it would certainly miss it. There were only four or five tables. The decor followed the familiar formula of an internet-famous niche café—full of personality that somehow did not feel very distinctive. The owner was attentive: he found a storage basket for my backpack, placed it inside, and slid the basket beside me. Unfortunately, the food itself did not satisfy me, or at least did not justify its price. We ordered a sour-cream chicken curry for ¥45, a ¥55 pasta dish whose name I cannot remember that supposedly used a chili sauce made by the owner's mother, plus a pineapple slush and a sweet-milk slush for ¥30 each. Starting with the drinks, I had the pineapple slush. It tasted like an ordinary instant pineapple drink mixed a little weak and poured through a slush machine. Nothing about it stood out, and there is not much more to say. The curry, to be fair, tasted good. But for the signature dish, neither its flavor nor its character was particularly clear. The curry itself was mild, containing chopped and crushed chicken and irregular pieces of potato stewed until very soft. The potato had almost dissolved into the sauce, making it good for mixing with rice. The minced meat retained some texture and filled the sauce, while the chicken—probably thigh meat—was not dry. Yet the sour cream was so subtle that it added no especially distinctive flavor. You could easily spend just over half as much elsewhere and get curry rice of comparable quality. The rice was another major deduction: it lacked any rice aroma, did not contain enough moisture, and tasted utterly ordinary. Perhaps we arrived too late—the curry was already a little cool, and the rice had dried out as well. Finally, the pasta. We originally wanted the pesto pasta, but then came the classic “indie-shop owner” routine: “Have you had pesto before? If not, I would not recommend it to beginners. Most people will not like it. I recommend this one instead.” So we accepted the owner's suggestion. The menu called it handmade pasta, but the photo makes it obvious that it was not. Truly handmade pasta should not only have a shape better suited to holding the sauce; the egg-to-water ratio should also make it chewier, rather than giving it the same texture as supermarket pasta. As for the sauce, it had heat but no fragrance. There is a Chinese saying that serving food hot makes it taste three times fresher, yet even straight from the kitchen this sauce felt dull and left no strong impression. Besides being expensive, the portion was not enough to feed one person. Here are a few other photos from the trip to finish: And perhaps my university years deserve a speeding ticket too.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>My four years at university passed unbelievably quickly. It feels as though it was only yesterday that I first walked into the dorm as a freshman, met my roommates, and exchanged greetings with them. In the blink of an eye, it was already time to graduate.</p>\n<p>Xiamen is a beautiful city and an exceptionally comfortable place to live: its warm, humid climate; clean and orderly urban landscape; distinctive Minnan architecture; all kinds of greenery; bougainvillea blooming everywhere; and its broad, pristine bays... All of this made the city feel precious to me and made leaving difficult. My only regret was that, over those four years, I did not have more time and opportunities to wander through Xiamen and get to know it properly.</p>\n<p>To make up for that regret, I invited a classmate out for one more food crawl before graduation. We went looking for small shops with character and a personal touch. There were pleasant surprises, and there were disappointments too.</p>\n<blockquote>\n<p>All the photos were taken on a phone and left unedited, so please excuse the quality, haha.</p>\n</blockquote>\n<h2>PuKu Fresh Doughnut Specialty Shop</h2>\n<p>Opening hours: 10:30–20:00</p>\n<p>Address: Near the Jimei Ancestral Temple, Jimei District</p>\n<p>Price: The mochi doughnuts we ordered were ¥15 each</p>\n<div class=\"photo-scroll\">\n  <img src=\"https://img.markxu.icu/img58f5cb7a0b3f1be094f1f95f35aa5b31.png\" alt=\"Storefront\" />\n  <img src=\"https://img.markxu.icu/imgd15c3a7b56db1c7a96d1cc2b875c1821.png\" alt=\"Mochi doughnut\" />\n  <img src=\"https://img.markxu.icu/imgc2892ed8360fbc2529e909bab9d7705c.png\" alt=\"Matcha mochi doughnut\" />\n  <img src=\"https://img.markxu.icu/img2547117f7870a557842c500371dc648e.png\" alt=\"Preserved-plum guava mochi doughnut\" />\n  <img src=\"https://img.markxu.icu/imged40a5151fb57c4bffd74e0718716672.png\" alt=\"Inside the shop\" />\n  <img src=\"https://img.markxu.icu/imgaa03b8055f8c200b1bab97a0bef95b44.png\" alt=\"Inside the shop\" />\n</div>\n<p>We had originally come at lunchtime for sour-cream curry, only to discover that the shop did not open until 14:30. With that plan foiled, we had no choice but to look elsewhere. On our way back, we suddenly noticed this inconspicuous little shop. It did not even have a large sign; we only stopped because a sidewalk board happened to mention doughnuts.</p>\n<p>The shop was spotless and almost minimalist, with mostly wooden furniture. It felt simple yet warm—an ideal place to sit for a while and take a couple of photos on a sunny day.</p>\n<p>As the saying goes, “Life is bitter, so have some mochi. Hello everyone, I am...” Ahem. We ordered two matcha mochi doughnuts and one preserved-plum guava mochi doughnut.</p>\n<p>The texture was genuinely excellent. The inside was remarkably elastic, while the shell was crisp. When I bit into it, I could feel the gluten pulling with a satisfying resilience, but it was calibrated perfectly and never became awkwardly chewy 🥲. The shell did not crumble everywhere like an ordinary pastry, either. Instead, it cracked naturally as the interior stretched and clung to the dough, making that combination of crispness and bounce unmistakable in every bite.</p>\n<p>The fillings deserve an even closer description 😋. On a hot day, the center felt wonderfully cool, with a rich texture like ice cream on the verge of melting, but none of the unpleasant iciness of frozen food. The matcha was intense without the usual tea astringency. The preserved-plum guava filling was more delicate, carrying the clean sweetness of ripe guava and tiny pieces of fruit. Pressing it against the roof of my mouth with my tongue revealed a surprisingly rich series of textures and flavors, although I could not really taste much of the preserved plum.</p>\n<p>The dough itself was only faintly sweet; almost all the sweetness came from the filling, where it served as a background note supporting the matcha and fruit aromas. Even after the filling was gone, the fragrance left in my mouth lasted long enough for me to enjoy the rest of the doughnut.</p>\n<h2>Xiangkou Braised Beef Noodles</h2>\n<p>Opening hours: 11:00–14:00, 17:00–21:00</p>\n<p>Address: First floor of Xianghe Plaza, near Tuihua Park, Siming District</p>\n<p>Price: About ¥45 per person</p>\n<div class=\"photo-scroll\">\n  <img src=\"https://img.markxu.icu/img70adb0dffff60c301974ed0161db6ece.png\" alt=\"Storefront\" />\n  <img src=\"https://img.markxu.icu/img9836b25c5e83160778f4511c9c468349.png\" alt=\"Inside the restaurant\" />\n  <img src=\"https://img.markxu.icu/img1464c45b9c69648dee239b4d03b295ca.png\" alt=\"Inside the restaurant\" />\n  <img src=\"https://img.markxu.icu/imge7aae59d9657d5a40d014b77c76546a2.png\" alt=\"Braised beef noodles\" />\n  <img src=\"https://img.markxu.icu/img20260603000920074.png\" alt=\"Braised beef noodles\" />\n</div>\n<p>After finishing the mochi doughnuts, we immediately called a taxi into Xiamen Island and switched our plans to this beef-noodle shop. It was hidden beneath a very old, nearly abandoned shopping center. Even the lettering on the building looked so peculiar that, for a moment, I thought the world had glitched.</p>\n<p><img src=\"https://img.markxu.icu/img20260603001315979.png\" alt=\"The entrance is on the right side of this shopping center\"></p>\n<p>The shop was clean as well, but this one felt a little warmer. It was small, with five or six two-person tables inside and another two outside—only seven or eight tables in total. Refinement seemed to be its whole philosophy.</p>\n<p>The food arrived quickly, around seven or eight minutes after we ordered. We each had a bowl of braised beef noodles for ¥39. The menu labeled it “mildly spicy,” but there was no heat at all, which suited someone from the “cannot handle spice planet” like me perfectly.</p>\n<p>The noodles came with a small side dish of cabbage pickled in-house with vinegar and xiaomila chilies. It was refreshing and crunchy. The bowl did not look large, but it was surprisingly deep, and the portion was just right for both of us. Extra noodles cost money, though the standard serving was already sufficient.</p>\n<p>The broth was full-bodied, led by savory saltiness, with a faint sweetness and an almost imperceptible sour-spicy note when tasted carefully. It was exactly the classic flavor of proper braised beef noodles. The noodles were smooth, springy, and resilient. More importantly, they held onto the broth, so it never felt as though the noodles and soup were two separate things. The bowl also included softened lettuce and a soy-braised soft-boiled egg.</p>\n<p>The real star, of course, was the beef. As the writing on the wall proclaimed, “We use only fresh yellow cattle.” The beef flavor was especially pronounced, and the pieces were large. They had been stewed with excellent timing: the fibers were tender, nothing lodged between my teeth, and the seasoning had penetrated fully. The best part was the soft, almost sticky section in the middle—perhaps the center of the beef shank, though I am not certain. It elevated the texture and softened what might otherwise have been a slightly lean and dry cut. The web of gelatin inside could practically be pressed apart on the tongue.</p>\n<h2>Wild Strawberry Free Market</h2>\n<p>Opening hours: 11:00–20:00</p>\n<p>Address: 176 Lujiang Road, Siming District</p>\n<p>Price: Main dishes felt like about ¥40 on average; desserts about ¥30</p>\n<div class=\"photo-scroll\">\n  <img src=\"https://img.markxu.icu/img20260603004114559.png\" alt=\"Storefront\" />\n  <img src=\"https://img.markxu.icu/img20260603005149021.png\" alt=\"First-floor interior\" />\n  <img src=\"https://img.markxu.icu/img9cbbcefce4c740db1ab7099ac0187734.png\" alt=\"Second-floor interior\" />\n  <img src=\"https://img.markxu.icu/imgb8a4fbc9460e559666e5136536fc5ee3.png\" alt=\"Second-floor interior\" />\n  <img src=\"https://img.markxu.icu/img3797e81b82eeeb75f991ea85d1f8e151.png\" alt=\"Second-floor interior\" />\n  <img src=\"https://img.markxu.icu/imgb7d3ab1d77ff01a5432bf12b1dc2b09a.png\" alt=\"Second-floor interior\" />\n  <img src=\"https://img.markxu.icu/img20260603005151612.png\" alt=\"Second-floor restroom\" />\n  <img src=\"https://img.markxu.icu/img7cf4f4f2a2270538a3b10f2eb2a13a46.png\" alt=\"Finally getting the cucumber water I had been craving\" />\n</div>\n<p>After the beef noodles, both of us were far too full and had failed to transform into David Day (大胃袋, “Big Stomach”). We wandered around the park until roughly 3 p.m., then arrived at Wild Strawberry, a place some of our classmates often visited. It is close to the First Pier and the Twin Towers; walk less than 200 meters west after leaving and you will reach Zhongshan Road. Travelers with time to spare may want to take a look.</p>\n<p>The shop was not small—it occupied two fairly spacious floors—but the owner had created a distinctive atmosphere. Every table, chair, and even decorative object was different. The rooms were packed with things, like an exhibition assembled from finds at a second-hand market. The whole place carried the owner's unmistakable personal style.</p>\n<div class=\"photo-scroll-vertical\">\n  <img src=\"https://img.markxu.icu/img20260603005418459.png\" alt=\"Menu 1\" />\n  <img src=\"https://img.markxu.icu/img20260603005421759.png\" alt=\"Menu 2\" />\n  <img src=\"https://img.markxu.icu/img20260603005424537.png\" alt=\"Menu 3\" />\n  <img src=\"https://img.markxu.icu/img20260603005427148.png\" alt=\"Menu 4\" />\n</div>\n<div class=\"photo-scroll\">\n  <img src=\"https://img.markxu.icu/img3b095270f69fdb5bedb37c42c7900dbe.png\" alt=\"Pistachio gelato\" />\n  <img src=\"https://img.markxu.icu/imgf69eb875161225768b94de2d46c9b80b.png\" alt=\"Truffle cake\" />\n  <img src=\"https://img.markxu.icu/imgff6fdca35317f25aac77d3515a27a379.png\" alt=\"Inside the truffle cake\" />\n  <img src=\"https://img.markxu.icu/img20260602232937241.png\" alt=\"Coconut soufflé\" />\n  <img src=\"https://img.markxu.icu/imgc984cb0c70751d7ca0e4dd20f48bfc7b.png\" alt=\"Coconut soufflé\" />\n</div>\n<p>The menu covered everything from full meals to desserts. We were simply too full to order mains, so we each picked a dessert, and even those portions exceeded our expectations.</p>\n<p>The pistachio gelato had the texture you would expect from authentic gelato. It was not overly airy and did not vanish the instant it entered my mouth. Pistachio provided the dominant flavor, with a few crushed nuts, while the relatively low dairy-fat content kept it refreshing rather than greasy.</p>\n<p>The “truffle” was essentially a black-truffle chocolate cake. A light layer of cream covered the outside, dusted generously with cocoa powder. The chocolate was slightly bitter and the cream sweet; eaten together, they reached a reasonably good balance. Even after all the desserts we had already eaten, it did not feel excessively rich. The black-truffle flavor was not especially obvious, although there was indeed a lingering aroma reminiscent of truffle oil.</p>\n<p>The coconut soufflé surprised me the most. Soufflés seem to have become a new online craze recently, with little specialty shops appearing everywhere. This one did not have the factory-made texture of a mass-produced dessert; it had not even set completely. When we split it with a fork, a faint, warm egg aroma rose toward my nose. Instead of the hollow airiness of something packed with improvers, it felt soft, springy, and substantial. Dipping it in the salted cream on the side cut through the richness. There were also tart mango pieces and mildly sweet coconut chunks. I did not dare try the bayberry.</p>\n<h2>Cocoa Protagonist: Xiamen Chocolate Museum</h2>\n<p>Opening hours: 10:00–23:00</p>\n<p>Address: 20 meters west of the intersection of Zhongshan Road Pedestrian Street and Taishan Road, Siming District</p>\n<p>Price: Free</p>\n<div class=\"photo-scroll\">\n  <img src=\"https://img.markxu.icu/img20260603011421693.png\" alt=\"Chocolate Twin Towers\" />\n  <img src=\"https://img.markxu.icu/img20260603011424344.png\" alt=\"Chocolate Xiamen University\" />\n  <img src=\"https://img.markxu.icu/img20260603011426639.png\" alt=\"Chocolate pipe organ\" />\n  <img src=\"https://img.markxu.icu/img20260603011429410.png\" alt=\"Chocolate statue of Zheng Chenggong\" />\n</div>\n<p>It will not be the most educational museum you ever visit, nor the most beautiful, but it may well be the best-smelling museum you have ever entered.</p>\n<p>The museum was small, with only two floors, but it was filled with sculptures made from chocolate, ranging from tiny and delicate to large and impressive. Thoughtful price tags even told visitors how much they would have to pay if they damaged one 😂. Staff kept feeding us little snacks along the route. It is not worth making a special journey for, but since it sits on Zhongshan Road not far from Wild Strawberry, it makes a pleasant stop along the way.</p>\n<h2>Tutou Bar (Not Recommended)</h2>\n<p>Opening hours: 14:30–20:30</p>\n<p>Address: Near the Jimei Ancestral Temple, Jimei School Village</p>\n<p>Price: About ¥80 per person</p>\n<div class=\"photo-scroll\">\n  <img src=\"https://img.markxu.icu/imgc1bfd7747db5d8cae090fb9e23d3583b.png\" alt=\"Inside the shop\" />\n  <img src=\"https://img.markxu.icu/img9173ba87266c98c6709241997dd3d7e6.png\" alt=\"Inside the shop\" />\n  <img src=\"https://img.markxu.icu/img01666273752a4da625dd8a43ca652a43.png\" alt=\"Dinner photographed by my classmate\" />\n</div>\n<p>At the very end of the day, we returned to the sour-cream curry we had originally planned to eat. We doubled back and arrived at this little shop.</p>\n<p>It was tiny and tucked inside an alley; anyone not deliberately searching for it would certainly miss it. There were only four or five tables. The decor followed the familiar formula of an internet-famous niche café—full of personality that somehow did not feel very distinctive. The owner was attentive: he found a storage basket for my backpack, placed it inside, and slid the basket beside me. Unfortunately, the food itself did not satisfy me, or at least did not justify its price.</p>\n<p>We ordered a sour-cream chicken curry for ¥45, a ¥55 pasta dish whose name I cannot remember that supposedly used a chili sauce made by the owner's mother, plus a pineapple slush and a sweet-milk slush for ¥30 each.</p>\n<p>Starting with the drinks, I had the pineapple slush. It tasted like an ordinary instant pineapple drink mixed a little weak and poured through a slush machine. Nothing about it stood out, and there is not much more to say.</p>\n<p>The curry, to be fair, tasted good. But for the signature dish, neither its flavor nor its character was particularly clear. The curry itself was mild, containing chopped and crushed chicken and irregular pieces of potato stewed until very soft. The potato had almost dissolved into the sauce, making it good for mixing with rice. The minced meat retained some texture and filled the sauce, while the chicken—probably thigh meat—was not dry. Yet the sour cream was so subtle that it added no especially distinctive flavor. You could easily spend just over half as much elsewhere and get curry rice of comparable quality. The rice was another major deduction: it lacked any rice aroma, did not contain enough moisture, and tasted utterly ordinary. Perhaps we arrived too late—the curry was already a little cool, and the rice had dried out as well.</p>\n<p>Finally, the pasta. We originally wanted the pesto pasta, but then came the classic “indie-shop owner” routine: “Have you had pesto before? If not, I would not recommend it to beginners. Most people will not like it. I recommend this one instead.” So we accepted the owner's suggestion. The menu called it handmade pasta, but the photo makes it obvious that it was not. Truly handmade pasta should not only have a shape better suited to holding the sauce; the egg-to-water ratio should also make it chewier, rather than giving it the same texture as supermarket pasta. As for the sauce, it had heat but no fragrance. There is a Chinese saying that serving food hot makes it taste three times fresher, yet even straight from the kitchen this sauce felt dull and left no strong impression. Besides being expensive, the portion was not enough to feed one person.</p>\n<p>Here are a few other photos from the trip to finish:</p>\n<div class=\"img-grid-2\">\n  <img src=\"https://img.markxu.icu/img541775d2fe5dc898c3d7f02a6b631784.png\" alt=\"Jimei School Village at night\" />\n  <img src=\"https://img.markxu.icu/imgfc52cce5512cb72afde54fe996e0f758.png\" alt=\"A street somewhere around Zhongshan Road\" />\n</div>\n<p><img src=\"https://img.markxu.icu/imga45f872aac1ea98ec36e4d83f4332849.png\" alt=\"Someone give summer a speeding ticket\"></p>\n<p>And perhaps my university years deserve a speeding ticket too.</p>\n",
      "summary": "[AI-translated] With only a few days left before graduation, I was reluctant to leave Xiamen, so a classmate and I visited several interesting eateries around the city—some delightful, others disappointing.",
      "date_published": "2026-06-02T00:00:00.000Z",
      "image": "https://img.markxu.icu/img20260602232937241.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/what-do-computer-science-majors-learn-en",
      "url": "https://markxu.icu/blog/what-do-computer-science-majors-learn-en",
      "title": "What Do Computer Science Majors Actually Learn?",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. Whenever I chat with the elders in my family, or introduce my major to younger students who are getting ready to choose one for the gaokao, I get asked the same question: What do you computer science people actually study? The question looks simple, but answering it clearly is not easy. Because the word “computer” is far too broad in everyday life, and for anyone with even a passing knowledge of technology, there are also far too many directions within it. If you have never studied the major yourself, plenty of misconceptions remain. Some people think studying computers means writing code, some think it means repairing PCs and installing operating systems, and others think it means making games, building websites, or doing artificial intelligence. All of these are related to computers, but each is only one direction within the field, and they have little to do with what “computer science” means in undergraduate education. Undergraduate “Computer Science and Technology” is mainly a complete system built around “how to make machines process information reliably, efficiently, and automatically.” It begins with mathematics to lay the logical foundation, then expresses everything clearly in code; but what matters most is the abstraction, systems thinking, engineering, and problem decomposition behind it all. So I want to write a simple user manual. Next time someone asks me “What do you study in computer science?”, I can just send them this article, which beats explaining it from scratch every time and still not being clear or complete. Of course, the curriculum differs from school to school, and I have never personally taken computer science courses at a top university. Fortunately, Berkeley and MIT publish many open courses, and through them I have caught a glimpse of how top schools think about education. What follows is mainly based on my own school—an ordinary first-tier university—and the content common to most domestic “Computer Science and Technology” undergraduate programs, plus what I learned from those open courses. First, a Misconception: Computer Science Is Not “Learning a Piece of Software” Many people's first impression of a computer science major is whether you can fix a computer, install an operating system, make a PowerPoint deck, or edit a video. These are certainly useful skills, but they are not the core of the major. Otherwise you would study mechanical engineering just to turn screws, architecture just to move bricks, and food science just to put chocolate chips on Chips Ahoy cookies. In undergraduate education, computer science mainly cares about: Why can a program run at all? How is the data inside a program organized and processed? How does the network move information from one place to another? Why can an operating system run many programs at the same time? How is a large system designed, developed, tested, and maintained? How do algorithms solve problems while using as little time and as few resources as possible? Next, I will also discuss mainstream directions such as artificial intelligence, databases, graphics, cybersecurity, and embedded systems, sharing what I have come to understand about them over the past few years and what problems each one is trying to solve. Now let us go bottom-up, layer by layer, through where the ideas of computing come from, what our learning goals are, and the problems and detours I ran into along the way. Layer One: Mathematical Foundations When we first entered university, many classmates were surprised: “Why am I majoring in computers, and the first thing I have to study is this much math?” During freshman year we would get together and complain about exactly this, but later on we realized that mathematics is not just the algebra from elementary, middle, and high school—that is called elementary mathematics. What undergraduates study is broader and deeper, and the foundations really do matter. Calculus, linear algebra, probability and statistics, and discrete mathematics are basically unavoidable. Some schools even cover numerical analysis, combinatorics, or operations research. Calculus focuses on continuous change and the ideas of differentiation and integration; linear algebra shows up constantly in graphics, machine learning, and data processing; probability and statistics underpin artificial intelligence, data analysis, and stochastic processes; and discrete mathematics is especially close to computing—sets, logic, graphs, trees, relations, recursion. These sound like mathematical concepts, but they keep reappearing later in data structures, algorithms, databases, compilers, and artificial intelligence. I happened to study discrete mathematics fairly seriously back then, and later it gave me a feeling of understanding one thing and grasping many others. Of course, there is no need to be intimidated. For an ordinary student who does not plan to keep struggling at the frontier of technology, there is really no need to push mathematics especially hard . Even if you forget most of it later, it will not affect your job search or your ability to handle ordinary front-end and back-end programming problems. Most undergraduate math courses are just cultivating a way of thinking, so that when you meet complex concepts later you are not completely without a handhold. The problems I ran into: Over my four years of university, if I had simply followed the school's courses step by step, I most likely would not have gained any deep understanding of mathematics. To make assessment easy to quantify, school courses do not test your mathematical understanding or your way of thinking; they mainly test your ability to solve problems. I think this is a problem with how schools assess students. Take the most telling example: almost every classmate around me understands linear algebra as computing determinants, multiplying matrices, finding eigenvectors... and not one of them thinks about its geometric meaning. My impression is that we focus too much on the “algebra” part of linear algebra and neglect the idea of “linear,” so that most students, even after studying artificial intelligence or graphics, do not understand why matrices have to be there I will put together a separate article about this sometime . Only by teaching myself Professor Gilbert Strang's 18.06 course and 3Blue1Brown's animated explanations did I begin to grasp the concepts and meaning of mathematics from the ground up. Layer Two: Programming Foundations Anyone who studies computer science has to be able to program 👋😭👋! The usual introductory languages are C, C++, or Python, depending on the school. Most schools emphasize C at the start because it is closer to the metal you could almost call it the origin of everything , and it helps you understand foundational concepts like memory, pointers, arrays, and function calls. Once you have those down, learning other languages is much faster I highly recommend CS 50 here . Some schools use Python or Java for beginners, because it is easier to produce something that runs, which lowers the barrier to entry. At the beginning, the content usually covers variables, conditionals, loops, functions, arrays, structs, classes and objects, and file reading and writing. It looks like you are learning language syntax, but in essence you are training how to break a real-world problem into steps a machine can execute. Many people think the hard part of coding is memorizing syntax, when in fact syntax is only the surface. The genuinely hard part is understanding the problem, breaking it down, designing the flow, and handling edge cases. A simple example: write a program that computes the average of a set of grades. It looks simple, but what if the data is empty? What if the input is malformed? What if there are so many grades that they do not fit in memory? What if you need to display the result live on a web page? Dig into one problem layer by layer, pin down the requirements, and apply basic variables and logic, and it gradually turns from “a small feature that takes a few lines of code” into “designing a complete system.” The point of an introductory programming course is not to teach you a particular language, but to get you into the habit—for the first time—of organizing your thoughts the way a program does. The problems I ran into: Problems caused by assessment: Most computer science instructors now read off slides, hand out assignments, and grade mainly on written reports. Now that AI has taken off, most of the thinking practice can be handled by simply sending the AI a set of requirements, with almost no brainpower required. Classmates around me who chase GPA, wanting high marks from the instructor, choose the more conservative approach, avoid making mistakes, and use AI to do the main work directly, spending more energy polishing the report. As a result, I know plenty of classmates who can barely program but are quite good at documentation. Problems with how courses are designed: The quality and difficulty of the assignments I got in these foundational courses were clearly different from Berkeley's assignments. What we eventually completed was roughly at the level of Berkeley's homework before the midterm, and it is hard to go deep enough through a single project for students to learn to break a problem down clearly and properly. And I have a very clear and strong feeling: most of the instructors who taught our courses were constrained by the required class hours, and they did not really understand the language themselves. The C++ instructor's use of pointers, for instance, stopped at the basic usage, and the Python instructor's knowledge of Python's standard libraries was limited to what was in the course slides—not to mention the farce of writing code in class that would not run and then explaining that it was pseudocode. These problems mean that my classmates' programming foundations were never deep or systematic to begin with, so afterward they even had to take an extra language course to shore them up. Layer Three: Data Structures and Algorithms If I could pick only one course that best represents the fundamentals of a computer science major, I would probably pick data structures and algorithms. Data structures study how to store data; algorithms study how to solve problems. Arrays, linked lists, stacks, queues, trees, graphs, hash tables, and heaps are all common data structures. Sorting, searching, recursion, dynamic programming, greedy algorithms, and graph algorithms are common algorithmic ideas. This course was the most painful for me at first, because unlike building a web page, you do not see results right away, and unlike making a small piece of software, there is no clear interface giving you immediate positive feedback. It is like practicing internal martial arts: a lot of it looks easy to understand and easy to grasp, but most of the time you are failing over and over, looking for a way out. Data structures and algorithms together form the foundation of every program. It is also common for two excellent products to occupy the same niche with the same purpose but different data structures. Take Notion and Obsidian, both used for “taking notes.” Their interfaces both support pages, nesting, and links, but the data management underneath is completely different. Notion breaks documents into Blocks and uses a tree structure to manage hierarchy and nesting; every operation is essentially moving and collapsing subtrees. Obsidian treats each note as a node in a graph , creating edges through bidirectional links and maintaining associations and backlinks with an adjacency list. Put simply, Notion is like organizing a multi-level set of folders, while Obsidian is more like weaving a spider web. The former cares about hierarchy; the latter cares about connection points. Precisely because of this, even though both are “organizing notes,” Notion is asking “who belongs to whom” while Obsidian is asking “who is related to whom”— data structures do not change what you can do, but they determine how you think about it. So data structures and algorithms do not exist for the sake of grinding LeetCode. Grinding problems is only one form of training; what it really trains is: when facing a problem, how to choose the right abstraction and solution strategy. Layer Four: The Computer as a Whole System The biggest difference between a computer science major and pure software training is that it asks you to understand “what happens underneath the code.” This part usually includes courses such as computer organization, operating systems, computer networks, database systems, and compilers. Computer organization tells you how the CPU, memory, instructions, registers, and caches work together—that is, how everything on your screen is transformed by binary zeros and ones across electronic components and finally becomes software or a game. Operating systems covers processes, threads, memory management, file systems, scheduling, and deadlocks. You will understand why a computer can run many programs at once, why programs freeze, why running out of memory slows things down, and why poorly written multithreaded code produces strange bugs. Computer networks covers TCP/IP, HTTP, DNS, routing, and congestion control. Every day when you open a web page, send a message, or watch a video, you are using these protocols. After the course, at least you will know that “the network is bad” is not a universal explanation—behind it may be latency, packet loss, bandwidth, DNS, the server, the client, and a pile of other links all contributing. Databases covers the relational model, SQL, indexes, transactions, concurrency control, and data consistency. It solves the problem of how to store data long-term, reliably, and in an organized way, while keeping things from falling apart when many people access it at once. Compilers covers how a language goes from source code to a form the machine can execute. Lexical analysis, syntax analysis, semantic analysis, optimization, code generation—it sounds hardcore, but it will help you understand that programming languages did not appear out of nowhere; they are systems that can be designed and implemented. This course really tests your logic, and it is not easy. Together these courses form the systemic foundation of the major. You may not go on to work directly on operating systems or compilers, but understanding this material gives you a stronger sense of “what is underneath” when you face complex problems. If you study deeply enough, you can even work out the underlying direction when you hit a strange bug—for example, when I was reading this post, by the later part of the problem description I guessed that it might be related to floating-point arithmetic caused by historical reasons. The Final Layer: Specialized Direction Courses By junior and senior year, many schools offer direction courses or electives. They vary noticeably between schools, but the common directions are roughly the following. You can also use csdiy.wiki to find the courses you want if you are very capable, you can consult the author's reviews; for an ordinary student like me, everything took considerably longer than the author reported, felt somewhat harder, and I definitely did not remember it all after finishing . Artificial intelligence and machine learning. This includes machine learning, deep learning, natural language processing, computer vision, data mining, and more. It is very popular right now, but actually studying it is not just “prompting a large model.” You need to understand basic concepts like data, models, training, evaluation, overfitting, loss functions, and optimization methods, and you also need to learn frameworks such as PyTorch. Getting started really is exciting—watching the loss go down during training genuinely gives you a kind of simple, slightly silly happiness. Networks and information security. This includes cryptography, network security, system security, Web security, reverse engineering, and more. I know almost nothing about this area; I only once had the chance to interview cybersecurity staff in the police and at a company, and picked up a little knowledge. I know market demand in this area is still growing, but to really go deeper, that becomes the content of the cybersecurity major itself. Embedded systems and the Internet of Things. This leans toward the combination of hardware and systems, and may involve microcontrollers, sensors, real-time systems, embedded Linux, and so on. It cares more about how a program runs stably on a device with limited resources. Software development and engineering practice. This includes Web development, mobile development, cloud computing, microservices, software testing, DevOps, and more. This direction is closely tied to actual employment and emphasizes engineering ability and system development experience. In freshman year, our dean now a vice president taught us Introduction to Computers, and in class he recommended the book The Mythical Man-Month. When I saw that it was from the 1970s, I thought the dean really was an “old-timer” no disrespect intended . It was not until I organized a student innovation project and actually built something that I remembered the teacher's recommendation, and realized that the pitfalls I was hitting had already been given better solutions by others fifty years earlier. If you want to understand the development process, The Mythical Man-Month is a storybook with no barrier to entry that runs through the whole process. Graphics, games, and virtual reality. This involves geometry, rendering, physical simulation, engines, interaction, and more. It looks cool, but behind it is a great deal of mathematics, algorithms, and engineering detail. I once studied GAMES101, but eventually I really had no time and the course was a bit hard, so I gave up. Even so, that brief exposure made me pay close attention to a game engine's rendering logic when I play games, and I often watch Unreal Engine presentations to understand this dazzling technology. In fact, graphics is not only knowledge for game engines; there are many directions behind it, which I will introduce later. Big data and databases. This includes distributed systems, data warehousing, data analysis, recommendation systems, and more. The problems it handles usually involve large data scale, high concurrency, and complex systems. I have not gone deep into big data, so I am not in a position to offer an expert assessment. These directions are not isolated from one another. Artificial intelligence, for instance, needs algorithms and mathematics, but also systems and engineering; cybersecurity needs operating systems and networking fundamentals; back-end development cannot do without databases, networks, and software engineering. That is the nature of a computer science major: the further you go, the more you find that many courses connect to one another. As I see it, no one can learn all of the directions above thoroughly, or even take them all once. Even specializing in a single direction takes years. My exploration over four years of university was not small, but deeper study afterward is more or less the direction and purpose of graduate school. For specifics, see this Zhihu answer: Should computer science undergraduates spend a lot of time writing compilers and operating systems, or is that a distraction from their proper work? - Freezy's answer - Zhihu What Does Studying Computer Science Require? I think the most important thing is not being a “math genius,” nor “having written code since childhood.” What matters more is a few rather plain but rare abilities. First, a willingness to look things up yourself. The field changes fast, and many problems will no longer have a teacher walking you through them step by step—many classmates will not have the time to teach you hand in hand either. Error messages, official documentation, search engines, open-source projects, papers, and community discussions all become part of your learning. Even in today's AI era, this ability matters especially. Second, being able to tolerate frustration. Program errors are routine, and failing to get an environment set up is perfectly normal. I once spent three days getting an environment configured before it finally ran, and spent a week on one bug, staying up late every day. Maybe that is just me being bad, but judging by experiences shared on forums, it does not seem rare. Third, enjoying breaking problems apart. Computer science rarely solves one huge problem directly; instead it keeps making the problem smaller, turning vague requirements into concrete steps and complex systems into modules. Once this habit of thinking takes hold, even when you are browsing a web page you will unconsciously try to abstract the data structures inside it. It is actually quite similar to force analysis in high school physics. Fourth, a bit of patience for continuous learning. You may have just learned a framework when a new tool appears; you may have just understood one direction when the industry's hot topic shifts again. A computer science major is not well suited to living off what you learned in four years of university for the rest of your life. Especially now that the AI era has arrived, that old knowledge may be reset to zero outright, and keeping up learning may be the only way for ordinary people like us to keep pace with it. Final Thoughts A computer science and technology major covers a lot, and it is scattered. It has mathematics and engineering; theory and practice; it requires understanding the underlying systems and being able to build applications on top. When you first start, it is easy to feel that these courses have nothing to do with one another: math is math, C is C, operating systems are operating systems, databases are databases. But as you go further, you slowly discover that they are all answering the same question: How can information be better represented, stored, transmitted, processed, and used? This article is a very rough introduction, mainly as a record of what I have taken away from four years of continuous study, along with the problems I noticed and my most sincere advice. The learning experience of an ordinary student at a university outside the 985 and 211 tiers, who is full of uncertainty about the future, cannot cover everything, but at least I hope it helps parents and students interested in computers form a basic impression: computer science is not a pile of math, nor simply typing code. It is understanding every layer underneath the computer. If you do choose to study it, you will most likely run into plenty of bugs that give you headaches, courses you cannot follow, environments that will not configure, and projects you cannot finish. But there will also be some very happy moments: the first time a program runs, the first time a web page goes live, the first time a model produces a result, the first time a small tool you wrote actually helps someone. Those moments make you feel that you really are turning the ideas in your head into reality, little by little. These tiny bits of happiness and positive feedback are probably what attracts me most to computer science.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>Whenever I chat with the elders in my family, or introduce my major to younger students who are getting ready to choose one for the gaokao, I get asked the same question: <strong>What do you computer science people actually study?</strong></p>\n<p>The question looks simple, but answering it clearly is not easy.</p>\n<p>Because the word “computer” is far too broad in everyday life, and for anyone with even a passing knowledge of technology, there are also far too many directions within it. If you have never studied the major yourself, plenty of misconceptions remain.</p>\n<p>Some people think studying computers means writing code, some think it means repairing PCs and installing operating systems, and others think it means making games, building websites, or doing artificial intelligence. All of these are related to computers, but each is only one direction within the field, and they have little to do with what “computer science” means in undergraduate education.</p>\n<p>Undergraduate “Computer Science and Technology” is mainly a complete system built around “how to make machines process information reliably, efficiently, and automatically.” It begins with mathematics to lay the logical foundation, then expresses everything clearly in code; but what matters most is the <strong>abstraction, systems thinking, engineering, and problem decomposition</strong> behind it all.</p>\n<p>So I want to write a simple user manual. Next time someone asks me “What do you study in computer science?”, I can just send them this article, which beats explaining it from scratch every time and still not being clear or complete.</p>\n<p>Of course, the curriculum differs from school to school, and I have never personally taken computer science courses at a top university. Fortunately, Berkeley and MIT publish many open courses, and through them I have caught a glimpse of how top schools think about education. What follows is mainly based on my own school—an ordinary first-tier university—and the content common to most domestic “Computer Science and Technology” undergraduate programs, plus what I learned from those open courses.</p>\n<hr>\n<h3>First, a Misconception: Computer Science Is Not “Learning a Piece of Software”</h3>\n<p>Many people's first impression of a computer science major is whether you can fix a computer, install an operating system, make a PowerPoint deck, or edit a video.</p>\n<p>These are certainly useful skills, but they are not the core of the major. Otherwise you would study mechanical engineering just to turn screws, architecture just to move bricks, and food science just to put chocolate chips on Chips Ahoy cookies.</p>\n<p>In undergraduate education, computer science mainly cares about:</p>\n<ul>\n<li>Why can a program run at all?</li>\n<li>How is the data inside a program organized and processed?</li>\n<li>How does the network move information from one place to another?</li>\n<li>Why can an operating system run many programs at the same time?</li>\n<li>How is a large system designed, developed, tested, and maintained?</li>\n<li>How do algorithms solve problems while using as little time and as few resources as possible?</li>\n</ul>\n<p>Next, I will also discuss mainstream directions such as artificial intelligence, databases, graphics, cybersecurity, and embedded systems, sharing what I have come to understand about them over the past few years and what problems each one is trying to solve.</p>\n<p><strong>Now let us go bottom-up, layer by layer, through where the ideas of computing come from, what our learning goals are, and the problems and detours I ran into along the way.</strong></p>\n<h2>Layer One: Mathematical Foundations</h2>\n<p>When we first entered university, many classmates were surprised: “Why am I majoring in computers, and the first thing I have to study is this much math?”</p>\n<p>During freshman year we would get together and complain about exactly this, but later on we realized that mathematics is not just the algebra from elementary, middle, and high school—that is called elementary mathematics. What undergraduates study is broader and deeper, and the foundations really do matter.</p>\n<p>Calculus, linear algebra, probability and statistics, and discrete mathematics are basically unavoidable. Some schools even cover numerical analysis, combinatorics, or operations research.</p>\n<p>Calculus focuses on continuous change and the ideas of differentiation and integration; linear algebra shows up constantly in graphics, machine learning, and data processing; probability and statistics underpin artificial intelligence, data analysis, and stochastic processes; and discrete mathematics is especially close to computing—sets, logic, graphs, trees, relations, recursion. These sound like mathematical concepts, but they keep reappearing later in data structures, algorithms, databases, compilers, and artificial intelligence. <em>I happened to study discrete mathematics fairly seriously back then, and later it gave me a feeling of understanding one thing and grasping many others.</em></p>\n<p>Of course, there is no need to be intimidated. For an ordinary student who does not plan to keep struggling at the frontier of technology, <strong>there is really no need to push mathematics especially hard</strong>. Even if you forget most of it later, it will not affect your job search or your ability to handle ordinary front-end and back-end programming problems. Most undergraduate math courses are just cultivating a way of thinking, so that when you meet complex concepts later you are not completely without a handhold.</p>\n<blockquote>\n<p><strong>The problems I ran into:</strong></p>\n<p>Over my four years of university, <strong>if I had simply followed the school's courses step by step, I most likely would not have gained any deep understanding of mathematics.</strong></p>\n<p>To make assessment easy to quantify, school courses do not test your mathematical understanding or your way of thinking; they mainly test your ability to solve problems.</p>\n<p>I think this is a problem with how schools assess students. Take the most telling example: almost every classmate around me understands linear algebra as computing determinants, multiplying matrices, finding eigenvectors... and not one of them thinks about its geometric meaning. My impression is that we focus too much on the “algebra” part of linear algebra and neglect the idea of “linear,” so that <strong>most students, even after studying artificial intelligence or graphics, do not understand why matrices have to be there</strong> <em>(I will put together a separate article about this sometime)</em>.</p>\n<p>Only by teaching myself <a href=\"https://www.youtube.com/watch?v=7UJ4CFRGd-U&amp;list=PLE7DDD91010BC51F8\">Professor Gilbert Strang's 18.06 course</a> and <a href=\"https://www.bilibili.com/video/BV1ys411472E/?spm_id_from=333.1387.homepage.video_card.click&amp;vd_source=a1d2d12b1c9f3d1bf63ac9d24e2ff565\">3Blue1Brown's animated explanations</a> did I begin to grasp the concepts and meaning of mathematics from the ground up.</p>\n</blockquote>\n<p><img src=\"https://imgs.markxu.icu/imgs20260527134223611.png\" alt=\"3Blue1Brown's video explanation\"></p>\n<h2>Layer Two: Programming Foundations</h2>\n<p>Anyone who studies computer science has to be able to program 👋😭👋!</p>\n<p>The usual introductory languages are C, C++, or Python, depending on the school. Most schools emphasize C at the start because it is closer to the metal <em>(you could almost call it the origin of everything)</em>, and it helps you understand foundational concepts like memory, pointers, arrays, and function calls. Once you have those down, learning other languages is much faster (I highly recommend <a href=\"https://pll.harvard.edu/course/cs50-introduction-computer-science\">CS 50</a> here). Some schools use Python or Java for beginners, because it is easier to produce something that runs, which lowers the barrier to entry.</p>\n<p>At the beginning, the content usually covers variables, conditionals, loops, functions, arrays, structs, classes and objects, and file reading and writing. It looks like you are learning language syntax, but in essence you are <strong>training how to break a real-world problem into steps a machine can execute.</strong></p>\n<p>Many people think the hard part of coding is memorizing syntax, when in fact syntax is only the surface. The genuinely hard part is understanding the problem, breaking it down, designing the flow, and handling edge cases.</p>\n<p>A simple example: write a program that computes the average of a set of grades.</p>\n<p>It looks simple, but what if the data is empty? What if the input is malformed? What if there are so many grades that they do not fit in memory? What if you need to display the result live on a web page? Dig into one problem layer by layer, pin down the requirements, and apply basic variables and logic, and it gradually turns from “a small feature that takes a few lines of code” into “designing a complete system.”</p>\n<p>The point of an introductory programming course is not to teach you a particular language, but to get you into the habit—for the first time—of organizing your thoughts the way a program does.</p>\n<blockquote>\n<p><strong>The problems I ran into:</strong></p>\n<ol>\n<li>Problems caused by assessment:</li>\n</ol>\n<p>Most computer science instructors now read off slides, hand out assignments, and grade mainly on written reports. Now that AI has taken off, most of the thinking practice can be handled by simply sending the AI a set of requirements, with almost no brainpower required. Classmates around me who chase GPA, wanting high marks from the instructor, choose the more conservative approach, avoid making mistakes, and use AI to do the main work directly, spending more energy polishing the report. As a result, I know plenty of classmates who can barely program but are quite good at documentation.</p>\n<ol start=\"2\">\n<li>Problems with how courses are designed:</li>\n</ol>\n<p>The quality and difficulty of the assignments I got in these foundational courses were clearly different from <a href=\"https://fa25.datastructur.es/\">Berkeley's assignments</a>. What we eventually completed was roughly at the level of Berkeley's homework before the midterm, and it is hard to go deep enough through a single project for students to learn to break a problem down clearly and properly.</p>\n<p>And I have a very clear and strong feeling: most of the instructors who taught our courses were constrained by the required class hours, and they did not really understand the language themselves. The C++ instructor's use of pointers, for instance, stopped at the basic usage, and the Python instructor's knowledge of Python's standard libraries was limited to what was in the course slides—not to mention the farce of writing code in class that would not run and then explaining that it was pseudocode.</p>\n<p>These problems mean that my classmates' programming foundations were never deep or systematic to begin with, so afterward they even had to take an extra language course to shore them up.</p>\n</blockquote>\n<p><img src=\"https://img.markxu.icu/img20260527141237484.png\" alt=\"The last assignment in Berkeley's data structures course\"></p>\n<h2>Layer Three: Data Structures and Algorithms</h2>\n<p>If I could pick only one course that best represents the fundamentals of a computer science major, I would probably pick data structures and algorithms.</p>\n<p>Data structures study how to store data; algorithms study how to solve problems.</p>\n<ul>\n<li>Arrays, linked lists, stacks, queues, trees, graphs, hash tables, and heaps are all common data structures.</li>\n<li>Sorting, searching, recursion, dynamic programming, greedy algorithms, and graph algorithms are common algorithmic ideas.</li>\n</ul>\n<p>This course was the most painful for me at first, because unlike building a web page, you do not see results right away, and unlike making a small piece of software, there is no clear interface giving you immediate positive feedback. It is like practicing internal martial arts: a lot of it looks easy to understand and easy to grasp, but most of the time you are failing over and over, looking for a way out.</p>\n<p>Data structures and algorithms together form the foundation of every program. It is also common for two excellent products to occupy the same niche with the same purpose but different data structures.</p>\n<p>Take Notion and Obsidian, both used for “taking notes.” Their interfaces both support pages, nesting, and links, but the data management underneath is completely different. Notion breaks documents into Blocks and uses a <strong>tree structure</strong> to manage hierarchy and nesting; every operation is essentially moving and collapsing subtrees. Obsidian treats each note as <strong>a node in a graph</strong>, creating edges through bidirectional links and maintaining associations and backlinks with an adjacency list.</p>\n<p>Put simply, <a href=\"https://www.notion.com/help/what-is-a-block\">Notion is like organizing a multi-level set of folders</a>, while <a href=\"https://obsidian.md/zh/help/plugins/graph\">Obsidian is more like weaving a spider web</a>. The former cares about hierarchy; the latter cares about connection points. Precisely because of this, even though both are “organizing notes,” Notion is asking “who belongs to whom” while Obsidian is asking “who is related to whom”—<strong>data structures do not change what you can do, but they determine how you think about it.</strong></p>\n<div class=\"img-grid-2\">\n  <img src=\"https://img.markxu.icu/img20260527144633900.png\" alt=\"Notion's Blocks\" />\n  <img src=\"https://img.markxu.icu/img20260527144337463.png\" alt=\"Obsidian's graph view\" />\n</div>\n<p>So data structures and algorithms do not exist for the sake of grinding LeetCode. Grinding problems is only one form of training; what it really trains is: <strong>when facing a problem, how to choose the right abstraction and solution strategy.</strong></p>\n<h2>Layer Four: The Computer as a Whole System</h2>\n<p>The biggest difference between a computer science major and pure software training is that it asks you to understand “what happens underneath the code.”</p>\n<p>This part usually includes courses such as computer organization, operating systems, computer networks, database systems, and compilers.</p>\n<p><strong>Computer organization</strong> tells you how the CPU, memory, instructions, registers, and caches work together—that is, how everything on your screen is transformed by binary zeros and ones across electronic components and finally becomes software or a game.</p>\n<p><strong>Operating systems</strong> covers processes, threads, memory management, file systems, scheduling, and deadlocks. You will understand why a computer can run many programs at once, why programs freeze, why running out of memory slows things down, and why poorly written multithreaded code produces strange bugs.</p>\n<p><strong>Computer networks</strong> covers TCP/IP, HTTP, DNS, routing, and congestion control. Every day when you open a web page, send a message, or watch a video, you are using these protocols. After the course, at least you will know that “the network is bad” is not a universal explanation—behind it may be latency, packet loss, bandwidth, DNS, the server, the client, and a pile of other links all contributing.</p>\n<p><strong>Databases</strong> covers the relational model, SQL, indexes, transactions, concurrency control, and data consistency. It solves the problem of how to store data long-term, reliably, and in an organized way, while keeping things from falling apart when many people access it at once.</p>\n<p><strong>Compilers</strong> covers how a language goes from source code to a form the machine can execute. Lexical analysis, syntax analysis, semantic analysis, optimization, code generation—it sounds hardcore, but it will help you understand that programming languages did not appear out of nowhere; they are systems that can be designed and implemented. <em>(This course really tests your logic, and it is not easy.)</em></p>\n<p>Together these courses form the systemic foundation of the major. You may not go on to work directly on operating systems or compilers, but understanding this material gives you a stronger sense of “what is underneath” when you face complex problems. If you study deeply enough, you can even work out the underlying direction when you hit a strange bug—for example, when I was reading <a href=\"https://mastodon.gamedev.place/@TomF/115589875974658415\">this post</a>, by the later part of the problem description I guessed that it might be related to floating-point arithmetic caused by historical reasons.</p>\n<p><img src=\"https://img.markxu.icu/img20260527150240665.png\" alt=\"The moment I guessed it\"></p>\n<h2>The Final Layer: Specialized Direction Courses</h2>\n<p>By junior and senior year, many schools offer direction courses or electives. They vary noticeably between schools, but the common directions are roughly the following. You can also use <a href=\"https://csdiy.wiki/\">csdiy.wiki</a> to find the courses you want <em>(if you are very capable, you can consult the author's reviews; for an ordinary student like me, everything took considerably longer than the author reported, felt somewhat harder, and I definitely did not remember it all after finishing)</em>.</p>\n<p><strong>Artificial intelligence and machine learning.</strong><br>\nThis includes machine learning, deep learning, natural language processing, computer vision, data mining, and more. It is very popular right now, but actually studying it is not just “prompting a large model.” You need to understand basic concepts like data, models, training, evaluation, overfitting, loss functions, and optimization methods, and you also need to learn frameworks such as PyTorch. Getting started really is exciting—watching the loss go down during training genuinely gives you a kind of simple, slightly silly happiness.</p>\n<p><strong>Networks and information security.</strong><br>\nThis includes cryptography, network security, system security, Web security, reverse engineering, and more. I know almost nothing about this area; I only once had the chance to interview cybersecurity staff in the police and at a company, and picked up a little knowledge. I know market demand in this area is still growing, but to really go deeper, that becomes the content of the cybersecurity major itself.</p>\n<p><strong>Embedded systems and the Internet of Things.</strong><br>\nThis leans toward the combination of hardware and systems, and may involve microcontrollers, sensors, real-time systems, embedded Linux, and so on. It cares more about how a program runs stably on a device with limited resources.</p>\n<p><strong>Software development and engineering practice.</strong><br>\nThis includes Web development, mobile development, cloud computing, microservices, software testing, DevOps, and more. This direction is closely tied to actual employment and emphasizes engineering ability and system development experience.</p>\n<p>In freshman year, our dean (now a vice president) taught us Introduction to Computers, and in class he recommended the book The Mythical Man-Month. When I saw that it was from the 1970s, I thought the dean really was an “old-timer” <em>(no disrespect intended)</em>. It was not until I organized a student innovation project and actually built something that I remembered the teacher's recommendation, and realized that the pitfalls I was hitting had already been given better solutions by others fifty years earlier. If you want to understand the development process, The Mythical Man-Month is a storybook with no barrier to entry that runs through the whole process.</p>\n<p><strong>Graphics, games, and virtual reality.</strong><br>\nThis involves geometry, rendering, physical simulation, engines, interaction, and more. It looks cool, but behind it is a great deal of mathematics, algorithms, and engineering detail. I once studied GAMES101, but eventually I really had no time and the course was a bit hard, so I gave up. Even so, that brief exposure made me pay close attention to a game engine's rendering logic when I play games, and I often watch Unreal Engine presentations to understand this dazzling technology. In fact, graphics is not only knowledge for game engines; there are many directions behind it, which I will introduce later.</p>\n<p><strong>Big data and databases.</strong><br>\nThis includes distributed systems, data warehousing, data analysis, recommendation systems, and more. The problems it handles usually involve large data scale, high concurrency, and complex systems. I have not gone deep into big data, so I am not in a position to offer an expert assessment.</p>\n<p>These directions are not isolated from one another. Artificial intelligence, for instance, needs algorithms and mathematics, but also systems and engineering; cybersecurity needs operating systems and networking fundamentals; back-end development cannot do without databases, networks, and software engineering.</p>\n<p>That is the nature of a computer science major: the further you go, the more you find that many courses connect to one another. As I see it, no one can learn all of the directions above thoroughly, or even take them all once. Even specializing in a single direction takes years. My exploration over four years of university was not small, but deeper study afterward is more or less the direction and purpose of graduate school.</p>\n<p>For specifics, see this Zhihu answer:</p>\n<p>Should computer science undergraduates spend a lot of time writing compilers and operating systems, or is that a distraction from their proper work? - Freezy's answer - Zhihu<br>\n<a href=\"https://www.zhihu.com/question/321433640/answer/2066636784763720375\">https://www.zhihu.com/question/321433640/answer/2066636784763720375</a></p>\n<h2>What Does Studying Computer Science Require?</h2>\n<p>I think the most important thing is not being a “math genius,” nor “having written code since childhood.”</p>\n<p>What matters more is a few rather plain but rare abilities.</p>\n<p>First, a willingness to look things up yourself. The field changes fast, and many problems will no longer have a teacher walking you through them step by step—many classmates will not have the time to teach you hand in hand either. Error messages, official documentation, search engines, open-source projects, papers, and community discussions all become part of your learning. Even in today's AI era, this ability matters especially.</p>\n<p>Second, being able to tolerate frustration. Program errors are routine, and failing to get an environment set up is perfectly normal. I once spent three days getting an environment configured before it finally ran, and spent a week on one bug, staying up late every day. Maybe that is just me being bad, but judging by experiences shared on forums, it does not seem rare.</p>\n<p>Third, enjoying breaking problems apart. Computer science rarely solves one huge problem directly; instead it keeps making the problem smaller, turning vague requirements into concrete steps and complex systems into modules. Once this habit of thinking takes hold, even when you are browsing a web page you will unconsciously try to abstract the data structures inside it. <em>(It is actually quite similar to force analysis in high school physics.)</em></p>\n<p>Fourth, a bit of patience for continuous learning. You may have just learned a framework when a new tool appears; you may have just understood one direction when the industry's hot topic shifts again. A computer science major is not well suited to living off what you learned in four years of university for the rest of your life. Especially now that the AI era has arrived, that old knowledge may be reset to zero outright, and keeping up learning may be the only way for ordinary people like us to keep pace with it.</p>\n<h2>Final Thoughts</h2>\n<p>A computer science and technology major covers a lot, and it is scattered.</p>\n<p>It has mathematics and engineering; theory and practice; it requires understanding the underlying systems and being able to build applications on top. When you first start, it is easy to feel that these courses have nothing to do with one another: math is math, C is C, operating systems are operating systems, databases are databases. But as you go further, you slowly discover that they are all answering the same question:</p>\n<p><strong>How can information be better represented, stored, transmitted, processed, and used?</strong></p>\n<p>This article is a very rough introduction, mainly as a record of what I have taken away from four years of continuous study, along with the problems I noticed and my most sincere advice. The learning experience of an ordinary student at a university outside the 985 and 211 tiers, who is full of uncertainty about the future, cannot cover everything, but at least I hope it helps parents and students interested in computers form a basic impression: computer science is not a pile of math, nor simply typing code. It is understanding every layer underneath the computer.</p>\n<p>If you do choose to study it, you will most likely run into plenty of bugs that give you headaches, courses you cannot follow, environments that will not configure, and projects you cannot finish.</p>\n<p>But there will also be some very happy moments: the first time a program runs, the first time a web page goes live, the first time a model produces a result, the first time a small tool you wrote actually helps someone. Those moments make you feel that you really are turning the ideas in your head into reality, little by little.</p>\n<p>These tiny bits of happiness and positive feedback are probably what attracts me most to computer science.</p>\n",
      "summary": "[AI-translated] For anyone who wants to know what a computer science and technology major actually studies: it is not just writing code, and not just fixing computers, but a whole course of training built around computation, systems, software, and engineering practice.",
      "date_published": "2026-05-27T00:00:00.000Z",
      "date_modified": "2026-08-05T00:00:00.000Z",
      "image": "https://img.markxu.icu/img20260527160205221.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/why-I-recommend-everyone-to-write-blog-en",
      "url": "https://markxu.icu/blog/why-I-recommend-everyone-to-write-blog-en",
      "title": "Why Do I Recommend College Students to Write Blogs?",
      "content_text": "After the gaokao, I came to a city 1,800 kilometers away from home for university. I walked out of the stage of life where exam scores were almost the only standard, and I encountered many things that made me start thinking. I also faced many choices that I had to make by myself. Sometimes one small thing would stay in my mind until midnight, repeating itself before sleep, leaving me with a stomach full of things to say. The next day, when I tried writing it down, I realized it was often just one or two short paragraphs of scattered thoughts, not much content at all. Writing helped me ease a lot of anxiety, and it also left clues for me to look back on later. Now when I turn back and read those records, I can clearly see how my worldview was pieced together from fragments of daily life. Writing a blog is a very good way to record these bits and pieces of life, making the origins of your thoughts traceable. As a College Student, What Can You Write About? The main audience of a blog is yourself. Many people do not write, and some are even afraid to write, because they think, \"I'm not an expert, so nobody will read what I write.\" But the opposite is exactly why blogging is valuable. The most precious stage is precisely when you have not fully thought something through. Writing it down is itself a process of organizing your knowledge and the information you have. When writing, in order to express your thoughts, you gather related materials, check relevant content, and clarify the chain of logic. The goal is to squeeze that vague cloud floating in your mind into a visible shape. Actually, blogging does not have that many rules. You can record your hobbies, record the things that went wrong, and record the thoughts that make you feel \"different\" your \"difference\" is actually a clue and trace that helps people like you find you . You can also share your views on trending internet events. For example, regarding the recent popular \"Jiahao\" behavior, I feel it is a stage of life that may be embarrassing to look back on, but is still full of chaos and heat. Like the changes in adolescent thinking described in FLCL . There is no need to pick up a magnifying glass after growing up and start judging everything, suppressing the expression and emotions of teenagers who are still in adolescence. You can share knowledge you have learned: bugs and solutions from small projects you tinkered with, difficulties and challenges you encountered, how you completed an experiment, reflections after finishing a book, or experiences battling Linux. All of these can become small articles, like save points in life that you can come back to from time to time. How Do You Publish It After Writing? A blog is meant to be seen. Publishing it is like turning the sign at the door of a small shop to \"OPEN,\" opening a door on the internet. Andy Matuschak calls this \"working with the garage door up.\" Passersby glance inside, see you knocking things together, and naturally know someone is working here. If you have a bit of spare money, you can try buying your own domain name and use AI plus open-source projects to build a small website that fits your own taste. There are also many ready-made blog templates online. You can learn to adapt one, or directly deploy a site that belongs to you. If you do not want to deal with all that for now, you can simply write in any note-taking app and publish selected pieces to platforms with broad audiences, such as Zhihu, Bilibili, or WeChat Official Accounts. Good content will eventually find people willing to read it. As for things you do not publish publicly, you can keep them as your own archive, as clues to your thoughts. Looking back later is not a bad thing either. What Is a Good Blog Like? When it comes to what kind of blog counts as \"good,\" I do not think you need to make it too complicated at the beginning. But there are a few points I want to talk about. The Audience of a Blog A blog is a public article once it is published, so you first need to consider the audience: who is reading it? Although I said earlier that the biggest audience of a blog is yourself, what you write should still have a certain public quality. It can be a place to clarify your own thoughts, but while you benefit from thinking and organizing, others should also be able to see something in it. The more professional and niche the topic is, the more you need to think about this. Of course, you can also use your blog like a \"QQ Zone.\" Many bloggers dislike this concept and find it childish, but if your purpose is socializing and sharing, I do not think there is anything wrong with it. Through many similar personal blogs, I have seen the worries and joys of many peers like me. It feels like meeting a stranger who somehow becomes a friend. I think that is pretty nice. The Labeling of a Blog A blog should have its own theme. If a good blog wants to be remembered, it is best if people can easily attach a label to it. For example, when people want news analysis, philosophy reading notes, historical explanations, economic changes, or technical blogs, they can immediately think of whose website to visit. But this requirement is a bit too high for a college student. A normal college student, after coming out of the gaokao experience, usually does not have much life experience, deep reading, or even a writing habit beyond the style of exam essays. That is completely normal. When you have fewer topics to choose from, you can write as much as you can and record many aspects of your life. At this stage, you can use category management so that readers who need specific content can filter things simply. They can block out your daily fragments and focus only on your main output. This may be a passable solution. After you accumulate enough experience, it is not too late to open a dedicated column. I partly refer to the ideas of the 2750words blogger. I agree with most of his philosophy, though perhaps our life stages are different, or our entry points into blogging are different. I think a blog should still be centered on the blogger. Since it is already a personal blog, it is unreasonable to let someone else's design philosophy decide everything. If you do not like a website, you can simply choose not to read it. There will always be people who do like it. Just keep your own strong authorial presence. After all, the owner of the blog is you. Comfort will always matter more than being \"correct.\" The Word Count of a Blog There should be some limit to word count. Many people do not like reading overly long articles, or rather, overly verbose articles. If a simple matter is hard to explain in fewer words, it probably means the writing is not clear enough yet. Verbose but unclear expression is a big problem. My advisor once gave me a requirement: \"Your writing should have key points, so readers can roughly understand the intention within 30 seconds. It should have a two-minute reading path and a five-minute reading path.\" Proper bold text and well-designed section headings are fairly good ways to solve this problem. A Rational Discussion: Benefits and Costs I have said many things above, and it may sound like writing a blog is a \"guaranteed profitable\" deal. But if I do not tell you the corresponding costs and price, that would feel a little irresponsible. Writing a blog is not a social shortcut. It is more like opening a quiet little shop that few people visit. A good blog is a good way to speak on the internet. What you write represents the things you care about and the way you think about problems. But this does not automatically bring traffic, make your personal brand explode, make important people suddenly discover you, or bring opportunities on its own. Most of the time, blogging is slow cultivation by one person. From a personal perspective, maintaining and persisting with a blog is not something everyone can do. I have seen too many blogs disappear after the first three or five posts, sometimes even with the domain name no longer renewed. To be honest, I am also still in that \"three-minute enthusiasm\" stage, and future updates probably will not be as frequent as they are now. Also, \"being misunderstood is the fate of anyone who expresses themselves.\" If you are sensitive, you will pay some emotional cost. Maintaining a website also requires continuous financial input. How it develops later is indeed hard to say. Blogging, as an activity that has already declined, does not bring many direct returns. We are now in the era of new self-media. Making videos and short videos is a more popular and mainstream choice. With platform support, there is more traffic and more visibility. Considering so many disadvantages, why do I still recommend it? First, as discussed earlier, writing articles is, so far, the fastest form of output, the one that most forces you to think, and the one that exposes logic most directly . What is truly valuable is the thought behind the words. Writing can greatly train a person's ability to think. Second, as a flexible form of output with low time cost, low storage cost, and a low entry threshold, writing has the highest cost-performance ratio among output-oriented hobbies. Pure text publishing is supported by many platforms, and a good article can be lightly modified into a video script, serving as the foundation for other forms of output. Create Boldly The confusion, awkwardness, sudden happiness, and unexplained sadness we are experiencing now are all gifts that belong uniquely to this stage of life. When we look back later, perhaps we will be grateful that we once made the decision to lay out our thoughts and record them. After all, in this empty space of the internet, only when you keep a light on will someone follow that light and come over to say: \"Hey, I am here too.\" I am not sure how far this light can shine, nor how long it can stay lit. I simply believe that some things do not need to prove they are worth starting before they become worth starting.",
      "content_html": "<p>After the gaokao, I came to a city 1,800 kilometers away from home for university. I walked out of the stage of life where exam scores were almost the only standard, and I encountered many things that made me start thinking. I also faced many choices that I had to make by myself. Sometimes one small thing would stay in my mind until midnight, repeating itself before sleep, leaving me with a stomach full of things to say. The next day, when I tried writing it down, I realized it was often just one or two short paragraphs of scattered thoughts, not much content at all. Writing helped me ease a lot of anxiety, and it also left clues for me to look back on later.</p>\n<p>Now when I turn back and read those records, I can clearly see how my worldview was pieced together from fragments of daily life. <strong>Writing a blog is a very good way to record these bits and pieces of life, making the origins of your thoughts traceable.</strong></p>\n<h2>As a College Student, What Can You Write About?</h2>\n<p><strong>The main audience of a blog is yourself.</strong> Many people do not write, and some are even afraid to write, because they think, &quot;I'm not an expert, so nobody will read what I write.&quot; But the opposite is exactly why blogging is valuable. The most precious stage is precisely when you have not fully thought something through. Writing it down is itself a process of organizing your knowledge and the information you have. When writing, in order to express your thoughts, you gather related materials, check relevant content, and clarify the chain of logic. The goal is to squeeze that vague cloud floating in your mind into a visible shape.</p>\n<p>Actually, blogging does not have that many rules.</p>\n<p>You can record your hobbies, record the things that went wrong, and record the thoughts that make you feel &quot;different&quot; <em>(your &quot;difference&quot; is actually a clue and trace that helps people like you find you)</em>.</p>\n<p>You can also share your views on trending internet events. For example, regarding the recent popular &quot;Jiahao&quot; behavior, I feel it is a stage of life that may be embarrassing to look back on, but is still full of chaos and heat. Like the changes in adolescent thinking described in <em>FLCL</em>. There is no need to pick up a magnifying glass after growing up and start judging everything, suppressing the expression and emotions of teenagers who are still in adolescence.</p>\n<p><img src=\"https://img.markxu.icu/imgflcl.gif\" alt=\"FLCL\"></p>\n<p>You can share knowledge you have learned: bugs and solutions from small projects you tinkered with, difficulties and challenges you encountered, how you completed an experiment, reflections after finishing a book, or experiences battling Linux. All of these can become small articles, like save points in life that you can come back to from time to time.</p>\n<h2>How Do You Publish It After Writing?</h2>\n<p>A blog is meant to be seen. Publishing it is like turning the sign at the door of a small shop to &quot;OPEN,&quot; opening a door on the internet. <a href=\"https://notes.andymatuschak.org/Work_with_the_garage_door_up\">Andy Matuschak</a> calls this &quot;working with the garage door up.&quot; Passersby glance inside, see you knocking things together, and naturally know someone is working here.</p>\n<p><img\n  src=\"https://img.markxu.icu/img20260520161119114.png\"\n  alt=\"Image from Taobao\"\n  style=\"width: min(100%, 360px); height: auto; display: block; margin: 1.5rem auto;\"\n/></p>\n<p>If you have a bit of spare money, you can try buying your own domain name and use AI plus open-source projects to build a small website that fits your own taste. There are also many ready-made blog templates online. You can learn to adapt one, or directly deploy a site that belongs to you. If you do not want to deal with all that for now, you can simply write in any note-taking app and publish selected pieces to platforms with broad audiences, such as Zhihu, Bilibili, or WeChat Official Accounts. Good content will eventually find people willing to read it.</p>\n<p>As for things you do not publish publicly, you can keep them as your own archive, as clues to your thoughts. Looking back later is not a bad thing either.</p>\n<h2>What Is a Good Blog Like?</h2>\n<p>When it comes to what kind of blog counts as &quot;good,&quot; I do not think you need to make it too complicated at the beginning. But there are a few points I want to talk about.</p>\n<h3>The Audience of a Blog</h3>\n<p>A blog is a public article once it is published, so you first need to consider the audience: who is reading it?</p>\n<p>Although I said earlier that the biggest audience of a blog is yourself, what you write should still have a certain public quality. It can be a place to clarify your own thoughts, but while you benefit from thinking and organizing, others should also be able to see something in it. The more professional and niche the topic is, the more you need to think about this.</p>\n<p>Of course, you can also use your blog like a &quot;QQ Zone.&quot; Many bloggers dislike this concept and find it childish, but if your purpose is socializing and sharing, I do not think there is anything wrong with it. Through many similar personal blogs, I have seen the worries and joys of many peers like me. It feels like meeting a stranger who somehow becomes a friend. I think that is pretty nice.</p>\n<h3>The Labeling of a Blog</h3>\n<p>A blog should have its own theme. <strong>If a good blog wants to be remembered, it is best if people can easily attach a label to it.</strong> For example, when people want news analysis, philosophy reading notes, historical explanations, economic changes, or technical blogs, they can immediately think of whose website to visit.</p>\n<p>But this requirement is a bit too high for a college student. A normal college student, after coming out of the gaokao experience, usually does not have much life experience, deep reading, or even a writing habit beyond the style of exam essays. That is completely normal. When you have fewer topics to choose from, you can write as much as you can and record many aspects of your life. At this stage, you can use category management so that readers who need specific content can filter things simply. They can block out your daily fragments and focus only on your main output. This may be a passable solution. After you accumulate enough experience, it is not too late to open a dedicated column.</p>\n<p>I partly refer to the ideas of the <a href=\"https://pathos.page/about/\">2750words blogger</a>. I agree with most of his philosophy, though perhaps our life stages are different, or our entry points into blogging are different. I think a blog should still be centered on the blogger. Since it is already a personal blog, it is unreasonable to let someone else's design philosophy decide everything. If you do not like a website, you can simply choose not to read it. There will always be people who do like it. Just keep your own strong authorial presence. After all, <strong>the owner of the blog is you. Comfort will always matter more than being &quot;correct.&quot;</strong></p>\n<h3>The Word Count of a Blog</h3>\n<p>There should be some limit to word count. Many people do not like reading overly long articles, or rather, overly verbose articles. If a simple matter is hard to explain in fewer words, it probably means the writing is not clear enough yet. Verbose but unclear expression is a big problem. My advisor once gave me a requirement: <strong>&quot;Your writing should have key points, so readers can roughly understand the intention within 30 seconds. It should have a two-minute reading path and a five-minute reading path.&quot;</strong> Proper bold text and well-designed section headings are fairly good ways to solve this problem.</p>\n<h2>A Rational Discussion: Benefits and Costs</h2>\n<p>I have said many things above, and it may sound like writing a blog is a &quot;guaranteed profitable&quot; deal. But if I do not tell you the corresponding costs and price, that would feel a little irresponsible.</p>\n<p>Writing a blog is not a social shortcut. It is more like opening a quiet little shop that few people visit. A good blog is a good way to speak on the internet. What you write represents the things you care about and the way you think about problems. But this does not automatically bring traffic, make your personal brand explode, make important people suddenly discover you, or bring opportunities on its own. Most of the time, blogging is slow cultivation by one person.</p>\n<p>From a personal perspective, maintaining and persisting with a blog is not something everyone can do. I have seen too many blogs disappear after the first three or five posts, sometimes even with the domain name no longer renewed. To be honest, I am also still in that &quot;three-minute enthusiasm&quot; stage, and future updates probably will not be as frequent as they are now. Also, &quot;being misunderstood is the fate of anyone who expresses themselves.&quot; If you are sensitive, you will pay some emotional cost. Maintaining a website also requires continuous financial input. How it develops later is indeed hard to say.</p>\n<p>Blogging, as an activity that has already declined, does not bring many direct returns. We are now in the era of new self-media. Making videos and short videos is a more popular and mainstream choice. With platform support, there is more traffic and more visibility.</p>\n<p>Considering so many disadvantages, why do I still recommend it?</p>\n<p>First, as discussed earlier, writing articles is, so far, <strong>the fastest form of output, the one that most forces you to think, and the one that exposes logic most directly</strong>. What is truly valuable is the thought behind the words. Writing can greatly train a person's ability to think. Second, as a flexible form of output with low time cost, low storage cost, and a low entry threshold, writing has the highest cost-performance ratio among output-oriented hobbies. Pure text publishing is supported by many platforms, and a good article can be lightly modified into a video script, serving as the foundation for other forms of output.</p>\n<h2>Create Boldly</h2>\n<p>The confusion, awkwardness, sudden happiness, and unexplained sadness we are experiencing now are all gifts that belong uniquely to this stage of life. When we look back later, perhaps we will be grateful that we once made the decision to lay out our thoughts and record them. After all, in this empty space of the internet, only when you keep a light on will someone follow that light and come over to say: &quot;Hey, I am here too.&quot;</p>\n<p>I am not sure how far this light can shine, nor how long it can stay lit. <strong>I simply believe that some things do not need to prove they are worth starting before they become worth starting.</strong></p>\n",
      "summary": "Although the idea of writing blogs already feels a little outdated, I still recommend it. It helps you clarify your thoughts, record the process of growing up, and maybe even meet like-minded people along the way.",
      "date_published": "2026-05-20T00:00:00.000Z",
      "date_modified": "2026-05-22T00:00:00.000Z",
      "image": "https://img.markxu.icu/imgflcl.gif",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/websurf-tools-recommendation-en",
      "url": "https://markxu.icu/blog/websurf-tools-recommendation-en",
      "title": "Handy Little Tools for Surfing the Web",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. Let me start a post I can update whenever I like. There are some tools I really do want to recommend, but doing it one at a time feels too fragmented, so I will collect them here. If I come across new ones, I will keep this updated too! To make things easier for everyone, I will paste the original links as well. Immersive Translate In the internet age, multilingual education has gradually spread into compulsory schooling everywhere, and getting information across languages is easier than ever before. But a person's energy is always limited. Most of us can read content in two languages, and beyond that things start to get headache-inducing. Meanwhile, Chinese does not make up much of the information on the internet — according to Wikipedia's statistics, it ranks only tenth. The numbers are not exact, but they do say something. A lot of computer-related knowledge can only be found by searching in English. And information about the specific culture of a particular country is even more of a challenge. Rank Language Percentage ---- ---------- ---------- 1 English 59.3% 2 Russian 8.4% 3 Spanish 4.2% 4 German 2.9% 5 Turkish 2.9% 6 Persian 2.8% 7 French 2.8% 8 Japanese 2.4% 9 Portuguese 2.2% 10 Chinese 1.3% 11 Vietnamese 1.3% 12 Italian 1.0% At that point, having an effective translation tool — one that also lets you glance at the original for comparison — works like Zhuge Liang's feather fan and silk headdress: the channels through which you find information suddenly widen. Tampermonkey a.k.a. “Oil Monkey” This extension needs no introduction. It is basically a cheat code for your browser: by running scripts inside the browser, it unlocks new features or grants you entirely new ones! But there is a strange pattern — everyone knows this extension is powerful, yet few people actually use it. The reason is that they do not know what they need, or where they would need it, and when they go browse the script marketplace, everything they see is a feature they are unlikely to use. So here are a few that have worked well for me; you can give them a try. UserScript+--: The Script That Finds Scripts As the description says, it is a script for finding scripts. Run it on a site you visit often, and you can discover how many people have already added which features for that site. It is also extremely simple to use: just click the button on the extension and it jumps straight to a list showing the relevant scripts. The script is lightweight and efficient. I will paste the source code directly — it is already enough for me: Weibo, Zhihu, Bilibili, and Xiaohongshu Keyword Blocker Sometimes when you are browsing online, plenty of posts with particular keywords get pushed at you, and some of them are just there to stir up anxiety. With this extension you can cut down on some of that pointless anxiety, and you can add your own custom keywords too. Pretty good. Scripts That Lift Copy Restrictions There are actually plenty of scripts for this on Greasy Fork, and even on GitHub, or really anywhere on the internet, so I will not paste a specific link. It is genuinely useful — you can search for one yourself. Auto-Skip the Sponsored Bits on Bilibili There is a bit of a moral constraint around using this one — it might count as watching pirated content... But if you really hate spending your video-scrolling time on ads for used-phone recycling, massage devices, robot vacuums, and mite removers, maybe it is fine to use it in moderation? Anyway, I am not encouraging it; I am just leaving it here in case someone needs it. Wappalyzer: Shows the Tech Stack Behind a Web Page This one is quite fun. You can see what tech stack the page you are currently browsing uses and which JS libraries it runs. If you are just getting into front-end development, you can check what the sites you like are built with. Or when you see a cool animation effect, you can see exactly which animation library it uses. That said, I should be clear: Wappalyzer's results are best treated as technical clues. They are not the same as the site's real tech stack. For example, I really do use React, React Router, GSAP, Lenis, and Framer Motion, but WordPress and PHP are actually false positives. The main site is in fact a semi-static site built on Vite + React + TypeScript + Tailwind CSS, and it does not use the WordPress CMS or a PHP backend. As for why it misreads things, it probably has a lot to do with the fact that my link preview feature uses WordPress.com's mShots screenshot service. A friend once told me that most people are afraid to click a link they do not recognize, so I added that feature — it makes the interaction more interesting too. As for MySQL, my comment section uses Twikoo, and Twikoo uses MySQL to store comments. That is probably the reason. That is all I have written down for now. If you have tools to recommend as well, feel free to share them in the comments!",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>Let me start a post I can update whenever I like. There are some tools I really do want to recommend, but doing it one at a time feels too fragmented, so I will collect them here. If I come across new ones, I will keep this updated too! To make things easier for everyone, I will paste the original links as well.</p>\n<hr>\n<h2>Immersive Translate</h2>\n<p><a href=\"https://chromewebstore.google.com/detail/immersive-translate-trans/bpoadfkcbjbfhfodiogcnhhhpibjhbnh\">https://chromewebstore.google.com/detail/immersive-translate-trans/bpoadfkcbjbfhfodiogcnhhhpibjhbnh</a></p>\n<p>In the internet age, multilingual education has gradually spread into compulsory schooling everywhere, and getting information across languages is easier than ever before. But a person's energy is always limited. Most of us can read content in two languages, and beyond that things start to get headache-inducing. Meanwhile, Chinese does not make up much of the information on the internet — according to <a href=\"https://zh.wikipedia.org/wiki/%E4%BA%92%E8%81%94%E7%BD%91%E4%BD%BF%E7%94%A8%E8%AF%AD%E8%A8%80\">Wikipedia's statistics</a>, it ranks only tenth. The numbers are not exact, but they do say something. A lot of computer-related knowledge can only be found by searching in English. And information about the specific culture of a particular country is even more of a challenge.</p>\n<table>\n<thead>\n<tr>\n<th>Rank</th>\n<th>Language</th>\n<th>Percentage</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1</td>\n<td>English</td>\n<td>59.3%</td>\n</tr>\n<tr>\n<td>2</td>\n<td>Russian</td>\n<td>8.4%</td>\n</tr>\n<tr>\n<td>3</td>\n<td>Spanish</td>\n<td>4.2%</td>\n</tr>\n<tr>\n<td>4</td>\n<td>German</td>\n<td>2.9%</td>\n</tr>\n<tr>\n<td>5</td>\n<td>Turkish</td>\n<td>2.9%</td>\n</tr>\n<tr>\n<td>6</td>\n<td>Persian</td>\n<td>2.8%</td>\n</tr>\n<tr>\n<td>7</td>\n<td>French</td>\n<td>2.8%</td>\n</tr>\n<tr>\n<td>8</td>\n<td>Japanese</td>\n<td>2.4%</td>\n</tr>\n<tr>\n<td>9</td>\n<td>Portuguese</td>\n<td>2.2%</td>\n</tr>\n<tr>\n<td>10</td>\n<td>Chinese</td>\n<td>1.3%</td>\n</tr>\n<tr>\n<td>11</td>\n<td>Vietnamese</td>\n<td>1.3%</td>\n</tr>\n<tr>\n<td>12</td>\n<td>Italian</td>\n<td>1.0%</td>\n</tr>\n</tbody>\n</table>\n<p>At that point, having an effective translation tool — one that also lets you glance at the original for comparison — works like Zhuge Liang's feather fan and silk headdress: the channels through which you find information suddenly widen.</p>\n<p><img src=\"https://img.markxu.icu/img20260517181359803.png\" alt=\"Osaka food recommendation website\"></p>\n<h2>Tampermonkey (a.k.a. “Oil Monkey”)</h2>\n<p><a href=\"https://chromewebstore.google.com/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo\">https://chromewebstore.google.com/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo</a></p>\n<p>This extension needs no introduction. It is basically a cheat code for your browser: by running scripts inside the browser, it unlocks new features or grants you entirely new ones! But there is a strange pattern — everyone knows this extension is powerful, yet few people actually use it. The reason is that they do not know what they need, or where they would need it, and when they go browse the script marketplace, everything they see is a feature they are unlikely to use. So here are a few that have worked well for me; you can give them a try.</p>\n<h3>UserScript+--: The Script That Finds Scripts</h3>\n<p><a href=\"https://greasyfork.org/zh-CN/scripts/409727-userscript-show-site-all-userjs-num-%E6%98%BE%E7%A4%BA%E9%80%82%E7%94%A8%E7%9A%84userjs%E8%84%9A%E6%9C%AC%E6%95%B0%E5%8F%8A%E8%B7%B3%E8%BD%AC\">https://greasyfork.org/zh-CN/scripts/409727-userscript-show-site-all-userjs-num-%E6%98%BE%E7%A4%BA%E9%80%82%E7%94%A8%E7%9A%84userjs%E8%84%9A%E6%9C%AC%E6%95%B0%E5%8F%8A%E8%B7%B3%E8%BD%AC</a></p>\n<p>As the description says, it is a script for finding scripts. Run it on a site you visit often, and you can discover how many people have already added which features for that site. It is also extremely simple to use: just click the button on the extension and it jumps straight to a list showing the relevant scripts.</p>\n<p><img src=\"https://img.markxu.icu/img20260517192034039.png\" alt=\"Just click OpenuserJS\"></p>\n<p>The script is lightweight and efficient. I will paste the source code directly — it is already enough for me:</p>\n<pre><code class=\"language-javascript\">// ==UserScript==\n// @name         Userscript+-- : Show Site All UserJS num 显示适用的UserJS脚本数及跳转\n// @name:zh      UserScript+--: 显示适用的UserJS脚本数及跳转\n// @name:zh-CN   UserScript+--: 显示适用的UserJS脚本数及跳转\n// @name:zh-TW   UserScript+--: 显示適用的UserJS脚本数及跳转\n// @namespace    https://github.com/ultranity/Userscript-Plus--\n// @version      1.0.0\n// @description        在脚本菜单显示适用于当前网站的油猴脚本数及跳转 Show the num of all Tampermonkey scripts for the current site.\n// @description:zh      在脚本菜单显示适用于当前网站的油猴脚本数及跳转\n// @description:zh-CN   在脚本菜单显示适用于当前网站的油猴脚本数及跳转\n// @description:zh-TW   在脚本菜单顯示適用於當前網站的油猴腳本数及跳转\n// @author       momoos\n// @include        *\n// @resource     siteData  https://greasyfork.org/scripts/by-site.json\n// @run-at       document-idle\n// @noframes\n// @grant        GM_xmlHttpRequest\n// @grant        GM_getResourceText\n// @grant        GM_registerMenuCommand\n// @grant        GM_openInTab\n// @grant        unsafeWindow\n// @connect      greasyfork.org\n// @downloadURL https://update.greasyfork.org/scripts/409727/Userscript%2B--%20%3A%20Show%20Site%20All%20UserJS%20num%20%E6%98%BE%E7%A4%BA%E9%80%82%E7%94%A8%E7%9A%84UserJS%E8%84%9A%E6%9C%AC%E6%95%B0%E5%8F%8A%E8%B7%B3%E8%BD%AC.user.js\n// @updateURL https://update.greasyfork.org/scripts/409727/Userscript%2B--%20%3A%20Show%20Site%20All%20UserJS%20num%20%E6%98%BE%E7%A4%BA%E9%80%82%E7%94%A8%E7%9A%84UserJS%E8%84%9A%E6%9C%AC%E6%95%B0%E5%8F%8A%E8%B7%B3%E8%BD%AC.meta.js\n// ==/UserScript==\n\n/* #eslint-disable no-undef */\n(function() {\n  'use strict';\n\n  // ==============================参数配置\n  let thisDomain =  document.domain.split('.').slice(-2).join('.').toLowerCase();\n\n  // 检查域名对应的脚本数量\n  let getDomainCount = function(domain) {\n      let siteData = GM_getResourceText('siteData');\n      siteData = JSON.parse(siteData);\n      let count = 0;\n      if (siteData.hasOwnProperty(domain)) {\n          count = siteData[domain];\n      }\n      return count;\n  };\n\n  function goGreasyfork() {\n      let url = `https://greasyfork.org/zh-CN/scripts/by-site/${thisDomain}?filter_locale=0`;\n      GM_openInTab(url, {\n          active: true\n      });\n  }\n\n  function goOpenuserJS() {\n      let url = `https://openuserjs.org/?q=${thisDomain}`;\n      GM_openInTab(url, {\n          active: true\n      });\n  }\n  // END\n\n  // ############### 开始\n\n  // 检查当前域名是否有对应油猴脚本\n  let domainCount = getDomainCount(thisDomain);\n // if (domainCount === 0) return;\n\n  GM_registerMenuCommand('Greasyfork:'+domainCount, goGreasyfork); // 设置油猴插件的菜单\n  GM_registerMenuCommand('OpenuserJS', goOpenuserJS) // 设置油猴插件的菜单\n  // ########\n})();\n\n\n</code></pre>\n<h3>Weibo, Zhihu, Bilibili, and Xiaohongshu Keyword Blocker</h3>\n<p><a href=\"https://greasyfork.org/zh-CN/scripts/543102-%E5%BE%AE%E5%8D%9A%E7%9F%A5%E4%B9%8Eb%E7%AB%99%E5%B0%8F%E7%BA%A2%E4%B9%A6%E5%85%B3%E9%94%AE%E8%AF%8D%E5%B1%8F%E8%94%BD%E5%99%A8\">https://greasyfork.org/zh-CN/scripts/543102-%E5%BE%AE%E5%8D%9A%E7%9F%A5%E4%B9%8Eb%E7%AB%99%E5%B0%8F%E7%BA%A2%E4%B9%A6%E5%85%B3%E9%94%AE%E8%AF%8D%E5%B1%8F%E8%94%BD%E5%99%A8</a></p>\n<p>Sometimes when you are browsing online, plenty of posts with particular keywords get pushed at you, and some of them are just there to stir up anxiety. With this extension you can cut down on some of that pointless anxiety, and you can add your own custom keywords too. Pretty good.</p>\n<p><img src=\"https://img.markxu.icu/img20260517193440719.png\" alt=\"Extension example\"></p>\n<h3>Scripts That Lift Copy Restrictions</h3>\n<p>There are actually plenty of scripts for this on Greasy Fork, and even on GitHub, or really anywhere on the internet, so I will not paste a specific link. It is genuinely useful — you can search for one yourself.</p>\n<h2>Auto-Skip the Sponsored Bits on Bilibili</h2>\n<p><a href=\"https://chromewebstore.google.com/detail/%E5%B0%8F%E7%94%B5%E8%A7%86%E7%A9%BA%E9%99%8D%E5%8A%A9%E6%89%8B/eaoelafamejbnggahofapllmfhlhajdd\">https://chromewebstore.google.com/detail/%E5%B0%8F%E7%94%B5%E8%A7%86%E7%A9%BA%E9%99%8D%E5%8A%A9%E6%89%8B/eaoelafamejbnggahofapllmfhlhajdd</a></p>\n<p>There is a bit of a moral constraint around using this one — it might count as watching pirated content... But if you really hate spending your video-scrolling time on ads for used-phone recycling, massage devices, robot vacuums, and mite removers, maybe it is fine to use it in moderation? Anyway, I am not encouraging it; I am just leaving it here in case someone needs it.</p>\n<p><img src=\"https://img.markxu.icu/img20260517194010162.png\" alt=\"Timestamp Jump Assistant\"></p>\n<h2>Wappalyzer: Shows the Tech Stack Behind a Web Page</h2>\n<p><a href=\"https://chromewebstore.google.com/detail/wappalyzer-technology-pro/gppongmhjkpfnbhagpmjfkannfbllamg\">https://chromewebstore.google.com/detail/wappalyzer-technology-pro/gppongmhjkpfnbhagpmjfkannfbllamg</a></p>\n<p>This one is quite fun. You can see what tech stack the page you are currently browsing uses and which JS libraries it runs. If you are just getting into front-end development, you can check what the sites you like are built with. Or when you see a cool animation effect, you can see exactly which animation library it uses.</p>\n<div class=\"img-grid-2\">\n  <img src=\"https://img.markxu.icu/img20260517194335017.png\" alt=\"My little website\" />\n  <img src=\"https://img.markxu.icu/img20260517194523519.png\" alt=\"Bilibili\" />\n</div>\n<p>That said, I should be clear: Wappalyzer's results are best treated as technical clues. They are not the same as the site's real tech stack. For example, I really do use React, React Router, GSAP, Lenis, and Framer Motion, but WordPress and PHP are actually false positives. The main site is in fact a semi-static site built on Vite + React + TypeScript + Tailwind CSS, and it does not use the WordPress CMS or a PHP backend.</p>\n<p>As for why it misreads things, it probably has a lot to do with the fact that my link preview feature uses <a href=\"http://WordPress.com\">WordPress.com</a>'s mShots screenshot service. A friend once told me that most people are afraid to click a link they do not recognize, so I added that feature — it makes the interaction more interesting too. As for MySQL, my comment section uses Twikoo, and Twikoo uses MySQL to store comments. That is probably the reason.</p>\n<hr>\n<p>That is all I have written down for now. If you have tools to recommend as well, feel free to share them in the comments!</p>\n",
      "summary": "[AI-translated] A running record of the browser extensions I use and the other small web tools I recommend.",
      "date_published": "2026-05-17T00:00:00.000Z",
      "date_modified": "2026-05-17T00:00:00.000Z",
      "image": "https://img.markxu.icu/img20260517181359803.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/ai-through-meme-death-stranding-2-en",
      "url": "https://markxu.icu/blog/ai-through-meme-death-stranding-2-en",
      "title": "Looking at AI Through the Lens of Meme: Thoughts After Playing Death Stranding 2",
      "content_text": "Recently I finished the story of Death Stranding 2 . The abstractness of some of its performances shocked me. I could not understand why certain scenes were designed that way: why did people suddenly start singing here? Why did the villain perform the American national anthem at the end? And why was pizza kung fu so powerful? After doing some quick research, I suddenly realized something: this kind of game seems very hard to copy. Not only because of its gameplay, but even more because of its cultural expression. What Kind of Game Is Death Stranding? Unlike the game genres already on the market, it has its own new gameplay experience and worldview. It is not like the Assassin's Creed series, which has already formed a relatively mature AAA industrial template: a big open world, repeated outpost clearing, RPG-style equipment growth, question marks all over the map the question marks in The Witcher 3 were still pretty good , and static story cutscenes. It feels as if, as long as the budget, team, and production pipeline are big enough, you can make something that \"looks about right\" though I heard Ubisoft is remaking Black Flag, so I will still have to try it on hands . It is also not like Soulslikes, which have a very clear gameplay skeleton: bonfires, death penalties, stamina bars, boss design, fragmented narrative. Change the worldview and art theme, and players can quickly understand, \"Oh, this has some Souls flavor.\" Nor is it like a linear AAA game that gives you as many cinematic shocks and exquisite level designs as possible within a limited route. Death Stranding is different. Its gameplay is new, its game concept is something I have never seen before, and the pleasure it brings has a very distinctive taste. Others can imitate its art, its long takes, its barren, absurd, realistic, or slightly mystical atmosphere. They can even make an identical \"delivery game\" or \"infrastructure-building game.\" But it is very hard to truly reproduce the feeling it gives players. Because the core of the game is not a single system, but a complete experience: walking through the wilderness with cargo, planning routes, keeping balance, completing asynchronous cooperation or confrontation with strangers , slowly stepping out a path on what used to be a barren and rugged wasteland, all inside a self-consistent and rich worldview, in a work that has its own philosophical discussion and ideological expression. Games are interactive art, so let's talk about gameplay. If we only talk about gameplay, it seems a little boring. What it focuses on is the worst part of almost every open-world game: walking time. Most game companies keep optimizing fast travel so players do not spend too much time on the road and get tired. But Death Stranding makes this its main selling point. The game is basically about delivering cargo from A to B. Yet once you truly get into it and quietly sync with the game's wavelength, you find a strong stickiness inside this \"boredom\": climbing steep mountains, sneaking through watcher territory, drawing out and defeating BTs, building infrastructure along the road to make later deliveries easier, and finally delivering cargo to an NPC with zero damage, receiving likes and blueprints for new equipment. Everything you do on this journey, including the path you walk, can be synchronized into another player's world: maybe your infrastructure helps someone else, or maybe the simplest thing you leave behind is just telling others that this route can reach the destination. During the process, you feel the joy of getting new equipment and the sense of achievement that comes from forming \"connections\" with other players. Its worldview also reaches a clever kind of balance. Timefall, umbilical cords, the Beach, the chiral network: a series of new terms and settings relate to each other while also corresponding to meanings in the real world, creating certain metaphors that make the world easier to understand and enter let me complain here about the long-winded text and so-called \"setting terms\" in some gacha games . All settings participate in the difficulties and challenges players encounter: some as special mechanics, some as final goals, and some as potential dangers that need to be guarded against. Meanwhile, Kojima-san also adds many Easter eggs into this world, or you could say \"humorous and abstract\" content, just like the performances I mentioned at the beginning. At first they made me wonder: won't this break the carefully designed worldview? For such a high-budget AAA game, is it not too risky for the director to smuggle in so much personal taste? But the result is just like the line circulating online: \"Commercialization is under pressure, and finally yielded to Hideo Kojima.\" For casual players, these moments bring a knowing smile during the game. But if you dig into them carefully, you find that they not only do not break the worldview, but add another layer of metaphor and philosophical thought to it. The appropriately abrupt feeling hooks players into thinking. While feeling surprised, they can also experience the corresponding meaning. Whether you understand it or not, you slowly accept this abruptness, and it becomes one of the game's unique features. So I started to wonder: what kind of game creator is Hideo Kojima exactly? Why does he have so many fans around the world? Why does he dare to invest so much money into making a game that looks \"not in line with the blockbuster formula\"? Why was the first game so divisive when it first came out, while by the second one, many players could already seriously discuss it as a Game of the Year candidate? What Meme Means With this curiosity, I watched some Death Stranding 2 lore analysis videos on Bilibili and read some interviews with Hideo Kojima. I learned about a turbulent life experience full of misfortune and coincidence, one that carries the shadow of many otaku boys. I also learned how movies, games, reading, and thinking inspired his creative career. One interview especially caught my attention: when talking about his first directed work, Metal Gear , he said it mainly explored Meme. When I first saw the word, my immediate reaction was: how could \"meme images\" have been a subject of discussion nearly forty years ago? After all, in today's internet context, the most common Chinese translation of \"meme\" is basically \"funny image template\": cats and dogs with captions, reaction images, abstract templates. They all seem to count as memes. But the Meme Kojima was talking about was clearly not that. So I looked it up. According to the Wikipedia entry on Meme, the concept of the meme comes from Richard Dawkins's 1976 book The Selfish Gene . Dawkins wanted to express that culture also has units of transmission similar to genes. A meme can be a melody, an idea, a slogan, a fashion, a skill, or any cultural fragment that people imitate, copy, transform, and continue spreading. In other words, a meme was not originally an internet reaction image. Reaction images are certainly memes, but memes are much bigger than reaction images. Our two-part allegorical sayings are memes, idioms are memes, books are memes, movies are memes, internet catchphrases are memes, collective repetition in Bilibili comments is a meme, gamer slang is a meme you are not qualified, you are not qualified, and because of that you are not qualified , and even certain creative methods, aesthetic tastes, and narrative formulas are memes. Like genes inside culture, they are copied, rewritten, sometimes misunderstood, then recreated by generation after generation, and they keep living. AI Cannot Create Cultural Genes After understanding what meme means, and then thinking about the story design and hidden metaphors behind Death Stranding 2 , I could not help connecting it to the hottest concept of our era: AI. Suddenly, the whole thing became interesting to me. Today's large language models are not truly people with lived experience. They do not have blurry childhood memories, the joy of staying up all night playing games, the surprise of walking into the wilderness for the first time, or the feeling of being struck by a lyric or a melody late at night while emo. In essence, they are huge probability calculators, absorbing a vast amount of text, images, code, music, and knowledge left behind by humans, then searching for vector relationships among those existing materials and giving an answer that is \"most like something a human would say.\" In other words, through a massive amount of human corpus, AI has already become very good at using existing memes. Ask it to write a cyberpunk story, and it knows how neon lights, rainy nights, prosthetic bodies, monopolistic corporations, and lower-class districts should be combined. Ask it to design a Soulslike game setting, and it knows roughly what ruined kingdoms, lost gods, curses, bosses, and obscure item descriptions should look like. Ask it to make an \"internet-style\" image, and it also knows the common templates in contemporary aesthetics. But the problem is that most of this is still calling, stitching, and redistributing existing cultural genes. AI can quickly rearrange the familiar things we already know, sometimes beautifully, completely, and convincingly. But New Cultural Currents Often Come from Bold Attempts and Mistakes Looking back at new cultural currents in human history, quite a few impressive ones came from a bold attempt, a misunderstanding, or simply an accident. Take the Renaissance as an example. Some ancient Greek and Roman knowledge of philosophy, literature, art, and science had been preserved, filtered, and used by the Church for theological purposes during the Middle Ages, partly to consolidate its rule and class structure. During the Renaissance, humanists such as Petrarch and Boccaccio rediscovered a large number of classical texts from the Arab world and monasteries. But they did not simply restore them in a fundamentalist way. Instead, they interpreted them through the eyes and needs of their own time . They emphasized the praise of human reason, secular life, and bodily beauty in classical culture, while to some extent ignoring or downplaying the superstition, slavery, and pessimism that also existed in antiquity. This created a historical and cultural explosion. Even Confucianism, which we now sometimes criticize as part of feudal tradition, was also an incredibly rebellious existence in its own social context. AI only produces something that is highly likely to be correct based on existing memes, or cultural content. The difference already appears at the level of underlying logic. So if you ask whether it can create a truly new meme, whether it can continuously create a new mode of expression, a new structure of experience, or a new \"cultural gene\" that humans will understand from then on, I still feel somewhat doubtful. That brings me back to why the performance arrangements and gameplay design of Death Stranding feel so precious to me. It does not simply invite Hollywood actors, shoot a pile of luxurious cutscenes, and then stuff them into an open-world gameplay loop everyone already knows. It did invite recognizable actors such as Norman Reedus, Léa Seydoux, and Mads Mikkelsen, and it does have cinematic shots, music, and narrative ambition. But it does not treat these things merely as gimmicks to make money. Instead, it uses a very strange, risky, and unstable gameplay system to push a new experience in front of players. \"Delivery\" is such an ordinary thing, even boring-sounding. But in Death Stranding , it becomes a way of connecting people. What you carry is not just cargo, but also weight, responsibility, route choice, risk, and time. A bridge you build may be used by another player you will never meet. A charging station you place may just happen to save a desperate player. The game's \"connection\" is not a line of dialogue. It is built into terrain, physics, resources, and asynchronous multiplayer systems. Even when I continued building facilities later, I started thinking about whether this device would still help other players after entering their worlds. This feels very much like the birth of a new meme. Before it, it was hard to imagine that a big-budget game could turn \"walking\" and \"delivery\" into the core experience. After it, whenever people see similar asynchronous assistance or confrontation, they may say: \"This feels a bit like Death Stranding.\" That sentence itself shows that it has already become a cultural unit that can be recognized, quoted, and spread. From now on, there are two kinds of people in the world: those who have not played Death Stranding , and those who have. As for the bizarre performance effects mentioned earlier, I think it is precisely these bizarre performances that, in return, become strange memes. They give this distinctive game an additional kind of \"connection\" with other cultures and fans, and create new memes belonging to its own players, like a secret key for connection among the game's fans. From Another Angle, the Relationship Between AI and Meme May Not Be So Pessimistic If AI only keeps repeating old memes, then of course it will make culture more average, more similar, more \"correct but boring.\" We can already see this flavor in many places: copy that is as pretty as a template but stiff, images that are as polished as templates but incomplete, and expressions that are fluent but lack personal experience. They are not bad, but they are also unlikely to leave long-lasting traces. But from the opposite direction, AI may also become a tool that helps memes keep growing. In the past, when someone had a strange idea and wanted to turn it into a game, animation, software, short film, or other cultural work, they often needed a lot of money, a team, technical accumulation, or the ability to absorb trial-and-error costs. Many ideas were not worthless; they died before they could land. Modeling, programming, art, sound effects, editing, testing: these are not merely \"repetitive labor,\" but they really are high walls that must be crossed before an idea becomes a work. If AI continues to develop and can truly reduce costs in modeling, code, animation, scripts, level prototypes, and other parts of the process, letting more people bring their ideas into reality with smaller teams, less money, and shorter trial-and-error cycles, then that might be a beautiful thing. My own little website also developed this quickly and gained this many effects precisely because of AI. Because culture is not created only by big companies and big budgets. Many new memes may begin as one person's strange thought, a small team's obsession, a gameplay idea that does not look very commercial, or a simple sentence: \"I just want to try whether this works.\" If AI can drag these thoughts out of people's heads and into reality, allowing more works that previously had no chance to take shape to be seen, then it is not replacing culture. It is widening the entrance through which culture can happen. Of course, the premise is that we still provide something truly human. AI can help us reach the work faster. But new memes may still have to grow out of human loneliness, curiosity, stubbornness, failure, and experience. The inspiration Death Stranding 2 gave me is probably this: when a creator truly presses their feelings, aesthetics, and sense of problems into a work, it has the chance to become a new cultural gene. At first, people may find it strange, alternative, or unlike a \"normal game.\" But as long as it really strikes something, it will be remembered, discussed, imitated, or misunderstood, and then it will keep spreading. Finally, I want to use the closing words Hideo Kojima gives to players in Death Stranding 2 to look toward our future and development in the AI era. Even if we do not know what the future will be like, we should not stop exploring, and we should not stop trying. Though the future is a product of every present that precedes it, Tomorrow does not belong to today. To live is to imagine ourselves in the future. And there we inevitably arrive Yet our place in said future may not be the one we envision. -- Kobo Abe, The Search for the Present",
      "content_html": "<p>Recently I finished the story of <em>Death Stranding 2</em>. The abstractness of some of its performances shocked me. I could not understand why certain scenes were designed that way: why did people suddenly start singing here? Why did the villain perform the American national anthem at the end? And why was pizza kung fu so powerful? After doing some quick research, I suddenly realized something: this kind of game seems very hard to copy. Not only because of its gameplay, but even more because of its cultural expression.</p>\n<hr>\n<h2>What Kind of Game Is Death Stranding?</h2>\n<p><img src=\"https://img.markxu.icu/img3fb98aadb2ed7fe1f555e6139a76c72919300662.png\" alt=\"\"></p>\n<p>Unlike the game genres already on the market, it has its own new gameplay experience and worldview.</p>\n<p>It is not like the <em>Assassin's Creed</em> series, which has already formed a relatively mature AAA industrial template: a big open world, repeated outpost clearing, RPG-style equipment growth, question marks all over the map <em>(the question marks in The Witcher 3 were still pretty good)</em>, and static story cutscenes. It feels as if, as long as the budget, team, and production pipeline are big enough, you can make something that &quot;looks about right&quot; <em>(though I heard Ubisoft is remaking Black Flag, so I will still have to try it on hands)</em>. It is also not like Soulslikes, which have a very clear gameplay skeleton: bonfires, death penalties, stamina bars, boss design, fragmented narrative. Change the worldview and art theme, and players can quickly understand, &quot;Oh, this has some Souls flavor.&quot; Nor is it like a linear AAA game that gives you as many cinematic shocks and exquisite level designs as possible within a limited route.</p>\n<p><em>Death Stranding</em> is different.</p>\n<p><img src=\"https://img.markxu.icu/img820493971faa912767921b744a29bc6219300662.png\" alt=\"\"></p>\n<p>Its gameplay is new, its game concept is something I have never seen before, and the pleasure it brings has a very distinctive taste.</p>\n<p>Others can imitate its art, its long takes, its barren, absurd, realistic, or slightly mystical atmosphere. They can even make an identical &quot;delivery game&quot; or &quot;infrastructure-building game.&quot; But it is very hard to truly reproduce the feeling it gives players. Because the core of the game is not a single system, but a complete experience: walking through the wilderness with cargo, planning routes, keeping balance, completing asynchronous cooperation <em>or confrontation</em> with <strong>strangers</strong>, slowly stepping out a path on what used to be a barren and rugged wasteland, all inside a self-consistent and rich worldview, in a work that has its own philosophical discussion and ideological expression.</p>\n<p><img src=\"https://img.markxu.icu/img5fa4345c95279387582d4c01011e86b019300662.png\" alt=\"A screenshot from someone on Bilibili\"></p>\n<p>Games are interactive art, so let's talk about gameplay.</p>\n<p>If we only talk about gameplay, it seems a little boring. What it focuses on is the worst part of almost every open-world game: walking time. Most game companies keep optimizing fast travel so players do not spend too much time on the road and get tired. But <em>Death Stranding</em> makes this its main selling point. The game is basically about delivering cargo from A to B. Yet once you truly get into it and quietly sync with the game's wavelength, you find a strong stickiness inside this &quot;boredom&quot;: climbing steep mountains, sneaking through watcher territory, drawing out and defeating BTs, building infrastructure along the road to make later deliveries easier, and finally delivering cargo to an NPC with zero damage, receiving likes and blueprints for new equipment. Everything you do on this journey, including the path you walk, can be synchronized into another player's world: maybe your infrastructure helps someone else, or maybe the simplest thing you leave behind is just telling others that this route can reach the destination. During the process, you feel the joy of getting new equipment and the sense of achievement that comes from forming &quot;connections&quot; with other players.</p>\n<p><img src=\"https://img.markxu.icu/img20260515235156594.png\" alt=\"Let's deliver together, peko\"></p>\n<p>Its worldview also reaches a clever kind of balance. Timefall, umbilical cords, the Beach, the chiral network: a series of new terms and settings relate to each other while also corresponding to meanings in the real world, creating certain metaphors that make the world easier to understand and enter <em>(let me complain here about the long-winded text and so-called &quot;setting terms&quot; in some gacha games)</em>. All settings participate in the difficulties and challenges players encounter: some as special mechanics, some as final goals, and some as potential dangers that need to be guarded against. Meanwhile, Kojima-san also adds many Easter eggs into this world, or you could say &quot;humorous and abstract&quot; content, just like the performances I mentioned at the beginning. At first they made me wonder: won't this break the carefully designed worldview? For such a high-budget AAA game, is it not too risky for the director to smuggle in so much personal taste?</p>\n<p>But the result is just like the line circulating online: <strong>&quot;Commercialization is under pressure, and finally yielded to Hideo Kojima.&quot;</strong></p>\n<p>For casual players, these moments bring a knowing smile during the game. But if you dig into them carefully, you find that they not only do not break the worldview, but add another layer of metaphor and philosophical thought to it. The appropriately abrupt feeling hooks players into thinking. While feeling surprised, they can also experience the corresponding meaning. Whether you understand it or not, you slowly accept this abruptness, and it becomes one of the game's unique features.</p>\n<p><img src=\"https://img.markxu.icu/img20260515234853420.png\" alt=\"Hideo Kojima and his studio logo\"></p>\n<p><strong>So I started to wonder: what kind of game creator is Hideo Kojima exactly?</strong></p>\n<p>Why does he have so many fans around the world? Why does he dare to invest so much money into making a game that looks &quot;not in line with the blockbuster formula&quot;? Why was the first game so divisive when it first came out, while by the second one, many players could already seriously discuss it as a Game of the Year candidate?</p>\n<h2>What Meme Means</h2>\n<p>With this curiosity, I watched some <em>Death Stranding 2</em> lore analysis videos on Bilibili and read some interviews with Hideo Kojima. I learned about a turbulent life experience full of misfortune and coincidence, one that carries the shadow of many otaku boys. I also learned how movies, games, reading, and thinking inspired his creative career. One interview especially caught my attention: when talking about his <strong>first directed work, Metal Gear</strong>, he said it mainly explored Meme.</p>\n<p>When I first saw the word, my immediate reaction was: how could &quot;meme images&quot; have been a subject of discussion nearly forty years ago? After all, in today's internet context, the most common Chinese translation of &quot;meme&quot; is basically &quot;funny image template&quot;: cats and dogs with captions, reaction images, abstract templates. They all seem to count as memes. But the Meme Kojima was talking about was clearly not that.</p>\n<p><img src=\"https://img.markxu.icu/img20260516003944566.png\" alt=\"A familiar kind of meme\"></p>\n<p>So I looked it up.</p>\n<p>According to the <a href=\"https://en.wikipedia.org/wiki/Meme\">Wikipedia entry on Meme</a>, the concept of the meme comes from Richard Dawkins's 1976 book <em>The Selfish Gene</em>. Dawkins wanted to express that culture also has units of transmission similar to genes. A meme can be a melody, an idea, a slogan, a fashion, a skill, or any cultural fragment that people imitate, copy, transform, and continue spreading.</p>\n<p>In other words, a meme was not originally an internet reaction image.</p>\n<p>Reaction images are certainly memes, but memes are much bigger than reaction images. Our two-part allegorical sayings are memes, idioms are memes, books are memes, movies are memes, internet catchphrases are memes, collective repetition in Bilibili comments is a meme, gamer slang is a meme <em>(you are not qualified, you are not qualified, and because of that you are not qualified)</em>, and even certain creative methods, aesthetic tastes, and narrative formulas are memes. Like genes inside culture, they are copied, rewritten, sometimes misunderstood, then recreated by generation after generation, and they keep living.</p>\n<h2>AI Cannot Create Cultural Genes</h2>\n<p>After understanding what meme means, and then thinking about the story design and hidden metaphors behind <em>Death Stranding 2</em>, I could not help connecting it to the hottest concept of our era: AI. Suddenly, the whole thing became interesting to me.</p>\n<p>Today's large language models are not truly people with lived experience. They do not have blurry childhood memories, the joy of staying up all night playing games, the surprise of walking into the wilderness for the first time, or the feeling of being struck by a lyric or a melody late at night while emo. In essence, they are <a href=\"https://ynarwal.github.io/how-llms-work/\">huge probability calculators</a>, absorbing a vast amount of text, images, code, music, and knowledge left behind by humans, then searching for vector relationships among those existing materials and giving an answer that is &quot;most like something a human would say.&quot;</p>\n<p><img src=\"https://img.markxu.icu/img20260516010924848.png\" alt=\"A large model calculating the probability of the next word\"></p>\n<p>In other words, through a massive amount of human corpus, AI has already become very good at using existing memes.</p>\n<p>Ask it to write a cyberpunk story, and it knows how neon lights, rainy nights, prosthetic bodies, monopolistic corporations, and lower-class districts should be combined. Ask it to design a Soulslike game setting, and it knows roughly what ruined kingdoms, lost gods, curses, bosses, and obscure item descriptions should look like. Ask it to make an &quot;internet-style&quot; image, and it also knows the common templates in contemporary aesthetics.</p>\n<p>But the problem is that most of this is still calling, stitching, and redistributing existing cultural genes. AI can quickly rearrange the familiar things we already know, sometimes beautifully, completely, and convincingly.</p>\n<h3>But New Cultural Currents Often Come from Bold Attempts and Mistakes</h3>\n<p>Looking back at new cultural currents in human history, quite a few impressive ones came from a bold attempt, a misunderstanding, or simply an accident.</p>\n<p>Take the Renaissance as an example. Some ancient Greek and Roman knowledge of philosophy, literature, art, and science had been preserved, filtered, and used by the Church for theological purposes during the Middle Ages, partly to consolidate its rule and class structure. During the Renaissance, humanists such as Petrarch and Boccaccio rediscovered a large number of classical texts from the Arab world and monasteries. But they did not simply restore them in a fundamentalist way. Instead, they <strong>interpreted them through the eyes and needs of their own time</strong>. They emphasized the praise of human reason, secular life, and bodily beauty in classical culture, while to some extent ignoring or downplaying the superstition, slavery, and pessimism that also existed in antiquity. This created a historical and cultural explosion. Even Confucianism, which we now sometimes criticize as part of feudal tradition, was also an incredibly rebellious existence in its own social context.</p>\n<p>AI only produces something that is highly likely to be correct based on existing memes, or cultural content. The difference already appears at the level of underlying logic. So if you ask whether it can create a truly new meme, whether it can continuously create a new mode of expression, a new structure of experience, or a new &quot;cultural gene&quot; that humans will understand from then on, I still feel somewhat doubtful.</p>\n<p><img src=\"https://img.markxu.icu/img20260516011336588.png\" alt=\"I will catch you steadily\"></p>\n<p>That brings me back to why the performance arrangements and gameplay design of <em>Death Stranding</em> feel so precious to me.</p>\n<p>It does not simply invite Hollywood actors, shoot a pile of luxurious cutscenes, and then stuff them into an open-world gameplay loop everyone already knows. It did invite recognizable actors such as Norman Reedus, Léa Seydoux, and Mads Mikkelsen, and it does have cinematic shots, music, and narrative ambition. But it does not treat these things merely as gimmicks to make money. Instead, it uses a very strange, risky, and unstable gameplay system to push a new experience in front of players.</p>\n<p><img src=\"https://img.markxu.icu/img20260516012130003.png\" alt=\"A star-studded cast\"></p>\n<p>&quot;Delivery&quot; is such an ordinary thing, even boring-sounding.</p>\n<p>But in <em>Death Stranding</em>, it becomes a way of connecting people. What you carry is not just cargo, but also weight, responsibility, route choice, risk, and time. A bridge you build may be used by another player you will never meet. A charging station you place may just happen to save a desperate player. The game's &quot;connection&quot; is not a line of dialogue. It is built into terrain, physics, resources, and asynchronous multiplayer systems. Even when I continued building facilities later, I started thinking about whether this device would still help other players after entering their worlds.</p>\n<p>This feels very much like the birth of a new meme.</p>\n<p>Before it, it was hard to imagine that a big-budget game could turn &quot;walking&quot; and &quot;delivery&quot; into the core experience. After it, whenever people see similar asynchronous assistance or confrontation, they may say: &quot;This feels a bit like Death Stranding.&quot; That sentence itself shows that it has already become a cultural unit that can be recognized, quoted, and spread.</p>\n<p><strong>From now on, there are two kinds of people in the world: those who have not played <em>Death Stranding</em>, and those who have.</strong></p>\n<p>As for the bizarre performance effects mentioned earlier, I think it is precisely these bizarre performances that, in return, become strange memes. They give this distinctive game an additional kind of &quot;connection&quot; with other cultures and fans, and create new memes belonging to its own players, like a secret key for connection among the game's fans.</p>\n<p><img src=\"https://img.markxu.icu/img20260516012207764.png\" alt=\"The background music of the original Ultraman starts here\"></p>\n<h2>From Another Angle, the Relationship Between AI and Meme May Not Be So Pessimistic</h2>\n<p>If AI only keeps repeating old memes, then of course it will make culture more average, more similar, more &quot;correct but boring.&quot; We can already see this flavor in many places: copy that is as pretty as a template but stiff, images that are as polished as templates but incomplete, and expressions that are fluent but lack personal experience. They are not bad, but they are also unlikely to leave long-lasting traces.</p>\n<p><strong>But from the opposite direction, AI may also become a tool that helps memes keep growing.</strong></p>\n<p>In the past, when someone had a strange idea and wanted to turn it into a game, animation, software, short film, or other cultural work, they often needed a lot of money, a team, technical accumulation, or the ability to absorb trial-and-error costs. Many ideas were not worthless; they died before they could land. Modeling, programming, art, sound effects, editing, testing: these are not merely &quot;repetitive labor,&quot; but they really are high walls that must be crossed before an idea becomes a work.</p>\n<p>If AI continues to develop and can truly reduce costs in modeling, code, animation, scripts, level prototypes, and other parts of the process, letting more people bring their ideas into reality with smaller teams, less money, and shorter trial-and-error cycles, then that might be a beautiful thing. <em>(My own little website also developed this quickly and gained this many effects precisely because of AI.)</em></p>\n<p>Because culture is not created only by big companies and big budgets.</p>\n<p>Many new memes may begin as one person's strange thought, a small team's obsession, a gameplay idea that does not look very commercial, or a simple sentence: <strong>&quot;I just want to try whether this works.&quot;</strong> If AI can drag these thoughts out of people's heads and into reality, allowing more works that previously had no chance to take shape to be seen, then it is not replacing culture. It is widening the entrance through which culture can happen. Of course, the premise is that we still provide something truly human. AI can help us reach the work faster. But new memes may still have to grow out of human loneliness, curiosity, stubbornness, failure, and experience.</p>\n<p>The inspiration <em>Death Stranding 2</em> gave me is probably this: when a creator truly presses their feelings, aesthetics, and sense of problems into a work, it has the chance to become a new cultural gene. At first, people may find it strange, alternative, or unlike a &quot;normal game.&quot; But as long as it really strikes something, it will be remembered, discussed, imitated, or misunderstood, and then it will keep spreading.</p>\n<p>Finally, I want to use the closing words Hideo Kojima gives to players in <em>Death Stranding 2</em> to look toward our future and development in the AI era. Even if we do not know what the future will be like, we should not stop exploring, and we should not stop trying.</p>\n<blockquote>\n<p>Though the future is a product of every present that precedes it,</p>\n<p>Tomorrow does not belong to today.</p>\n<p>To live is to imagine ourselves in the future.</p>\n<p>And there we inevitably arrive</p>\n<p>Yet our place in said future may not be the one we envision.</p>\n<p>-- Kobo Abe, <em>The Search for the Present</em></p>\n</blockquote>\n<p><img src=\"https://img.markxu.icu/img20260516013501617.png\" alt=\"End credits of the game\"></p>\n",
      "summary": "After finishing the story of Death Stranding 2, I began to understand MEME as a way for the genes of human culture to keep growing. AI can reuse these cultural genes, but new cultural genes may still have to grow out of human experience.",
      "date_published": "2026-05-16T00:00:00.000Z",
      "image": "https://img.markxu.icu/img69e8647962c8ab32d1d0e0fbf1e40b61.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/my-graduation-project-en",
      "url": "https://markxu.icu/blog/my-graduation-project-en",
      "title": "From Student Innovation Project to Graduation Thesis: How My Research Thinking Evolved",
      "content_text": "After finishing my graduation project, I kept wanting to write a blog post about it. I do not want to rewrite my thesis abstract. If I did that, it would be better to just read the thesis. What I want to record is how the project actually grew: at first it did not even have a clear direction. It was only an ordinary attempt to ask my advisor whether there was a suitable project I could join. Later, step by step, it became a student innovation project, a system, an algorithm, and finally my undergraduate graduation thesis. Looking back now, the most interesting part is not that I eventually built some amazing model or algorithm implementation. It is that I clearly felt, for the first time, that failure in research or engineering is not a full stop. It is a concrete signpost, telling me where to go next. The Starting Point: A Challenge In the second semester of my sophomore year, I went to my undergraduate advisor and asked whether there was any project suitable for me to participate in, partly because a senior strongly recommended that I try applying for a student innovation and entrepreneurship project. At that time, I did not have a clear idea of \"medical imaging\" yet. My understanding of \"segmentation\" was still roughly: circle out a certain region in an image, just like those classic YOLO examples from introductory computer science courses. My advisor introduced me to the PENGWIN 2024 pelvic fracture segmentation challenge, and asked me to first try exploring around this competition. PENGWIN's task is very intuitive, and also very difficult: given CT or X-ray data from patients with pelvic fractures, let an algorithm automatically segment the fracture fragments. The public description mentions why this type of task is hard: the pelvis has a complex structure; fracture fragments vary greatly in shape and location; and fracture surfaces often become blurry due to collision, compression, and adhesion. This was already close to the frontier of the field. For a complete beginner like me, it was extremely hard to even get started, not to mention that I still had ambitions to make something \"innovative.\" The training set for PENGWIN Task 1 on Zenodo contains 100 CT cases with labels. The full dataset comes from 150 patients, with 50 cases reserved as the official test set for final scoring. The data clearly annotates the sacrum, left and right hip bones, and their fragments, with detailed descriptions. It was an ideal dataset. Back then, I did not truly feel the weight of those difficulties. My first reaction was more like: since there is data, a competition, and a task, let's build the system first. And so the student innovation project that \"tortured\" me for a whole year began. That year, several teammates and I built a basic medical management system. The system itself needed to handle data management, model invocation, and result visualization. It sounded like a complete project, and in practice it really did take a lot of time. Frontend, backend, data flow, model APIs: many things were written while searching, rewritten while learning, and overturned while building. Very often we worked until four or five in the morning before realizing we really had to rest. The hardest part of the whole process was team collaboration: explaining task requirements, expected outcomes, and so on. That development experience with teammates also exhausted me quite a lot maybe I will write another article about that later, but it is not the focus this time . By the midterm stage, the system finally had a basic shape, but my advisor pointed out a more important issue: the system could display results, but our medical image segmentation algorithm also needed innovation. Directly using other people's work was not enough for a student innovation project. That sentence hurt a bit. After slowly grinding out the system, it is easy to mistakenly believe that most of the work is already done, at least because the visible workload is clear. But in medical image segmentation, the interface only \"serves the result.\" The truly difficult part is where the result comes from. And that hidden workload is much harder to explain clearly to others. My First U-Net, and My First Failure That was when I officially started filling in my knowledge of medical image segmentation. Naturally, the first thing I could not avoid was U-Net. It is a classic encoder-decoder structure in medical image segmentation. One side downsamples to extract semantic information, the other side upsamples to recover spatial details, and skip connections bring shallow texture information back in between. It did not sound too complicated, so I naturally developed a naive confidence: then I will write one myself. So I hand-wrote my first U-Net. Looking back now, that was probably one of the most educational pieces of code I have ever written. The model could run, the loss would drop, and the results could be visualized. Seeing my GPU fully occupied, the terminal reporting a new round of training information, and the loss curve slowly going down was honestly a very happy thing. But the results were still poor. Later, I tried adding small modules such as depthwise separable convolutions, spatial-channel dual attention, and SE blocks. At that stage, I had read very few papers. More often, I picked things from existing modules that \"looked possibly useful,\" stuffed them into the network, and then listened to AI's flattery, feeling that I had done a decent job. The results did not suddenly improve just because there were more modules. There was some improvement, but still not enough. What was more brutal was that it still could not beat the competition baseline. That hit me hard. I had spent a year building the system, modifying the model, staying up late for months, and even abandoning fitness I was genuinely afraid of sudden death , but the final algorithmic effect was not ideal. That was the first time I realized deep learning does not automatically become stronger by stacking a few \"advanced modules\" together. Especially in medical imaging, data volume, annotation strategy, preprocessing, loss functions, post-processing, and evaluation metrics may each be more important than \"adding another attention module.\" It was exactly this failure that pushed me toward the real problem of my graduation project: do not rush to invent a complex network. First understand why a strong baseline is strong, and then see where there is still room for improvement. Relearning from nnU-Net During the graduation project stage, a senior helped me reproduce nnU-Net. This step was crucial. What makes nnU-Net powerful is not only its network structure, but the way it systematizes a large amount of \"engineering experience\" in medical image segmentation. It automatically configures preprocessing, patch size, network topology, training strategy, and post-processing according to the properties of the data. So in many cases, it is not just an ordinary baseline, but a very solid segmentation pipeline. The nnU-Net paper also mentions that it can outperform many specially designed methods across multiple public medical segmentation tasks without heavy manual tuning. After reproducing nnU-Net, I finally had a more reliable reference. Then I read work related to FracSegNet. Instead of directly segmenting everything inside the full CT volume, it decomposes the task into two steps: first locating the left hip bone, right hip bone, and sacrum, then segmenting major and minor fragments inside the target bone regions. It also introduces a fracture distance map, or FDM, which makes the model pay more attention to regions near fracture contact surfaces. This idea inspired me a lot. The most troublesome part of fracture segmentation is often not whether the whole bone can be segmented, but whether a tiny gap between neighboring fragments gets filled, whether small fragments are missed, and whether strange false positives appear near broken ends. The overall Dice score may already look very high, but if two bone fragments that should be separated are stuck together by the model, the spatial relationship in 3D reconstruction and preoperative planning becomes completely different. In other words, this task does not need \"roughly similar.\" It needs \"the local structure must not be messed up.\" The New Paper from March Just after I finished reproducing nnU-Net and FracSegNet, while I was still worrying about how to improve further, my advisor gave me a new paper from March. The method was called SCNP, short for Same Class Neighbor Penalization. The starting point of the paper is clever: many segmentation models perform well on the overall region, but cannot guarantee accurate topology. For example, thin structures may break, small regions may appear out of nowhere, and originally separated structures may become stuck together. SCNP does not redesign the network. Instead, during training, it performs neighborhood propagation on logits: foreground positions are affected by weaker responses among same-class neighbors, while background positions are affected by more confusing responses among same-class neighbors. Intuitively, it means the model cannot pass by only predicting the center voxel well. It also needs to care about the most error-prone places among nearby same-class neighbors. This idea immediately matched pelvic fracture segmentation. The problem of fracture fragments is exactly a local structure problem. A narrow gap being filled, a small fragment being swallowed, or a false positive appearing near a broken end are all typical cases where \"the large overall region looks fine, but the local connection relationship is wrong.\" SCNP also has an attractive engineering property: it mainly changes the logits before loss calculation during training. It does not require changing the inference process, nor does it require redesigning an entire complex network. However, directly applying SCNP to pelvic CT also carries risks. CT has noise, and there are stable bone regions far away from fracture surfaces. If neighborhood propagation is applied everywhere, it may amplify local errors in irrelevant regions too. So my graduation project made a compromise: combine the FDM idea from FracSegNet with SCNP, using the fracture distance map as a spatial gate so that SCNP acts more strongly near the high-risk fracture contact surfaces. In one AI-style sentence: do not make the model nervous across the whole bone; make it more serious where things are truly likely to go wrong. What My Graduation Project Actually Did In the end, the core of my graduation project can be summarized into three things. First, I reproduced and organized the baseline pipeline for pelvic fracture CT segmentation. The experiments used nnU-Net as the unified framework, comparing Max-flow, standard nnU-Net, the reproduced FracSegNet baseline, and different SCNP variants. The labels were also reorganized into three semantic classes: background, major fragments, and minor fragments. Second, I introduced SCNP neighborhood structure propagation before loss calculation. It does not change the inference structure of the network. During training, it makes locally difficult regions participate more directly in optimization, thereby complementing the insufficient constraints of CE + Dice on fragment boundaries, small gaps, and local connectivity. Third, I combined FDM and SCNP into a gated version. FDM-gated SCNP concentrates structural propagation near fracture surfaces and reduces unnecessary propagation in stable bone regions or noisy areas. In the experiments, I roughly compared different receptive fields and gating thresholds, and finally found that was a relatively balanced setting. Under unified local post-processing and the same evaluation protocol, FDM-gated SCNP achieved more balanced results compared with the reproduced FracSegNet baseline: All DSC reached , All LDSC reached , All HD95 was , and IoU was . Compared with the FracSegNet baseline, HD95 decreased by about , and IoU improved by about . Of course, these numbers should not be understood as \"the problem is solved.\" There are no such easy victories in medical imaging. They are more like evidence for a small and specific point: on top of a strong baseline, if the training objective is pushed a little toward local structure, the model can indeed become more stable in boundary distance, local Dice, and fragment separation quality. Beyond the Results: What I Really Learned The most useful part of this graduation project was not a certain metric, but the way my understanding of \"innovation\" changed. At first, I thought innovation meant adding modules, changing structures, and drawing a more complicated network. Later I realized that in a real task, innovation is sometimes the opposite: explaining the problem clearly. Is the whole segmentation inaccurate, or are local boundaries unstable? Is model capacity insufficient, or does the loss function fail to punish the truly important errors? Do we need a larger receptive field, or do we need to constrain the propagation range? My first U-Net did not beat the baseline. That is certainly nothing to brag about, but it forced me to admit that my understanding of the task was too shallow. Later, reproducing nnU-Net and FracSegNet showed me that strong methods are not strong out of nowhere. They consider data organization, label design, training strategy, post-processing, and evaluation metrics together. Finally, introducing SCNP was not because it sounded new, but because it happened to respond to the recurring local structure errors in this task. From the student innovation project to the graduation thesis, this project felt like taking a long detour. At first I wanted to build a system, then I realized the algorithm behind the system was not good enough. I went on to modify the model structure, then discovered that casually adding modules was meaningless. I began reproducing strong baselines, and only then gradually saw the real problem. Finally, I returned to a very small change: during training, let the model look more carefully at the hardest nearby region. Maybe this is what a graduation project is for. It does not necessarily make you build something that changes the world, but it lets you go through a complete learning process with frustration included. You learn how a result comes into being, and you also learn why it is still not good enough. Problems That Are Not Over Yet Of course, this work still has many limitations. The current label space is still a three-class semantic segmentation of background, major fragments, and minor fragments. It cannot truly output an instance ID for every individual fragment. For complex multi-fragment cases, especially very small fragments, unified post-processing may still miss or merge fragments. FDM gating also depends on task priors. The threshold and receptive field mainly come from experimental selection and have not yet become an adaptive mechanism. More importantly, improvement in segmentation metrics does not mean clinical value has been fully validated. If this work continues in the future, the results should be put into downstream tasks such as 3D reconstruction, reduction planning, and surgical path design, to see whether improvements in local boundaries can bring real benefits to the workflow. But at least for me, this graduation project has already completed its most important task: it took me from \"can it run?\" to \"why design it this way?\" It also let me truly understand for the first time that the hardest part of an algorithm project is often not writing the network, but finding the errors worth optimizing. PENGWIN 2024 Challenge Description PENGWIN Task 1 Dataset PENGWIN 2024 Challenge Report nnU-Net Paper FracSegNet / Multi-scale Distance-weighted Pelvic Fracture Segmentation SCNP Paper",
      "content_html": "<p>After finishing my graduation project, I kept wanting to write a blog post about it.</p>\n<p>I do not want to rewrite my thesis abstract. If I did that, it would be better to just read the thesis. What I want to record is how the project actually grew: at first it did not even have a clear direction. It was only an ordinary attempt to ask my advisor whether there was a suitable project I could join. Later, step by step, it became a student innovation project, a system, an algorithm, and finally my undergraduate graduation thesis.</p>\n<p>Looking back now, the most interesting part is not that I eventually built some amazing model or algorithm implementation. It is that I clearly felt, for the first time, that <strong>failure in research or engineering is not a full stop. It is a concrete signpost, telling me where to go next.</strong></p>\n<hr>\n<h2>The Starting Point: A Challenge</h2>\n<p>In the second semester of my sophomore year, I went to my undergraduate advisor and asked whether there was any project suitable for me to participate in, partly because a senior strongly recommended that I try applying for a student innovation and entrepreneurship project. At that time, I did not have a clear idea of &quot;medical imaging&quot; yet. My understanding of &quot;segmentation&quot; was still roughly: circle out a certain region in an image, just like those classic YOLO examples from introductory computer science courses. My advisor introduced me to the <a href=\"https://doi.org/10.5281/zenodo.10990768\">PENGWIN 2024 pelvic fracture segmentation challenge</a>, and asked me to first try exploring around this competition.</p>\n<p>PENGWIN's task is very intuitive, and also very difficult: given CT or X-ray data from patients with pelvic fractures, let an algorithm automatically segment the fracture fragments. The public description mentions why this type of task is hard: the pelvis has a complex structure; fracture fragments vary greatly in shape and location; and fracture surfaces often become blurry due to collision, compression, and adhesion. This was already close to the frontier of the field. For a complete beginner like me, it was extremely hard to even get started, not to mention that I still had ambitions to make something &quot;innovative.&quot; The training set for PENGWIN Task 1 on Zenodo contains 100 CT cases with labels. The full dataset comes from 150 patients, with 50 cases reserved as the official test set for final scoring. The data clearly annotates the sacrum, left and right hip bones, and their fragments, with detailed descriptions. It was an ideal dataset.</p>\n<p>Back then, I did not truly feel the weight of those difficulties. My first reaction was more like: <strong>since there is data, a competition, and a task, let's build the system first.</strong></p>\n<p>And so the student innovation project that &quot;tortured&quot; me for a whole year began.</p>\n<p>That year, several teammates and I built a basic medical management system. The system itself needed to handle data management, model invocation, and result visualization. It sounded like a complete project, and in practice it really did take a lot of time. Frontend, backend, data flow, model APIs: many things were written while searching, rewritten while learning, and overturned while building. Very often we worked until four or five in the morning before realizing we really had to rest. The hardest part of the whole process was team collaboration: explaining task requirements, expected outcomes, and so on. That development experience with teammates also exhausted me quite a lot <em>(maybe I will write another article about that later, but it is not the focus this time)</em>. By the midterm stage, the system finally had a basic shape, but my advisor pointed out a more important issue: the system could display results, but our medical image segmentation algorithm also needed innovation. Directly using other people's work was not enough for a student innovation project.</p>\n<p>That sentence hurt a bit. After slowly grinding out the system, it is easy to mistakenly believe that most of the work is already done, at least because the visible workload is clear. But in medical image segmentation, <strong>the interface only &quot;serves the result.&quot; The truly difficult part is where the result comes from.</strong> And that hidden workload is much harder to explain clearly to others.</p>\n<h2>My First U-Net, and My First Failure</h2>\n<p>That was when I officially started filling in my knowledge of medical image segmentation.</p>\n<p>Naturally, the first thing I could not avoid was U-Net. It is a classic encoder-decoder structure in medical image segmentation. One side downsamples to extract semantic information, the other side upsamples to recover spatial details, and skip connections bring shallow texture information back in between. It did not sound too complicated, so I naturally developed a naive confidence: then I will write one myself.</p>\n<p>So I hand-wrote my first U-Net.</p>\n<p><img src=\"https://img.markxu.icu/img20260515191007353.png\" alt=\"My first U-Net\"></p>\n<p>Looking back now, that was probably one of the most educational pieces of code I have ever written. The model could run, the loss would drop, and the results could be visualized. Seeing my GPU fully occupied, the terminal reporting a new round of training information, and the loss curve slowly going down was honestly a very happy thing. But the results were still poor. Later, I tried adding small modules such as depthwise separable convolutions, spatial-channel dual attention, and SE blocks. At that stage, I had read very few papers. More often, I picked things from existing modules that &quot;looked possibly useful,&quot; stuffed them into the network, and then listened to AI's flattery, feeling that I had done a decent job.</p>\n<p>The results did not suddenly improve just because there were more modules. There was some improvement, but still not enough.</p>\n<p><strong>What was more brutal was that it still could not beat the competition baseline.</strong></p>\n<p>That hit me hard. I had spent a year building the system, modifying the model, staying up late for months, and even abandoning fitness <em>(I was genuinely afraid of sudden death)</em>, but the final algorithmic effect was not ideal. That was the first time I realized deep learning does not automatically become stronger by stacking a few &quot;advanced modules&quot; together. Especially in medical imaging, data volume, annotation strategy, preprocessing, loss functions, post-processing, and evaluation metrics may each be more important than &quot;adding another attention module.&quot;</p>\n<p>It was exactly this failure that pushed me toward the real problem of my graduation project: do not rush to invent a complex network. First understand why a strong baseline is strong, and then see where there is still room for improvement.</p>\n<h2>Relearning from nnU-Net</h2>\n<p>During the graduation project stage, a senior helped me reproduce nnU-Net.</p>\n<p>This step was crucial. What makes nnU-Net powerful is not only its network structure, but the way it systematizes a large amount of &quot;engineering experience&quot; in medical image segmentation. It automatically configures preprocessing, patch size, network topology, training strategy, and post-processing according to the properties of the data. So in many cases, it is not just an ordinary baseline, but a very solid segmentation pipeline. The nnU-Net paper also mentions that it can outperform many specially designed methods across multiple public medical segmentation tasks without heavy manual tuning.</p>\n<p>After reproducing nnU-Net, I finally had a more reliable reference. Then I read work related to FracSegNet. Instead of directly segmenting everything inside the full CT volume, it decomposes the task into two steps: first locating the left hip bone, right hip bone, and sacrum, then segmenting major and minor fragments inside the target bone regions. It also introduces a fracture distance map, or FDM, which makes the model pay more attention to regions near fracture contact surfaces.</p>\n<p>This idea inspired me a lot.</p>\n<p>The most troublesome part of fracture segmentation is often not whether the whole bone can be segmented, but whether a tiny gap between neighboring fragments gets filled, whether small fragments are missed, and whether strange false positives appear near broken ends. The overall Dice score may already look very high, but if two bone fragments that should be separated are stuck together by the model, the spatial relationship in 3D reconstruction and preoperative planning becomes completely different.</p>\n<p>In other words, this task does not need &quot;roughly similar.&quot; It needs &quot;the local structure must not be messed up.&quot;</p>\n<h2>The New Paper from March</h2>\n<p>Just after I finished reproducing nnU-Net and FracSegNet, while I was still worrying about how to improve further, my advisor gave me a new paper from March. The method was called SCNP, short for Same Class Neighbor Penalization.</p>\n<p>The starting point of the paper is clever: many segmentation models perform well on the overall region, but cannot guarantee accurate topology. For example, thin structures may break, small regions may appear out of nowhere, and originally separated structures may become stuck together. SCNP does not redesign the network. Instead, during training, it performs neighborhood propagation on logits: foreground positions are affected by weaker responses among same-class neighbors, while background positions are affected by more confusing responses among same-class neighbors. Intuitively, it means the model cannot pass by only predicting the center voxel well. It also needs to care about the most error-prone places among nearby same-class neighbors.</p>\n<p><img src=\"https://img.markxu.icu/imgSCNP-method.png\" alt=\"SCNP method diagram\"></p>\n<p>This idea immediately matched pelvic fracture segmentation.</p>\n<p>The problem of fracture fragments is exactly a local structure problem. A narrow gap being filled, a small fragment being swallowed, or a false positive appearing near a broken end are all typical cases where &quot;the large overall region looks fine, but the local connection relationship is wrong.&quot; SCNP also has an attractive engineering property: it mainly changes the logits before loss calculation during training. It does not require changing the inference process, nor does it require redesigning an entire complex network.</p>\n<p>However, directly applying SCNP to pelvic CT also carries risks. CT has noise, and there are stable bone regions far away from fracture surfaces. If neighborhood propagation is applied everywhere, it may amplify local errors in irrelevant regions too. So my graduation project made a compromise: combine the FDM idea from FracSegNet with SCNP, using the fracture distance map as a spatial gate so that SCNP acts more strongly near the high-risk fracture contact surfaces.</p>\n<p>In one AI-style sentence: do not make the model nervous across the whole bone; make it more serious where things are truly likely to go wrong.</p>\n<h2>What My Graduation Project Actually Did</h2>\n<p>In the end, the core of my graduation project can be summarized into three things.</p>\n<p>First, I reproduced and organized the baseline pipeline for pelvic fracture CT segmentation. The experiments used nnU-Net as the unified framework, comparing Max-flow, standard nnU-Net, the reproduced FracSegNet baseline, and different SCNP variants. The labels were also reorganized into three semantic classes: background, major fragments, and minor fragments.</p>\n<p>Second, I introduced SCNP neighborhood structure propagation before loss calculation. It does not change the inference structure of the network. During training, it makes locally difficult regions participate more directly in optimization, thereby complementing the insufficient constraints of CE + Dice on fragment boundaries, small gaps, and local connectivity.</p>\n<p>Third, I combined FDM and SCNP into a gated version. FDM-gated SCNP concentrates structural propagation near fracture surfaces and reduces unnecessary propagation in stable bone regions or noisy areas. In the experiments, I roughly compared different receptive fields and gating thresholds, and finally found that <code>rf = 3, tau = 0.3</code> was a relatively balanced setting.</p>\n<p><img src=\"https://img.markxu.icu/imgSCNP-pipeline.png\" alt=\"My method pipeline\"></p>\n<p>Under unified local post-processing and the same evaluation protocol, FDM-gated SCNP achieved more balanced results compared with the reproduced FracSegNet baseline: All DSC reached <code>0.990 ± 0.012</code>, All LDSC reached <code>0.990 ± 0.013</code>, All HD95 was <code>0.868 ± 3.766 mm</code>, and IoU was <code>0.883 ± 0.142</code>. Compared with the FracSegNet baseline, HD95 decreased by about <code>40.3%</code>, and IoU improved by about <code>3.2%</code>.</p>\n<p>Of course, these numbers should not be understood as &quot;the problem is solved.&quot; There are no such easy victories in medical imaging. They are more like evidence for a small and specific point: on top of a strong baseline, if the training objective is pushed a little toward local structure, the model can indeed become more stable in boundary distance, local Dice, and fragment separation quality.</p>\n<h2>Beyond the Results: What I Really Learned</h2>\n<p>The most useful part of this graduation project was not a certain metric, but the way my understanding of &quot;innovation&quot; changed.</p>\n<p>At first, I thought innovation meant adding modules, changing structures, and drawing a more complicated network. Later I realized that in a real task, innovation is sometimes the opposite: explaining the problem clearly. Is the whole segmentation inaccurate, or are local boundaries unstable? Is model capacity insufficient, or does the loss function fail to punish the truly important errors? Do we need a larger receptive field, or do we need to constrain the propagation range?</p>\n<p>My first U-Net did not beat the baseline. That is certainly nothing to brag about, but it forced me to admit that my understanding of the task was too shallow. Later, reproducing nnU-Net and FracSegNet showed me that strong methods are not strong out of nowhere. They consider data organization, label design, training strategy, post-processing, and evaluation metrics together. Finally, introducing SCNP was not because it sounded new, but because it happened to respond to the recurring local structure errors in this task.</p>\n<p>From the student innovation project to the graduation thesis, this project felt like taking a long detour. At first I wanted to build a system, then I realized the algorithm behind the system was not good enough. I went on to modify the model structure, then discovered that casually adding modules was meaningless. I began reproducing strong baselines, and only then gradually saw the real problem. Finally, I returned to a very small change: during training, let the model look more carefully at the hardest nearby region.</p>\n<p>Maybe this is what a graduation project is for. It does not necessarily make you build something that changes the world, but it lets you go through a complete learning process with frustration included. You learn how a result comes into being, and you also learn why it is still not good enough.</p>\n<h2>Problems That Are Not Over Yet</h2>\n<p>Of course, this work still has many limitations.</p>\n<p>The current label space is still a three-class semantic segmentation of background, major fragments, and minor fragments. It cannot truly output an instance ID for every individual fragment. For complex multi-fragment cases, especially very small fragments, unified post-processing may still miss or merge fragments. FDM gating also depends on task priors. The threshold and receptive field mainly come from experimental selection and have not yet become an adaptive mechanism.</p>\n<p>More importantly, improvement in segmentation metrics does not mean clinical value has been fully validated. If this work continues in the future, the results should be put into downstream tasks such as 3D reconstruction, reduction planning, and surgical path design, to see whether improvements in local boundaries can bring real benefits to the workflow.</p>\n<p>But at least for me, this graduation project has already completed its most important task: it took me from &quot;can it run?&quot; to &quot;why design it this way?&quot; It also let me truly understand for the first time that <strong>the hardest part of an algorithm project is often not writing the network, but finding the errors worth optimizing.</strong></p>\n<article-references>\n  <a href=\"https://doi.org/10.5281/zenodo.10990768\" data-note=\"Pelvic fracture CT and X-ray segmentation challenge, introducing task background, data sources, and challenge goals.\">PENGWIN 2024 Challenge Description</a>\n  <a href=\"https://doi.org/10.5281/zenodo.10927452\" data-note=\"Public CT training set and label description.\">PENGWIN Task 1 Dataset</a>\n  <a href=\"https://arxiv.org/abs/2504.02382\" data-note=\"Challenge report covering multi-team results, CT/X-ray task differences, and uncertainty in fragment definitions.\">PENGWIN 2024 Challenge Report</a>\n  <a href=\"https://www.nature.com/articles/s41592-020-01008-z\" data-note=\"A very important self-configuring baseline in medical image segmentation.\">nnU-Net Paper</a>\n  <a href=\"https://conferences.miccai.org/2023/papers/490-Paper1597.html\" data-note=\"Related work on two-stage pelvic fracture segmentation, FDM distance-weighted loss, and public code/data.\">FracSegNet / Multi-scale Distance-weighted Pelvic Fracture Segmentation</a>\n  <a href=\"https://arxiv.org/abs/2603.18671\" data-note=\"A training method that improves topological accuracy in segmentation by penalizing the hardest positions among same-class neighbors.\">SCNP Paper</a>\n</article-references>\n",
      "summary": "From a student innovation project and a medical management system to reproducing and improving a pelvic fracture CT segmentation algorithm, this post records my graduation project and how I slowly learned to find the real problem inside failure.",
      "date_published": "2026-05-15T00:00:00.000Z",
      "date_modified": "2026-05-20T00:00:00.000Z",
      "image": "https://img.markxu.icu/img20260515191007353.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/university-info-drawers-en",
      "url": "https://markxu.icu/blog/university-info-drawers-en",
      "title": "Four Years at University, and a Few of My “Little Drawers”",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. I have always loved surfing the internet and collecting all kinds of strange and wonderful websites and sources of information. Sharing them back and forth with classmates brought me plenty of new ones, and over time they became a shared language among us. My four years of university were, I would say, the four years in which I grew up the fastest socially: I went through a lot and saw a lot. Everything I want to share in this article is something I have been reading all along, and they are the sources I genuinely like. If you have any of your own you would like to share, please leave a comment below! 👏 Tech News Ruan Yifeng's Web Log Teacher Ruan's blog is probably the best known and most familiar of all Chinese personal blogs, and the “Technology Enthusiast Weekly” on this site has become a Friday ritual that many people myself included never miss. It is not confined to pure technology; it reads more like a general-interest magazine for the digital age. It covers the author's observations and thoughts on technology, tech news, excellent articles, new tool recommendations, digests, interesting comments, and more. Add the cover image and introduction the author carefully picks each week, and the content is remarkably rich 🤩—enough to serve as a weekly roundup of tech highlights. My favorite part is the author's tech observations, which are full of interesting thoughts and insights and often leave me feeling as though my cerebral cortex has been swapped out for a new one. Then there is the article recommendation section: I can honestly say that it is mainly through this weekly that I have come across many excellent personal bloggers and fresh ideas the kind that set trends! . Chaping's WeChat Account Updated at midnight sharp every night, Chaping is more like a “tech midnight snack.” On the many nights I was busy fixing bugs 🥱, I would open it at midnight for a bit of diversion. It skims quickly through the day's important little tech news, mostly the stuff closely tied to us users. It also runs short analysis and discussion pieces on whatever topics are currently hot. Chaping's main distinguishing feature is the way it explains hardcore tech news and business developments in very plain language laced with internet memes. The interactions in the comments are usually full of jokes too, and it even has a Daily Joke and an interactive section that gets discussions going in the comments, loosening the nerves wound tight by the day. Hacker News This is a minimalist tech community run by Y Combinator, and an important window through which I get “raw information.” For me, what draws me in most is its ranking algorithm: $$ Score = \\frac{P - 1}{ T + 2 ^G} $$ where, $P$ is the entry's score; subtracting $1$ offsets the submitter's own vote; $T$ is the time elapsed since the entry was submitted, in hours; the +2 keeps the newest posts from making the denominator too small the choice of 2 may be because it takes an average of two hours for an original article to appear elsewhere and then be reposted to Hacker News ; $G$ is the gravity factor, which defaults to $1.8$ in the algorithm and determines how quickly a post's ranking falls, much like the idea of gravitational acceleration. This mechanism automatically filters out content the community agrees is valuable and not yet stale, keeping high-quality technical discussion, startup insights, and cutting-edge papers afloat. It is also basically comprehensive enough. I do not actually read much of it day to day, but I am sharing it here anyway. Current Affairs Then there is current affairs: The Bedtime News Editorial Department WeChat Account Updated every night. The Bedtime News Editorial Department account is the main business of Ma Dugong's studio. Although people on the internet have plenty of criticism for Ma Dugong, the news in the Bedtime News Editorial Department account is still in the higher tier of quality. Every item is distilled into a few key points; open it while you brush your teeth and you will have a basic grasp of what happened today. If your way of looking at problems leans toward “seeking truth from facts” and you dislike preset positions, then his Bilibili account Ma Dugong is worth following too. His analytical framework especially the long-form videos on Bilibili can also offer a lot of “counterintuitive” perspectives. Almost every episode sparks discussion on Zhihu, and when you have nothing else to do you can head over to Zhihu and see the different takes on a single event—which is quite nice, actually! Military Intelligence D7 This is one of the few current-affairs creators I follow who demands a lot of his sources and is not especially emotional no “win, win, win” 😭 . His presentations are very clearly structured, usually organized by region Iran, Russia-Ukraine, the Asia-Pacific, the United States to sort through recent developments. When things were busy he posted almost daily; the frequency has slowed recently, but the content still keeps up high information density and objectivity, avoiding the inflammatory language common to military content—which suits my taste rather well. Technical Deep Dives There are, in fact, a great many sources that lean toward specific technologies. But with the development of AI, going straight to a large model with a specific tech-stack question seems like a faster and more comprehensive option 😅—especially since, if you already have a particular stack in mind, there is no need for me to share anything here; everyone has suitable, targeted blogs and articles to draw on. So I will simply list two that I think are pretty good. Meituan Tech Team Updated irregularly. This is, in my personal view, the tech blog with the highest “substance” among China's big internet companies. It does not write fluff; it is all real, hands-on summaries of Meituan's work in large-scale, high-concurrency business scenarios. The content covers architecture design, AI engineering such as the LongCat series of models , big data, algorithm optimization, and more. It is a great way to sharpen your systems thinking through real cases. Google Research The official blog of Google Research now deeply integrated with DeepMind . This is the “source” of many frontier technologies—the earliest explanations of foundational work such as Transformer and BERT came from here. It mainly publishes the latest progress in machine learning, natural language processing, computer vision, and quantum computing all very cool technologies 😯! . Some of it is on the research-heavy side, but through its accessible posts you can directly feel the direction in which “future technology” is evolving, and seeing them, you are bound to feel at least a little excited. What I have shared above are the sources I followed most often during these four years—in other words, the mysterious “little drawers” I gradually found. They opened up a great many perspectives for me, and they kept my steps from falling too far behind the times. This article is mainly meant to start the conversation: if you have a treasured source of your own, I would love for you to share it in the comments!",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>I have always loved surfing the internet and collecting all kinds of strange and wonderful websites and sources of information. Sharing them back and forth with classmates brought me plenty of new ones, and over time they became a shared language among us. My four years of university were, I would say, the four years in which I grew up the fastest socially: I went through a lot and saw a lot. Everything I want to share in this article is something I have been reading all along, and they are the sources I genuinely like. If <strong>you have any of your own you would like to share, please leave a comment below!</strong> 👏</p>\n<hr>\n<h2>Tech News</h2>\n<h3>Ruan Yifeng's Web Log</h3>\n<p><a href=\"https://www.ruanyifeng.com/blog/\">https://www.ruanyifeng.com/blog/</a></p>\n<p>Teacher Ruan's blog is probably the best known and most familiar of all Chinese personal blogs, and the “Technology Enthusiast Weekly” on this site has become a Friday ritual that many people (myself included) never miss.</p>\n<p>It is not confined to pure technology; it reads more like a general-interest magazine for the digital age. It covers the author's observations and thoughts on technology, tech news, excellent articles, new tool recommendations, digests, interesting comments, and more. Add the cover image and introduction the author carefully picks each week, and the content is remarkably rich 🤩—enough to serve as a weekly roundup of tech highlights.</p>\n<p><img src=\"https://img.markxu.icu/img20260511165922614.png\" alt=\"Ruan Yifeng's Web Log\"></p>\n<p>My favorite part is the author's tech observations, which are full of interesting thoughts and insights and often leave me feeling as though my cerebral cortex has been swapped out for a new one. Then there is the article recommendation section: I can honestly say that it is mainly through this weekly that I have come across many excellent personal bloggers and fresh ideas <em>(the kind that set trends!)</em>.</p>\n<h3>Chaping's WeChat Account</h3>\n<p>Updated at midnight sharp every night, Chaping is more like a “tech midnight snack.” On the many nights I was busy fixing bugs 🥱, I would open it at midnight for a bit of diversion. It skims quickly through the day's important little tech news, mostly the stuff closely tied to us users. It also runs short analysis and discussion pieces on whatever topics are currently hot.</p>\n<p><img src=\"https://img.markxu.icu/img20260511171312528.png\" alt=\"Chaping's WeChat Account\"></p>\n<p>Chaping's main distinguishing feature is the way it explains hardcore tech news and business developments in very plain language laced with internet memes. The interactions in the comments are usually full of jokes too, and it even has a <strong>Daily Joke</strong> and an <strong>interactive section</strong> that gets discussions going in the comments, loosening the nerves wound tight by the day.</p>\n<h3>Hacker News</h3>\n<p><a href=\"https://news.ycombinator.com/\">https://news.ycombinator.com/</a></p>\n<p>This is a minimalist tech community run by Y Combinator, and an important window through which I get “raw information.” For me, what draws me in most is its <a href=\"https://medium.com/hacking-and-gonzo/how-hacker-news-ranking-algorithm-works-1d9b0cf2c08d\">ranking algorithm</a>:</p>\n<p>$$<br>\nScore = \\frac{P - 1}{(T + 2)^G}<br>\n$$</p>\n<p>where,</p>\n<ul>\n<li>$P$ is the entry's score; subtracting $1$ offsets the submitter's own vote;</li>\n<li>$T$ is the time elapsed since the entry was submitted, in hours; the +2 keeps the newest posts from making the denominator too small <em>(the choice of 2 may be because it takes an average of two hours for an original article to appear elsewhere and then be reposted to Hacker News)</em>;</li>\n<li>$G$ is the gravity factor, which defaults to $1.8$ in the algorithm and determines how quickly a post's ranking falls, much like the idea of gravitational acceleration.</li>\n</ul>\n<p>This mechanism automatically filters out content the community agrees is valuable and not yet stale, keeping high-quality technical discussion, startup insights, and cutting-edge papers afloat. It is also basically comprehensive enough. I do not actually read much of it day to day, but I am sharing it here anyway.</p>\n<p><img src=\"https://img.markxu.icu/img20260511181313296.png\" alt=\"Hacker News website\"></p>\n<hr>\n<h2>Current Affairs</h2>\n<p>Then there is current affairs:</p>\n<h3>The <em>Bedtime News Editorial Department</em> WeChat Account</h3>\n<p>Updated every night. The <em>Bedtime News Editorial Department</em> account is the main business of Ma Dugong's studio. Although people on the internet have <strong>plenty of criticism</strong> for Ma Dugong, the news in the <em>Bedtime News Editorial Department</em> account is still in the higher tier of quality. Every item is distilled into a few key points; open it while you brush your teeth and you will have a basic grasp of what happened today.</p>\n<p><img src=\"https://img.markxu.icu/img20260511181953046.png\" alt=\"Bedtime News Editorial Department\"></p>\n<p>If your way of looking at problems leans toward “seeking truth from facts” and you dislike preset positions, then his Bilibili account <a href=\"https://space.bilibili.com/316568752?spm_id_from=333.337.0.0\">Ma Dugong</a> is worth following too. His analytical framework (especially the long-form videos on Bilibili) can also offer a lot of “counterintuitive” perspectives. Almost every episode sparks discussion on Zhihu, and when you have nothing else to do you can head over to Zhihu and see the different takes on a single event—which is quite nice, actually!</p>\n<h3>Military Intelligence D7</h3>\n<p><a href=\"https://space.bilibili.com/448165099\">https://space.bilibili.com/448165099</a></p>\n<p>This is one of the few current-affairs creators I follow who demands a lot of his sources and is not especially emotional <em>(no “win, win, win” 😭)</em>. His presentations are very clearly structured, usually organized by region (Iran, Russia-Ukraine, the Asia-Pacific, the United States) to sort through recent developments. When things were busy he posted almost daily; the frequency has slowed recently, but the content still keeps up high information density and objectivity, avoiding the inflammatory language common to military content—which suits my taste rather well.</p>\n<hr>\n<h2>Technical Deep Dives</h2>\n<p>There are, in fact, a great many sources that lean toward specific technologies. But with the development of AI, going straight to a large model with a specific tech-stack question seems like a faster and more comprehensive option 😅—especially since, if you already have a particular stack in mind, there is no need for me to share anything here; everyone has suitable, targeted blogs and articles to draw on. So I will simply list two that I think are pretty good.</p>\n<h3>Meituan Tech Team</h3>\n<p><a href=\"https://tech.meituan.com/\">https://tech.meituan.com/</a></p>\n<p>Updated irregularly. This is, in my personal view, the tech blog with the highest “substance” among China's big internet companies. It does not write fluff; it is all real, hands-on summaries of Meituan's work in large-scale, high-concurrency business scenarios. The content covers architecture design, AI engineering <em>(such as the LongCat series of models)</em>, big data, algorithm optimization, and more. It is a great way to sharpen your systems thinking through real cases.</p>\n<p><img src=\"https://img.markxu.icu/img20260511190239663.png\" alt=\"Meituan Tech Team\"></p>\n<h3>Google Research</h3>\n<p><a href=\"https://research.google/blog/\">https://research.google/blog/</a></p>\n<p>The official blog of Google Research (now deeply integrated with DeepMind). This is the “source” of many frontier technologies—the earliest explanations of foundational work such as Transformer and BERT came from here. It mainly publishes the latest progress in machine learning, natural language processing, computer vision, and quantum computing <em>(all very cool technologies 😯!)</em>. Some of it is on the research-heavy side, but through its accessible posts you can directly feel the direction in which “future technology” is evolving, and seeing them, you are bound to feel at least a little excited.<br>\n<img src=\"https://img.markxu.icu/img20260511191040419.png\" alt=\"Google Research\"></p>\n<hr>\n<p>What I have shared above are the sources I followed most often during these four years—in other words, the mysterious “little drawers” I gradually found. They opened up a great many perspectives for me, and they kept my steps from falling too far behind the times. This article is mainly meant to start the conversation: <strong>if you have a treasured source of your own, I would love for you to share it in the comments!</strong></p>\n",
      "summary": "[AI-translated] I have always had a habit of collecting interesting sources of information, so I wanted to write down the few that I remember as high quality, frequently updated, and genuinely fun to read—for my future self, and to share with everyone else.",
      "date_published": "2026-05-11T00:00:00.000Z",
      "image": "https://img.markxu.icu/img06bdb9ed7cc5e8.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/mac-terminal-setup-notes-en",
      "url": "https://markxu.icu/blog/mac-terminal-setup-notes-en",
      "title": "Mac Terminal Setup Notes",
      "content_text": "For exploration and fun, my old terminal had been stuffed with oh-my-zsh and all kinds of plugins. At some point I could no longer remember what I had installed. Worse, the interface looked so awkward that I did not even want to open it. Recently I saw Ghostty on Bilibili, along with a few terminal customization videos that looked surprisingly nice. So this time I decided to start from zero and rebuild my terminal properly. I started using the terminal shortly after I bought my Mac and finished setting up the development environment. Back then I knew almost nothing about customization tools like Raycast or Bartender, let alone the command line habits that macOS users often build over time. Later, my older cousin tried to teach me a few things. When he opened my computer, he exclaimed: \"Why is your computer so primitive?\" That was probably the first time I realized how different macOS habits could be from Windows habits. I have always preferred simple setups on Windows too, but from that moment on I started exploring how to make my Mac environment both prettier and more useful. A Messy Beginning For a long time, my terminal looked close to the default style. I forgot to keep screenshots, but it was basically a gray background with plain text. Following different online tutorials, I installed iTerm2, oh-my-zsh, and a large number of other plugins. The problem was that the whole setup became too messy. I either forgot how to use some plugins, or simply never used them at all. The terminal still worked, but it often required plugin updates, and opening a new terminal window had more than a second of delay. That became annoying. So I decided to rebuild everything and write down the process. It may also serve as a reference for classmates who recently bought a Mac and want a clean terminal setup. Sharpen the Tools First Choosing the right tools is the core of this rebuild. My criteria were simple: Performance first: short startup time and fast rendering. Low maintenance cost: usable out of the box, easy to customize, clear configuration path, and not dependent on plugin stacking. Good looking: good looks are productivity. : In the end, I chose this combination: Ghostty + native Zsh + Starship + Yazi. It covers my current needs almost completely, and I can add smaller tools later if needed. After such a long time, I realized I barely used most of the old oh-my-zsh plugins. Installing oh-my-zsh again does not make much sense for me right now. This article also shares a similar idea: A Guide to the Zsh Line Editor with Examples. Let me briefly introduce the tools. Ghostty: Terminal Emulator The official docs describe it like this: Ghostty is a fast, feature-rich, and cross-platform terminal emulator that uses platform-native UI and GPU acceleration. Similar tools include Terminal.app, iTerm2, Warp, WezTerm, Alacritty, Kitty, and more. Why did I choose Ghostty among so many options? I first noticed it because Boris Cherny recommended it. I tried it and unexpectedly found that it fit me very well. It is compiled from Zig to native machine code, avoiding VM/interpreter overhead and GC pauses. Its character buffer maps directly to GPU memory, skipping CPU round trips. It parses TOML configuration once at startup and does not rely on plugin hot reload, which makes it fast. The configuration is also centralized in one TOML file, so settings are clear and easy to understand. After using it for a while, Ghostty starts much faster than my previous setup, and memory usage is reasonable. It feels close to what I imagine a modern terminal should be. Starship: Prompt Engine Similar tool: oh-my-posh. Starship is written in Rust and distributed as a compiled program. It starts very quickly. As a prompt engine, it replaces the main use case I previously relied on oh-my-zsh for: making the prompt look nice. The old setup had to run a pile of shell scripts every time the terminal opened. Now startup is faster, and the whole experience feels much smoother. Yazi: Async File Manager Yazi is somewhat like macOS Finder, but controlled through the keyboard. Once you understand your machine's file structure, it feels more efficient than constantly typing and . It is also written in Rust, so loading is fast. It supports live previews, which means you do not have to find a file and press Space just to inspect it. It can preview text, images, and many other files directly. It also uses Vim-style keybindings. If you have used Vim before, it feels natural. Even if you have not, you can probably learn the basics quickly, and the help menu is friendly. Reproducible Setup Steps Prerequisites Install Homebrew, the command-line package manager for macOS, and a Nerd Font for terminal icons. Install Homebrew: Update Homebrew: Install the Software Configure Ghostty Open Ghostty and press . A TOML config file should appear. You can copy my config into it: At this point, the terminal should already look much better. Next is Starship. You can also read the official Starship configuration docs. Create the config file: Open it: If you are not familiar with nano or Vim, you can use VS Code and open the file with . My config: If you want another theme, list Starship presets with: It will show options such as , , , and more. Apply a preset like this: Finally, configure Zsh. I recommend appending the following to your existing instead of overwriting it, because your file may already contain Conda, Node, or other environment settings. Verify the Setup If You Use VS Code If you use VS Code or another editor and see strange characters in the integrated terminal, the problem is usually the font. Open settings, search for , and set it to: Now you should have a beautiful and practical terminal. Why This Is Worth the Time The terminal is not just a tool. For developers, it is one of the workbenches we face every day. With vibe coding becoming more common, terminal usage is also becoming more frequent. A well-configured terminal can make the whole workflow feel clearer and more pleasant. Ghostty: Official Site GitHub Starship: Official Site GitHub Yazi: Official Site GitHub This setup was verified with: Ghostty 1.3.1 stable Starship 1.24.2 Yazi 0.3.0 Configuration syntax may differ across versions. If you run into problems, check the official docs for your installed version first.",
      "content_html": "<p>For exploration and fun, my old terminal had been stuffed with oh-my-zsh and all kinds of plugins. At some point I could no longer remember what I had installed. Worse, the interface looked so awkward that I did not even want to open it.</p>\n<p>Recently I saw Ghostty on Bilibili, along with a few terminal customization videos that looked surprisingly nice. So this time I decided to start from zero and rebuild my terminal properly.</p>\n<hr>\n<p>I started using the terminal shortly after I bought my Mac and finished setting up the development environment. Back then I knew almost nothing about customization tools like Raycast or Bartender, let alone the command line habits that macOS users often build over time.</p>\n<p>Later, my older cousin tried to teach me a few things. When he opened my computer, he exclaimed: &quot;Why is your computer so primitive?&quot;</p>\n<p>That was probably the first time I realized how different macOS habits could be from Windows habits. I have always preferred simple setups on Windows too, but from that moment on I started exploring how to make my Mac environment both prettier and more useful.</p>\n<h2>A Messy Beginning</h2>\n<p>For a long time, my terminal looked close to the default style. I forgot to keep screenshots, but it was basically a gray background with plain text. Following different online tutorials, I installed iTerm2, oh-my-zsh, and a large number of other plugins.</p>\n<p>The problem was that the whole setup became too messy. I either forgot how to use some plugins, or simply never used them at all. The terminal still worked, but it often required plugin updates, and opening a new terminal window had more than a second of delay. That became annoying.</p>\n<p>So I decided to rebuild everything and write down the process. It may also serve as a reference for classmates who recently bought a Mac and want a clean terminal setup.</p>\n<h2>Sharpen the Tools First</h2>\n<p>Choosing the right tools is the core of this rebuild. My criteria were simple:</p>\n<ol>\n<li>Performance first: short startup time and fast rendering.</li>\n<li>Low maintenance cost: usable out of the box, easy to customize, clear configuration path, and not dependent on plugin stacking.</li>\n<li>Good looking: good looks are productivity. :)</li>\n</ol>\n<p>In the end, I chose this combination: Ghostty + native Zsh + Starship + Yazi. It covers my current needs almost completely, and I can add smaller tools later if needed.</p>\n<p>After such a long time, I realized I barely used most of the old oh-my-zsh plugins. Installing oh-my-zsh again does not make much sense for me right now. This article also shares a similar idea: <a href=\"https://thevaluable.dev/zsh-install-configure-mouseless/\">A Guide to the Zsh Line Editor with Examples</a>.</p>\n<p>Let me briefly introduce the tools.</p>\n<h3>Ghostty: Terminal Emulator</h3>\n<p><a href=\"https://ghostty.org/docs\">The official docs describe it like this:</a></p>\n<blockquote>\n<p>Ghostty is a fast, feature-rich, and cross-platform terminal emulator that uses platform-native UI and GPU acceleration.</p>\n</blockquote>\n<p>Similar tools include Terminal.app, iTerm2, Warp, WezTerm, Alacritty, Kitty, and more.</p>\n<p>Why did I choose Ghostty among so many options? I first noticed it because <a href=\"https://x.com/bcherny/status/2017742753971769626\">Boris Cherny recommended it</a>. I tried it and unexpectedly found that it fit me very well.</p>\n<p><img src=\"https://img.markxu.icu/imgBorisRecommendGhostty.png\" alt=\"Boris Cherny recommending Ghostty\"></p>\n<ul>\n<li>It is compiled from Zig to native machine code, avoiding VM/interpreter overhead and GC pauses.</li>\n<li>Its character buffer maps directly to GPU memory, skipping CPU round trips.</li>\n<li>It parses TOML configuration once at startup and does not rely on plugin hot reload, which makes it fast. The configuration is also centralized in one TOML file, so settings are clear and easy to understand.</li>\n</ul>\n<p>After using it for a while, Ghostty starts much faster than my previous setup, and memory usage is reasonable. It feels close to what I imagine a modern terminal should be.</p>\n<h3>Starship: Prompt Engine</h3>\n<p>Similar tool: oh-my-posh.</p>\n<p>Starship is written in Rust and distributed as a compiled program. It starts very quickly. As a prompt engine, it replaces the main use case I previously relied on oh-my-zsh for: making the prompt look nice.</p>\n<p>The old setup had to run a pile of shell scripts every time the terminal opened. Now startup is faster, and the whole experience feels much smoother.</p>\n<h3>Yazi: Async File Manager</h3>\n<p>Yazi is somewhat like macOS Finder, but controlled through the keyboard. Once you understand your machine's file structure, it feels more efficient than constantly typing <code>cd</code> and <code>ls</code>.</p>\n<p>It is also written in Rust, so loading is fast. It supports live previews, which means you do not have to find a file and press Space just to inspect it. It can preview text, images, and many other files directly.</p>\n<p><img src=\"https://img.markxu.icu/imgyazi.png\" alt=\"Yazi preview looks really nice\"></p>\n<p>It also uses Vim-style keybindings. If you have used Vim before, it feels natural. Even if you have not, you can probably learn the basics quickly, and the <code>?</code> help menu is friendly.</p>\n<h2>Reproducible Setup Steps</h2>\n<h3>Prerequisites</h3>\n<p>Install Homebrew, the command-line package manager for macOS, and a Nerd Font for terminal icons.</p>\n<p>Install Homebrew:</p>\n<pre><code class=\"language-bash\">/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;\n</code></pre>\n<p>Update Homebrew:</p>\n<pre><code class=\"language-bash\">brew update\n</code></pre>\n<h3>Install the Software</h3>\n<pre><code class=\"language-bash\"># Font\nbrew install --cask font-jetbrains-mono-nerd-font\n\n# Terminal emulator\nbrew install --cask ghostty\n\n# Prompt engine\nbrew install starship\n\n# File manager + preview dependencies\nbrew install yazi ffmpegthumbnailer sevenzip poppler jq fd ripgrep fzf\n\n# Other useful tools\n# Command autosuggestions + syntax highlighting\nbrew install zsh-autosuggestions zsh-syntax-highlighting\n</code></pre>\n<h3>Configure Ghostty</h3>\n<p>Open Ghostty and press <code>Command + ,</code>. A TOML config file should appear. You can copy my config into it:</p>\n<pre><code class=\"language-toml\"># ===== Font settings =====\nfont-family = JetBrainsMono Nerd Font\nfont-size = 14\n\n# ===== Window padding =====\nwindow-padding-x = 12\nwindow-padding-y = 12\n\n# ===== Cursor and scrolling =====\ncursor-style = block\nscrollback-limit = 10000\ncopy-on-select = true\n\n# ===== Theme =====\ntheme = Catppuccin Mocha\n\n# ===== Quick terminal: Cmd + backtick =====\nkeybind = cmd+`=toggle_quick_terminal\n</code></pre>\n<p>At this point, the terminal should already look much better.</p>\n<p>Next is Starship. You can also read the <a href=\"https://starship.rs/config/#:~:text=To%20get%20started%20configuring%20starship%2C\">official Starship configuration docs</a>.</p>\n<p>Create the config file:</p>\n<pre><code class=\"language-bash\">mkdir -p ~/.config &amp;&amp; touch ~/.config/starship.toml\n</code></pre>\n<p>Open it:</p>\n<pre><code class=\"language-bash\">nano ~/.config/starship.toml\n</code></pre>\n<p>If you are not familiar with nano or Vim, you can use VS Code and open the file with <code>code ~/.config/starship.toml</code>.</p>\n<p>My config:</p>\n<pre><code class=\"language-toml\">&quot;$schema&quot; = 'https://starship.rs/config-schema.json'\n\nformat = &quot;&quot;&quot;\n[](red)\\\n$os\\\n$username\\\n[](bg:peach fg:red)\\\n$directory\\\n[](bg:yellow fg:peach)\\\n$git_branch\\\n$git_status\\\n[](fg:yellow bg:green)\\\n$c\\\n$rust\\\n$golang\\\n$nodejs\\\n$php\\\n$java\\\n$kotlin\\\n$haskell\\\n$python\\\n[](fg:green bg:sapphire)\\\n$conda\\\n[](fg:sapphire bg:lavender)\\\n$time\\\n[ ](fg:lavender)\\\n$cmd_duration\\\n$line_break\\\n$character&quot;&quot;&quot;\n\npalette = 'catppuccin_mocha'\n\n[os]\ndisabled = false\nstyle = &quot;bg:red fg:crust&quot;\n\n[os.symbols]\nWindows = &quot;&quot;\nUbuntu = &quot;󰕈&quot;\nSUSE = &quot;&quot;\nRaspbian = &quot;󰐿&quot;\nMint = &quot;󰣭&quot;\nMacos = &quot;󰀵&quot;\nManjaro = &quot;&quot;\nLinux = &quot;󰌽&quot;\nGentoo = &quot;󰣨&quot;\nFedora = &quot;󰣛&quot;\nAlpine = &quot;&quot;\nAmazon = &quot;&quot;\nAndroid = &quot;&quot;\nAOSC = &quot;&quot;\nArch = &quot;󰣇&quot;\nArtix = &quot;󰣇&quot;\nCentOS = &quot;&quot;\nDebian = &quot;󰣚&quot;\nRedhat = &quot;󱄛&quot;\nRedHatEnterprise = &quot;󱄛&quot;\n\n[username]\nshow_always = true\nstyle_user = &quot;bg:red fg:crust&quot;\nstyle_root = &quot;bg:red fg:crust&quot;\nformat = '[ $user]($style)'\n\n[directory]\nstyle = &quot;bg:peach fg:crust&quot;\nformat = &quot;[ $path ]($style)&quot;\ntruncation_length = 3\ntruncation_symbol = &quot;…/&quot;\n\n[directory.substitutions]\n&quot;Documents&quot; = &quot;󰈙 &quot;\n&quot;Downloads&quot; = &quot; &quot;\n&quot;Music&quot; = &quot;󰝚 &quot;\n&quot;Pictures&quot; = &quot; &quot;\n&quot;Developer&quot; = &quot;󰲋 &quot;\n\n[git_branch]\nsymbol = &quot;&quot;\nstyle = &quot;bg:yellow&quot;\nformat = '[[ $symbol $branch ](fg:crust bg:yellow)]($style)'\n\n[git_status]\nstyle = &quot;bg:yellow&quot;\nformat = '[[($all_status$ahead_behind )](fg:crust bg:yellow)]($style)'\n\n[nodejs]\nsymbol = &quot;&quot;\nstyle = &quot;bg:green&quot;\nformat = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'\n\n[c]\nsymbol = &quot; &quot;\nstyle = &quot;bg:green&quot;\nformat = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'\n\n[rust]\nsymbol = &quot;&quot;\nstyle = &quot;bg:green&quot;\nformat = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'\n\n[golang]\nsymbol = &quot;&quot;\nstyle = &quot;bg:green&quot;\nformat = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'\n\n[php]\nsymbol = &quot;&quot;\nstyle = &quot;bg:green&quot;\nformat = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'\n\n[java]\nsymbol = &quot; &quot;\nstyle = &quot;bg:green&quot;\nformat = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'\n\n[kotlin]\nsymbol = &quot;&quot;\nstyle = &quot;bg:green&quot;\nformat = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'\n\n[haskell]\nsymbol = &quot;&quot;\nstyle = &quot;bg:green&quot;\nformat = '[[ $symbol( $version) ](fg:crust bg:green)]($style)'\n\n[python]\nsymbol = &quot;&quot;\nstyle = &quot;bg:green&quot;\nformat = '[[ $symbol( $version)(\\(#$virtualenv\\)) ](fg:crust bg:green)]($style)'\n\n[docker_context]\nsymbol = &quot;&quot;\nstyle = &quot;bg:sapphire&quot;\nformat = '[[ $symbol( $context) ](fg:crust bg:sapphire)]($style)'\n\n[conda]\nsymbol = &quot;  &quot;\nstyle = &quot;fg:crust bg:sapphire&quot;\nformat = '[$symbol$environment ]($style)'\nignore_base = false\n\n[time]\ndisabled = false\ntime_format = &quot;%R&quot;\nstyle = &quot;bg:lavender&quot;\nformat = '[[  $time ](fg:crust bg:lavender)]($style)'\n\n[line_break]\ndisabled = true\n\n[character]\ndisabled = false\nsuccess_symbol = '[❯](bold fg:green)'\nerror_symbol = '[❯](bold fg:red)'\nvimcmd_symbol = '[❮](bold fg:green)'\nvimcmd_replace_one_symbol = '[❮](bold fg:lavender)'\nvimcmd_replace_symbol = '[❮](bold fg:lavender)'\nvimcmd_visual_symbol = '[❮](bold fg:yellow)'\n\n[cmd_duration]\nshow_milliseconds = true\nformat = &quot; in $duration &quot;\nstyle = &quot;bg:lavender&quot;\ndisabled = false\nshow_notifications = true\nmin_time_to_notify = 45000\n\n[palettes.catppuccin_mocha]\nrosewater = &quot;#f5e0dc&quot;\nflamingo = &quot;#f2cdcd&quot;\npink = &quot;#f5c2e7&quot;\nmauve = &quot;#cba6f7&quot;\nred = &quot;#f38ba8&quot;\nmaroon = &quot;#eba0ac&quot;\npeach = &quot;#fab387&quot;\nyellow = &quot;#f9e2af&quot;\ngreen = &quot;#a6e3a1&quot;\nteal = &quot;#94e2d5&quot;\nsky = &quot;#89dceb&quot;\nsapphire = &quot;#74c7ec&quot;\nblue = &quot;#89b4fa&quot;\nlavender = &quot;#b4befe&quot;\ntext = &quot;#cdd6f4&quot;\nsubtext1 = &quot;#bac2de&quot;\nsubtext0 = &quot;#a6adc8&quot;\noverlay2 = &quot;#9399b2&quot;\noverlay1 = &quot;#7f849c&quot;\noverlay0 = &quot;#6c7086&quot;\nsurface2 = &quot;#585b70&quot;\nsurface1 = &quot;#45475a&quot;\nsurface0 = &quot;#313244&quot;\nbase = &quot;#1e1e2e&quot;\nmantle = &quot;#181825&quot;\ncrust = &quot;#11111b&quot;\n</code></pre>\n<p>If you want another theme, list Starship presets with:</p>\n<pre><code class=\"language-bash\">starship preset --list\n</code></pre>\n<p>It will show options such as <code>bracketed-segments</code>, <code>catppuccin-powerline</code>, <code>tokyo-night</code>, and more.</p>\n<p>Apply a preset like this:</p>\n<pre><code class=\"language-bash\">starship preset tokyo-night &gt; ~/.config/starship.toml\n</code></pre>\n<p>Finally, configure Zsh. I recommend appending the following to your existing <code>~/.zshrc</code> instead of overwriting it, because your file may already contain Conda, Node, or other environment settings.</p>\n<pre><code class=\"language-bash\">nano ~/.zshrc\n</code></pre>\n<pre><code class=\"language-bash\"># ===== Homebrew: macOS package manager =====\nexport PATH=&quot;/opt/homebrew/bin:$PATH&quot;\n\n# ===== Starship =====\neval &quot;$(starship init zsh)&quot;\n\n# ===== Completion =====\nautoload -Uz compinit &amp;&amp; compinit\n\n# ===== Yazi terminal file manager =====\nfunction y() {\n  local tmp=&quot;$(mktemp -t &quot;yazi-cwd.XXXXXX&quot;)&quot; cwd\n  yazi &quot;$@&quot; --cwd-file=&quot;$tmp&quot;\n  if cwd=&quot;$(command cat -- &quot;$tmp&quot;)&quot; &amp;&amp; [ -n &quot;$cwd&quot; ] &amp;&amp; [ &quot;$cwd&quot; != &quot;$PWD&quot; ]; then\n    builtin cd -- &quot;$cwd&quot;\n  fi\n  rm -f -- &quot;$tmp&quot;\n}\n\n# yy as a shortcut for yazi\nalias yy=&quot;y&quot;\n\n# ===== Zsh Autosuggestions =====\nsource /opt/homebrew/share/zsh-autosuggestions/zsh-autosuggestions.zsh\n\n# ===== Zsh Syntax Highlighting =====\nsource /opt/homebrew/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh\n</code></pre>\n<h3>Verify the Setup</h3>\n<pre><code class=\"language-bash\">exec zsh\n</code></pre>\n<h3>If You Use VS Code</h3>\n<p>If you use VS Code or another editor and see strange characters in the integrated terminal, the problem is usually the font. Open settings, search for <code>terminal font</code>, and set it to:</p>\n<pre><code class=\"language-text\">JetBrainsMono Nerd Font\n</code></pre>\n<p>Now you should have a beautiful and practical terminal.</p>\n<h2>Why This Is Worth the Time</h2>\n<p><img src=\"https://img.markxu.icu/imgfinalshowTerminal.png\" alt=\"Final terminal result\"></p>\n<p>The terminal is not just a tool. For developers, it is one of the workbenches we face every day. With vibe coding becoming more common, terminal usage is also becoming more frequent. A well-configured terminal can make the whole workflow feel clearer and more pleasant.</p>\n<ul>\n<li>Ghostty: <a href=\"https://ghostty.org\">Official Site</a> | <a href=\"https://github.com/ghostty-org/ghostty\">GitHub</a></li>\n<li>Starship: <a href=\"https://starship.rs\">Official Site</a> | <a href=\"https://github.com/starship/starship\">GitHub</a></li>\n<li>Yazi: <a href=\"https://yazi-rs.github.io\">Official Site</a> | <a href=\"https://github.com/sxyazi/yazi\">GitHub</a></li>\n</ul>\n<blockquote>\n<p>This setup was verified with:</p>\n<ul>\n<li>Ghostty 1.3.1 (stable)</li>\n<li>Starship 1.24.2</li>\n<li>Yazi 0.3.0</li>\n</ul>\n<p>Configuration syntax may differ across versions. If you run into problems, check the official docs for your installed version first.</p>\n</blockquote>\n",
      "summary": "My old terminal was packed with oh-my-zsh and plugins I could barely remember, and it looked ugly enough that I did not even want to use it. So I decided to rebuild my terminal from scratch.",
      "date_published": "2026-04-07T00:00:00.000Z",
      "image": "https://img.markxu.icu/img4d29eab0f1dc8.jpeg",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/prompt-only-open-source-en",
      "url": "https://markxu.icu/blog/prompt-only-open-source-en",
      "title": "When Open Source Is Only a Prompt: I Found a Ridiculous New Way to Open Source",
      "content_text": "Recently, while wandering around GitHub, I came across a project that felt both shocking and funny: voice-input-src . It is a voice input tool built for Mac. When you opened the repository, you will not find any traditional Swift source code. Instead, the repo contains only an extremely detailed prompt written for AI, specifically Claude, with both Chinese and English instructions. That made me curious: Can this kind of open source actually work? Can users really reproduce a usable app from this? Since I happen to have a MacBook, I decided to try reproducing it on my own machine and see whether this open-source model was feasible. Project Breakdown: Is This Really \"Source Code\"? An Extremely Minimal Repository The project has a README.md file containing the required prompt text, and nothing else . I have to admit, this kind of open-source project is very easy to understand. Prompt as Source Code Traditional open source is \"humans read code, compilers execute code.\" This project is more like \"humans read the prompt, AI executes the prompt.\" The author, yetone, even created a separate voice-input-dist repository to store the app source code generated by AI. This cleanly separates the \"source\" from the \"artifact,\" and it also makes comparison easier for users. It is a small but interesting design choice. What Is Hidden Inside the Prompt? Since the prompt is basically the only thing we can inspect in this repository, I analyzed it before trying to reproduce the project. This is not a casual requirement document written by someone who knows nothing about programming. It is a document written by someone who clearly understands macOS APIs and implementation details. Module Implementation Details ------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------- Speech Recognition Uses Apple's Speech Recognition framework, supports streaming transcription, defaults to Chinese. Global Hotkey Listens to the Fn key through global event monitoring with CGEvent tap, while suppressing the default behavior to prevent the emoji panel from opening. Floating UI Borderless NSPanel, visual blur effects, and real-time waveform animation driven by audio RMS rather than fake animation. Input Method Compatibility Detects the current input method, switches to the ABC keyboard before pasting to avoid Cmd+V interception, then restores the original input method. LLM Post-processing Calls an OpenAI-compatible API for correction, only fixing obvious mistakes such as \"配森\" - \"Python\". From these details, it is clear that the author knew exactly which frameworks to call, which components to use, what conflicts might appear later, and how those conflicts should be handled. Because the author understands the technical background and implementation difficulties well enough, he could write such a precise project-level prompt. One GitHub issue even commented: This thing is the real prompt development tutorial. My Reproduction Attempt on My Mac To verify whether this kind of open source is actually effective, and also to experience this slightly wild form of prompt engineering, I ran a complete \"from prompt to app\" experiment on my Mac. Environment Mac : My 2021 M1 Pro MacBook, running Tahoe 26.3.1. AI tool : Codex, using GPT 5.4 Extra High. I wanted to see whether a model different from the author's Claude setup could still succeed. Here is my device configuration: Steps Feed the prompt : Send the full prompt from the repository to Codex. Functional acceptance testing : Definitely not because I do not know Swift well enough to read all the generated code. Checklist: ✅ Can the Fn key reliably start recording without opening the emoji panel? ✅ Does the floating waveform change in real time with volume? ✅ How accurate is Chinese recognition? Does LLM correction work? Actual Implementation On my machine, the app did not build successfully on the first attempt. The first full build took about 23 minutes and cost $1.3994 USD . After that, two small runtime/build issues appeared, so I asked Codex to fix them twice. Including fixes, the total cost was $1.9734 USD . Here is Codex's summary of the two fixes: These two fixes essentially solved one compile-time problem and one runtime crash. The first fix was in line 18. The original code used an expression like , relying on Swift to infer the type of . In some cases, this optional chain plus empty-array fallback made type inference unstable, eventually causing an error at . The fix was simple: explicitly write as . This did not change any business logic; it only replaced \"let the compiler guess\" with \"tell the compiler the type directly.\" The second fix was in line 93. The original code set . The issue is that and conflict semantically. One means \"join all Spaces,\" while the other means \"move with the currently active Space.\" AppKit is sensitive to this kind of window behavior combination, and it can trigger an assertion that ends as an abort signal crash. The fix changed it to a more stable combination: keep , , and , add , and remove . The floating window can still appear in the current Space and alongside full-screen apps, but it no longer crashes during initialization. Result You can probably guess it: this entire article was dictated using the generated app. The effect is actually pretty good. Characters appear quickly, faster and more immediate than Apple's built-in dictation, and invoking it is more convenient. Here is what it looked like: As for shortcomings, you can see that the text was not centered nicely at first, which felt a bit uncomfortable. I later prompted Codex to fix that too. Overall, my version still did not look as polished as the author s Claude-generated demo, but at least it worked. Is This the Future of Open Source, or Just a Joke? The Positive View: Extreme DRY Very low maintenance cost : The author does not need to care about Xcode version changes or Swift syntax evolution. He only provides a requirements document. Reproducibility : As long as the AI model is capable enough, anyone can generate a functionally similar app at any time. Easy sharing : This way of sharing is direct. Compared with publishing a huge codebase vibe-coded from scratch, a carefully written prompt may actually be cleaner in some cases. The Slightly Negative View Uncontrollability : Generated code quality depends heavily on the AI model. It is hard to guarantee identical results every time. My Codex version was usable, but visually not as good as the author's demo. Operational friction : To generate the code, users still need an AI environment and enough budget. For small projects this is fine, but larger projects can become expensive. Users also have to wait for generation, which is less convenient than downloading a release from the author. Overall, I do not think this experiment is a bad thing. On the contrary, the author's creative repo structure gave me a small shock. Maybe this should be understood less as a concrete project distribution method and more as an excellent AI tutorial. To write a prompt like the author's, you still need solid technical knowledge and a clear understanding of the target platform. At least for me, I definitely could not write such a perfect prompt in one shot. If you have some spare time, I recommend trying it yourself.",
      "content_html": "<p>Recently, while wandering around GitHub, I came across a project that felt both shocking and funny: <strong><a href=\"https://github.com/yetone/voice-input-src?tab=readme-ov-file\">voice-input-src</a></strong>. It is a voice input tool built for Mac.</p>\n<p>When you opened the repository, you will not find any traditional Swift source code. Instead, the repo contains only an extremely detailed prompt written for AI, specifically Claude, with both Chinese and English instructions.</p>\n<p>That made me curious: <strong>Can this kind of open source actually work? Can users really reproduce a usable app from this?</strong> Since I happen to have a MacBook, I decided to try reproducing it on my own machine and see whether this open-source model was feasible.</p>\n<hr>\n<h2>Project Breakdown: Is This Really &quot;Source Code&quot;?</h2>\n<h3>1. An Extremely Minimal Repository</h3>\n<p>The project has a <a href=\"http://README.md\">README.md</a> file containing the required <strong>prompt text, and nothing else</strong>.</p>\n<p><img src=\"https://img.markxu.icu/imgvoiceInputRepo.png\" alt=\"Project repository\"></p>\n<p>I have to admit, this kind of open-source project is very easy to understand.</p>\n<h3>2. Prompt as Source Code</h3>\n<p>Traditional open source is &quot;humans read code, compilers execute code.&quot; This project is more like &quot;humans read the prompt, AI executes the prompt.&quot;</p>\n<p>The author, yetone, even created a separate <a href=\"https://github.com/yetone/voice-input-dist\">voice-input-dist repository</a> to store the app source code generated by AI. This cleanly separates the &quot;source&quot; from the &quot;artifact,&quot; and it also makes comparison easier for users.</p>\n<p>It is a small but interesting design choice.</p>\n<h2>What Is Hidden Inside the Prompt?</h2>\n<p>Since the prompt is basically the only thing we can inspect in this repository, I analyzed it before trying to reproduce the project.</p>\n<p>This is not a casual requirement document written by someone who knows nothing about programming. It is a document written by someone who clearly understands macOS APIs and implementation details.</p>\n<table>\n<thead>\n<tr>\n<th>Module</th>\n<th>Implementation Details</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><strong>Speech Recognition</strong></td>\n<td>Uses Apple's Speech Recognition framework, supports streaming transcription, defaults to Chinese.</td>\n</tr>\n<tr>\n<td><strong>Global Hotkey</strong></td>\n<td>Listens to the Fn key through global event monitoring with CGEvent tap, while suppressing the default behavior to prevent the emoji panel from opening.</td>\n</tr>\n<tr>\n<td><strong>Floating UI</strong></td>\n<td>Borderless NSPanel, visual blur effects, and real-time waveform animation driven by audio RMS rather than fake animation.</td>\n</tr>\n<tr>\n<td><strong>Input Method Compatibility</strong></td>\n<td>Detects the current input method, switches to the ABC keyboard before pasting to avoid Cmd+V interception, then restores the original input method.</td>\n</tr>\n<tr>\n<td><strong>LLM Post-processing</strong></td>\n<td>Calls an OpenAI-compatible API for correction, only fixing obvious mistakes such as &quot;配森&quot; -&gt; &quot;Python&quot;.</td>\n</tr>\n</tbody>\n</table>\n<p>From these details, it is clear that the author knew exactly which frameworks to call, which components to use, what conflicts might appear later, and how those conflicts should be handled.</p>\n<p>Because the author understands the technical background and implementation difficulties well enough, he could write such a precise project-level prompt. One GitHub issue even commented:</p>\n<blockquote>\n<p>This thing is the real prompt development tutorial.</p>\n</blockquote>\n<h2>My Reproduction Attempt on My Mac</h2>\n<p>To verify whether this kind of open source is actually effective, and also to experience this slightly wild form of prompt engineering, I ran a complete &quot;from prompt to app&quot; experiment on my Mac.</p>\n<h3>1. Environment</h3>\n<ul>\n<li><strong>Mac</strong>: My 2021 M1 Pro MacBook, running Tahoe 26.3.1.</li>\n<li><strong>AI tool</strong>: Codex, using GPT 5.4 Extra High. I wanted to see whether a model different from the author's Claude setup could still succeed.</li>\n</ul>\n<p>Here is my device configuration:</p>\n<p><img src=\"https://img.markxu.icu/imgComputerConfig.png\" alt=\"My Mac configuration\"></p>\n<h3>2. Steps</h3>\n<ol>\n<li><strong>Feed the prompt</strong>: Send the full prompt from the repository to Codex.</li>\n<li><strong>Functional acceptance testing</strong>: Definitely not because I do not know Swift well enough to read all the generated code.</li>\n</ol>\n<p>Checklist:</p>\n<p>✅ Can the Fn key reliably start recording without opening the emoji panel?<br>\n✅ Does the floating waveform change in real time with volume?<br>\n✅ How accurate is Chinese recognition? Does LLM correction work?</p>\n<p><img src=\"https://img.markxu.icu/imgSendItToCodex.png\" alt=\"Prompt sent to Codex\"></p>\n<h3>3. Actual Implementation</h3>\n<p>On my machine, the app did not build successfully on the first attempt. The first full build took about <strong>23 minutes</strong> and cost <strong>$1.3994 USD</strong>. After that, two small runtime/build issues appeared, so I asked Codex to fix them twice. Including fixes, the total cost was <strong>$1.9734 USD</strong>.</p>\n<p>Here is Codex's summary of the two fixes:</p>\n<blockquote>\n<p>These two fixes essentially solved one compile-time problem and one runtime crash.</p>\n<p>The first fix was in <code>PasteboardInjector.swift</code> line 18. The original code used an expression like <code>pasteboard.pasteboardItems?.map { ... } ?? []</code>, relying on Swift to infer the type of <code>items</code>. In some cases, this optional chain plus empty-array fallback made type inference unstable, eventually causing an error at <code>return PasteboardBackup(items: items)</code>. The fix was simple: explicitly write <code>items</code> as <code>[Item]</code>. This did not change any business logic; it only replaced &quot;let the compiler guess&quot; with &quot;tell the compiler the type directly.&quot;</p>\n<p>The second fix was in <code>FloatingPanelController.swift</code> line 93. The original code set <code>collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary, .moveToActiveSpace, .ignoresCycle]</code>. The issue is that <code>.canJoinAllSpaces</code> and <code>.moveToActiveSpace</code> conflict semantically. One means &quot;join all Spaces,&quot; while the other means &quot;move with the currently active Space.&quot; AppKit is sensitive to this kind of window behavior combination, and it can trigger an assertion that ends as an abort signal crash. The fix changed it to a more stable combination: keep <code>.moveToActiveSpace</code>, <code>.fullScreenAuxiliary</code>, and <code>.ignoresCycle</code>, add <code>.transient</code>, and remove <code>.canJoinAllSpaces</code>. The floating window can still appear in the current Space and alongside full-screen apps, but it no longer crashes during initialization.</p>\n</blockquote>\n<h2>Result</h2>\n<p>You can probably guess it: this entire article was dictated using the generated app.</p>\n<p>The effect is actually pretty good. Characters appear quickly, faster and more immediate than Apple's built-in dictation, and invoking it is more convenient.</p>\n<p>Here is what it looked like:</p>\n<p><img src=\"https://img.markxu.icu/imgAppEffect.png\" alt=\"My result\"></p>\n<p>As for shortcomings, you can see that the text was not centered nicely at first, which felt a bit uncomfortable. I later prompted Codex to fix that too. Overall, my version still did not look as polished as <a href=\"https://private-user-images.githubusercontent.com/1206493/570996776-3228f78a-f035-447d-98ef-8826798a122c.mp4?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NzU0ODU4NTksIm5iZiI6MTc3NTQ4NTU1OSwicGF0aCI6Ii8xMjA2NDkzLzU3MDk5Njc3Ni0zMjI4Zjc4YS1mMDM1LTQ0N2QtOThlZi04ODI2Nzk4YTEyMmMubXA0P1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI2MDQwNiUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNjA0MDZUMTQyNTU5WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9OWY5YTBiZjNmNDNlYmVjMThjZTkyYmVlMzhhNWE5ZGVkMDUzYjE2NTA0Y2M0NmUxYWI4ODg0NmYxMmZjMDljMSZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QifQ.hn6EDW1eCK1AOOo7I77njFUspVoIxag2_bk1BwJxfQE\">the author's Claude-generated demo</a>, but at least it worked.</p>\n<h2>Is This the Future of Open Source, or Just a Joke?</h2>\n<h3>1. The Positive View: Extreme DRY</h3>\n<ul>\n<li><strong>Very low maintenance cost</strong>: The author does not need to care about Xcode version changes or Swift syntax evolution. He only provides a requirements document.</li>\n<li><strong>Reproducibility</strong>: As long as the AI model is capable enough, anyone can generate a functionally similar app at any time.</li>\n<li><strong>Easy sharing</strong>: This way of sharing is direct. Compared with publishing a huge codebase vibe-coded from scratch, a carefully written prompt may actually be cleaner in some cases.</li>\n</ul>\n<h3>2. The Slightly Negative View</h3>\n<ul>\n<li><strong>Uncontrollability</strong>: Generated code quality depends heavily on the AI model. It is hard to guarantee identical results every time. My Codex version was usable, but visually not as good as the author's demo.</li>\n<li><strong>Operational friction</strong>: To generate the code, users still need an AI environment and enough budget. For small projects this is fine, but larger projects can become expensive. Users also have to wait for generation, which is less convenient than downloading a release from the author.</li>\n</ul>\n<p><img src=\"https://img.markxu.icu/imgContrastBetweenMineandAuthor.png\" alt=\"Comparison between my structure and the author's\"></p>\n<p>Overall, I do not think this experiment is a bad thing. On the contrary, the author's creative repo structure gave me a small shock.</p>\n<p>Maybe this should be understood less as a concrete project distribution method and more as an excellent AI tutorial. To write a prompt like the author's, you still need solid technical knowledge and a clear understanding of the target platform.</p>\n<p>At least for me, I definitely could not write such a perfect prompt in one shot.</p>\n<p>If you have some spare time, I recommend trying it yourself.</p>\n",
      "summary": "I recently found a GitHub project called voice-input-src, a voice input app for Mac. The surprising part is that the repository contains only one detailed prompt for AI.",
      "date_published": "2026-04-05T00:00:00.000Z",
      "date_modified": "2026-04-06T00:00:00.000Z",
      "image": "https://img.markxu.icu/imgvoiceinput.jpeg",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/first-blog-post-en",
      "url": "https://markxu.icu/blog/first-blog-post-en",
      "title": "My First Blog",
      "content_text": "My website has been online for nearly 30 days, and only now do I have this first blog post in the true sense. I actually wanted to wait until the website in my initial plan was mostly finished before I started writing. It is also a way to record the beginning and journey of this little site. So, let's begin. Why did I build this website? When it comes to personal websites, or personal blogs, I have been exposed to the concept since childhood. Many books I read were essentially collections of personal online blog posts and essays. One example I really liked was 万万没想到 by Weigang Wan, which was compiled from his online journals. I used to think writing blogs was something really cool, but never have I imagined that one day I would also want to write and keep track of my own life. The shift from reading others' blogs to writing my own was actually accidental. One day I was scrolling 知乎 and saw a question: \"What should you do while you're still young?\" It was the typical kind of question for asking advice for young people. I read several answers and one of them still sticks with me to this day. Its core idea was: \"While you're young, leave yourself more beautiful memories. Even when life gets hard later, at least those memories can hold you up.\" That feels true. When I look back at the childhood photos my parents took for me, I still feel warm inside. I don't actually remember much from those years, but by watching old videos with my parents time and time again, and hearing them tell those stories time and time again, made even memories that never truly should be existed in my mind become vivid. Maybe that's why many classmates are surprised I can remember things from so long ago. During my university years, I also went through a vast transition: from a life of fixed study-and-exam routines primary school to high school , to arranging my own time and life. I joined many team-based competitions and made many choices that did not follow the mainstream. There were months when I stayed up all night every day just studying. There were also carefree days when I could just pack up and travel on impulse. I met friends and classmates from different cities and different age groups in society. I also changed from someone who ignored what was happening outside, to someone who started paying attention to social issues and current events. After all this, it was hard not to want to document my life and transformation. Ideas stop at imagination; execution depends on things actually happening. During my 考研 preparation period, I came across a personal website with many friend links. I started visiting different interesting personal sites. Many of them were from Chinese college students recording their study notes and daily life. They were not all \"guru-level\" blogs full of big insights though some certainly were . But still, through those ordinary records, I felt like I had met new friends with people I never met in person. Combined with the thoughts I had already been carrying, this finally pushed me to decide to build my own site. So after the exam, I began the journey. How was the website built? As everyone knows, websites are essentially frontend work. AI is already very strong in this area. It not only makes building easier, but can also produce interfaces with good interaction and visual quality. If someone still insists on purely old-school programming and types everything from scratch today, it's hard to say whether people would admire it or laugh at it. At the same time, there are many open-source blog templates for example, there are lots of beautiful templates on Hexo . If you directly use one, you can avoid reinventing the wheel. It's fast and practical, and often prettier than a hand-drawn draft made by one person. So from the early stage, when I was building this site, I kept thinking about what's convenient, especially how to make AI convenient to use. Across the whole internet frontend ecosystem, React has the largest share. That also means AI has seen the most React-related code, which makes future AI-assisted modifications easier. Also, when I was a sophomore, my older cousin asked me to learn some React, but I never got real practice. I always wanted to try it properly. Overall, React really suited me well: someone who wanted to build from scratch, experience the full React development process, and create a website with personal ideas. Image source: eleopardsolutions So I chose as the core stack of my website. After I finished the overall framework and structure myself, it was time for AI to shine. As the site improved bit by bit and animations were adjusted continuously, the look I had in mind gradually became real you can see more details on our Timeline page . Later, with free hosting and image services from Vercel and Cloudflare, our website finally went live, and in a cost-effective way. There are already lots of tutorials online for the exact process, so I won't repeat them here. What's next？ I am already quite satisfied with this little site, but the content is still very empty. Right now it mostly has a shell. So moving forward, I will document my life, especially my university life. If someone reads this before entering college, maybe they can learn from my mistakes in advance, and maybe pick up a bit of useful experience from me too. Haha. To expand this site, I will also keep working in my spare time on two additional projects. Maybe I can make a project collection page? Or perhaps share some great papers and blog posts I have read. Who knows? Just let everything happen!",
      "content_html": "<p>My website has been online for nearly 30 days, and only now do I have this first blog post in the true sense. I actually wanted to wait until the website in my initial plan was mostly finished before I started writing. It is also a way to record the beginning and journey of this little site. So, let's begin.</p>\n<h2>Why did I build this website?</h2>\n<p>When it comes to personal websites, or personal blogs, I have been exposed to the concept since childhood. Many books I read were essentially collections of personal online blog posts and essays. One example I really liked was <em>万万没想到</em> by Weigang Wan, which was compiled from his online journals.</p>\n<p>I used to think writing blogs was something really cool, but never have I imagined that one day I would also want to write and keep track of my own life.</p>\n<p>The shift from reading others' blogs to writing my own was actually accidental. One day I was scrolling 知乎 and saw a question: &quot;What should you do while you're still young?&quot; It was the typical kind of question for asking advice for young people. I read several answers and one of them still sticks with me to this day. Its core idea was:</p>\n<p><strong>&quot;While you're young, leave yourself more beautiful memories. Even when life gets hard later, at least those memories can hold you up.&quot;</strong></p>\n<p>That feels true. When I look back at the <strong>childhood photos</strong> my parents took for me, I still feel warm inside. I don't actually remember much from those years, but by watching old videos with my parents time and time again, and hearing them tell those stories time and time again, made even memories that never truly should be existed in my mind become vivid. Maybe that's why many classmates are surprised I can remember things from so long ago.</p>\n<div class=\"img-grid-2\">\n  <img src=\"https://img.markxu.icu/img2012turtle.png\" alt=\"The little turtle we raised in 2012\" />\n  <img src=\"https://img.markxu.icu/img07cartoon.png\" alt=\"Watching Digimon with my older brother at grandpa's house in 2007\" />\n</div>\n<p>During my university years, I also went through a vast transition: from a life of fixed study-and-exam routines (primary school to high school), to arranging my own time and life. I joined many team-based competitions and made many choices that did not follow the mainstream. There were months when I stayed up all night every day just studying. There were also carefree days when I could just pack up and travel on impulse.</p>\n<p>I met friends and classmates from different cities and different age groups in society. I also changed from someone who ignored what was happening outside, to someone who started paying attention to social issues and current events.</p>\n<p>After all this, it was hard not to want to document my life and transformation.</p>\n<h3><strong>Ideas stop at imagination; execution depends on things actually happening.</strong></h3>\n<p>During my 考研 preparation period, I came across a personal website with many friend links. I started visiting different interesting personal sites. Many of them were from Chinese college students recording their study notes and daily life. They were not all &quot;guru-level&quot; blogs full of big insights (though some certainly were). But still, through those ordinary records, I felt like I had met new friends with people I never met in person.</p>\n<p>Combined with the thoughts I had already been carrying, this finally pushed me to decide to build my own site. So after the exam, I began the journey.</p>\n<h2>How was the website built?</h2>\n<p>As everyone knows, websites are essentially frontend work. AI is already very strong in this area. It not only makes building easier, but can also produce interfaces with good interaction and visual quality.</p>\n<p>If someone still insists on purely old-school programming and types everything from scratch today, it's hard to say whether people would admire it or laugh at it.</p>\n<p>At the same time, there are many open-source blog templates (for example, there are lots of beautiful templates on <a href=\"https://hexo.io/zh-cn/\">Hexo</a>). If you directly use one, you can avoid reinventing the wheel. It's fast and practical, and often prettier than a hand-drawn draft made by one person.</p>\n<p>So from the early stage, when I was building this site, I kept thinking about what's convenient, especially how to make AI convenient to use. Across the whole internet frontend ecosystem, <a href=\"https://gist.github.com/tkrotoff/b1caa4c3a185629299ec234d2314e190?permalink_comment_id=5072574\">React has the largest share</a>. That also means AI has seen the most React-related code, which makes future AI-assisted modifications easier. Also, when I was a sophomore, my older cousin asked me to learn some React, but I never got real practice. I always wanted to try it properly.</p>\n<p>Overall, React really suited me well: someone who wanted to build from scratch, experience the full React development process, and create a website with personal ideas.</p>\n<p><img src=\"https://img.markxu.icu/imgmarketshareofReactVueAngular.png\" alt=\"React market share\"></p>\n<p>Image source: <a href=\"https://eleopardsolutions.com/react-vs-vue-vs-angular-key-differences/\">eleopardsolutions</a></p>\n<p>So I chose <code>React + Vite + Tailwind CSS</code> as the core stack of my website. After I finished the overall framework and structure myself, it was time for AI to shine.</p>\n<p>As the site improved bit by bit and animations were adjusted continuously, the look I had in mind gradually became real (you can see more details on our <a href=\"https://markxu.icu/timeline\">Timeline page</a>). Later, with free hosting and image services from Vercel and Cloudflare, our website finally went live, and in a cost-effective way. (There are already lots of tutorials online for the exact process, so I won't repeat them here.)</p>\n<h2>What's next？</h2>\n<p>I am already quite satisfied with this little site, but the content is still very empty. Right now it mostly has a shell. So moving forward, I will document my life, especially my university life.</p>\n<p>If someone reads this before entering college, maybe they can learn from my mistakes in advance, and maybe pick up a bit of useful experience from me too. Haha.</p>\n<p>To expand this site, I will also keep working in my spare time on two additional projects. Maybe I can make a project collection page? Or perhaps share some great papers and blog posts I have read.</p>\n<p><strong>Who knows? Just let everything happen!</strong></p>\n",
      "summary": "This is the literal first blog post on my personal site, sharing my thoughts and experiences.",
      "date_published": "2026-03-24T00:00:00.000Z",
      "date_modified": "2026-03-26T00:00:00.000Z",
      "image": "https://img.markxu.icu/imgmarketshareofReactVueAngular.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/guangzhou-trip-notes-en",
      "url": "https://markxu.icu/blog/guangzhou-trip-notes-en",
      "title": "Guangzhou Travel Notes",
      "content_text": "Itinerary: 3/22: Train at 8:50 AM - Arrive in Guangzhou at 12:30 PM - Dim sum at Dian Du De - Chen Clan Ancestral Hall - Liwan Lake - Yongqing Fang - Shangxiajiu - Shamian Island - Canton Customs - Sacred Heart Cathedral - Beijing Road - Guesthouse - Dafo Temple 3/23: Early morning meetup with a new friend at Sun Yat-sen Memorial Hall - Walk to Nanyue King Museum - Bamboo-pressed noodles - Yuexiu Park - Dongshankou - Taikoo Hui - Gym - Saizeriya - Huacheng Square - Canton Tower - Haixinsha - Guesthouse 3/24: Guangzhou Museum of Art - Sun Yat-sen University - APM Line - Xihua Road - Train station Last Friday, our database teacher took a day off, which unexpectedly gave us free time and turned into a three-day mini holiday. Taking this chance, my close buddy and I planned a trip to Guangzhou, and we also wanted to meet his childhood friend there. So we set off early on Friday morning. My ticket was to Guangzhou East Station. Normally, stations named \"East\" or \"West\" are newer and larger. But this one felt the opposite. Guangzhou must have developed so early that even the later-built stations already feel old. The moment we walked out, we were in a bustling district: shiny commercial streets mixed with aging residential blocks, with a few thick, old, lively trees on both sides of the road casting shade over the wide sidewalks. We gradually tuned into the rhythm of the city. It took us a while, but eventually we caught it: long history, rich daily life, youth, and vitality. We quickly decided to go for morning tea. My buddy asked several classmates, and they all recommended Dian Du De. It was a little pricey, but the quality and service were excellent. We went to the branch closest to the station there were a few izakayas across the street; stepping in felt like entering a Japanese drama, totally different from the lively Chinese street outside , and started feasting. The two of us spent 250 RMB and basically tried a whole range of dishes. After the meal we took an elevator, then realized it only went up. Buttons from floors 4 to 11 were all lit and couldn't be canceled. So my friend and I, together with an elderly man, just kept riding floor by floor and eventually came back down to the ground from what had originally been the second floor. The old man chatted in Cantonese, different from what you hear in Hong Kong movies, and complained about a lot of things. I only clearly understood one phrase: \"damn it.\" Just like Lu Xun wrote, that phrase is nationally unified. After that we quickly checked off all the recommended spots from Xiaohongshu. During the process, we slowly resonated with Guangzhou itself: we watched elderly bands playing in riverside parks along the Pearl River, seniors doing partner dancing, little opera stages in parks, filmed with foreign friends, saw young influencers posing at check-in spots, watched young office workers hurrying by, middle-aged people exercising steadily, elementary school kids choosing ice cream after class, parents picking up children after work, and children still playing before their parents arrived. Day one ended quickly. We were exhausted and looked forward to deep sleep that night. Three-Person Walkabout Early on the second day, we skipped refined breakfast shops and went to a roadside stand at a quiet crossroads. We had rice noodles for 5 RMB, and they were surprisingly great 🎉. We met my buddy's childhood friend at Sun Yat-sen Memorial Hall. At first we were not familiar, just acquainted, and didn't talk much. After visiting the memorial hall together, we passed a Taoist temple on our way to the Nanyue King Museum. Somewhere along that walk, the awkwardness faded and our conversation gradually opened up. After the Nanyue King Museum, we went to a noodle shop that our friend's childhood friend said was good. Unexpectedly, the bamboo-pressed noodles were made with fish-bone broth. I didn't eat a single bite I don't like seafood 😟 , and we continued on. After wandering for a long time, none of us knew what to do next, so I suggested going to the gym our friend's childhood friend is incredibly fit! . We found a cheap gym, but it required reservation and wouldn't let us train on the spot. So we switched to a better one. The service was excellent, and the equipment was new and great. I learned a lot from him. I updated my training plan, clarified my training goals, learned many new movements and details, and also checked whether my existing form was standard. I benefited a lot. Then we went to Canton Tower. Another day ended in complete exhaustion. Train Delay We got up early and walked again to the Guangzhou Museum of Art, because we had forgotten to reserve Guangdong Provincial Museum and couldn't get in. After a quick visit, we arrived at the gate of Sun Yat-sen University. I had already contacted another close friend studying there. He told us about a side gate where we could enter directly without prior reservation since we failed to reserve in advance . He also kindly told us which places on campus were worth visiting. Seeing him study in such a beautiful place made me really envious. I started imagining that one day I could study there too. Then we took a taxi to the APM line, where we heard we could see flowers, but the queue was too long. So we refunded and switched to a snack street. We tried many new foods there. One snack called bo bo gui looked like rice cake with brown sugar syrup, but it turned out to be garlic oil. It left a bad smell in our mouths and was salty, not very tasty. After eating, we rushed to the train station, expecting to arrive on time according to plan, but ran into unexpected traffic and delays. We even cut into line at the station and sprinted all the way to the gate, but were still stopped. If we had arrived just five minutes earlier, we could have made it such a pity . Then came ticket changes. My buddy couldn't get a refund after the train departure and lost 388 RMB. I paid over 110 RMB in service fees to change mine. We didn't get back to Xiamen until 10 PM. Overall, those three days were full and joyful, with rich experiences, both ups and downs. This season is actually a great time to visit Guangzhou. The sun is strong, but it's not unbearably hot. We walked so much that we were completely worn out, averaging 38,000 steps per day. I sincerely hope that one day I can study in Guangzhou, or even stay there for a while and truly live there.",
      "content_html": "<p>Itinerary:</p>\n<p>3/22: Train at 8:50 AM -&gt; Arrive in Guangzhou at 12:30 PM -&gt; Dim sum at Dian Du De -&gt; Chen Clan Ancestral Hall -&gt; Liwan Lake -&gt; Yongqing Fang -&gt; Shangxiajiu -&gt; Shamian Island -&gt; Canton Customs -&gt; Sacred Heart Cathedral -&gt; Beijing Road -&gt; Guesthouse -&gt; Dafo Temple</p>\n<p>3/23: Early morning meetup with a new friend at Sun Yat-sen Memorial Hall -&gt; Walk to Nanyue King Museum -&gt; Bamboo-pressed noodles -&gt; Yuexiu Park -&gt; Dongshankou -&gt; Taikoo Hui -&gt; Gym -&gt; Saizeriya -&gt; Huacheng Square -&gt; Canton Tower -&gt; Haixinsha -&gt; Guesthouse</p>\n<p>3/24: Guangzhou Museum of Art -&gt; Sun Yat-sen University -&gt; APM Line -&gt; Xihua Road -&gt; Train station</p>\n<p><img src=\"https://img.markxu.icu/imgRedNoteGuide.jpg\" alt=\"Xiaohongshu Guide\"></p>\n<p>Last Friday, our database teacher took a day off, which unexpectedly gave us free time and turned into a three-day mini holiday. Taking this chance, my close buddy and I planned a trip to Guangzhou, and we also wanted to meet his childhood friend there. So we set off early on Friday morning.</p>\n<p>My ticket was to Guangzhou East Station. Normally, stations named &quot;East&quot; or &quot;West&quot; are newer and larger. But this one felt the opposite. Guangzhou must have developed so early that even the later-built stations already feel old. The moment we walked out, we were in a bustling district: shiny commercial streets mixed with aging residential blocks, with a few thick, old, lively trees on both sides of the road casting shade over the wide sidewalks. We gradually tuned into the rhythm of the city. It took us a while, but eventually we caught it: long history, rich daily life, youth, and vitality.</p>\n<p>We quickly decided to go for morning tea. My buddy asked several classmates, and they all recommended Dian Du De. It was a little pricey, but the quality and service were excellent. We went to the branch closest to the station (there were a few izakayas across the street; stepping in felt like entering a Japanese drama, totally different from the lively Chinese street outside), and started feasting. The two of us spent 250 RMB and basically tried a whole range of dishes.</p>\n<p>After the meal we took an elevator, then realized it only went up. Buttons from floors 4 to 11 were all lit and couldn't be canceled. So my friend and I, together with an elderly man, just kept riding floor by floor and eventually came back down to the ground from what had originally been the second floor. The old man chatted in Cantonese, different from what you hear in Hong Kong movies, and complained about a lot of things. I only clearly understood one phrase: &quot;damn it.&quot; Just like Lu Xun wrote, that phrase is nationally unified.</p>\n<p>After that we quickly checked off all the recommended spots from Xiaohongshu. During the process, we slowly resonated with Guangzhou itself: we watched elderly bands playing in riverside parks along the Pearl River, seniors doing partner dancing, little opera stages in parks, filmed with foreign friends, saw young influencers posing at check-in spots, watched young office workers hurrying by, middle-aged people exercising steadily, elementary school kids choosing ice cream after class, parents picking up children after work, and children still playing before their parents arrived.</p>\n<p><img src=\"https://img.markxu.icu/imgGuangZhouStreet.jpg\" alt=\"Guangzhou Street\"></p>\n<p>Day one ended quickly. We were exhausted and looked forward to deep sleep that night.</p>\n<h2>Three-Person Walkabout</h2>\n<p>Early on the second day, we skipped refined breakfast shops and went to a roadside stand at a quiet crossroads. We had rice noodles for 5 RMB, and they were surprisingly great 🎉. We met my buddy's childhood friend at Sun Yat-sen Memorial Hall. At first we were not familiar, just acquainted, and didn't talk much. After visiting the memorial hall together, we passed a Taoist temple on our way to the Nanyue King Museum. Somewhere along that walk, the awkwardness faded and our conversation gradually opened up.</p>\n<p><img src=\"https://img.markxu.icu/imgzhongshan_park.jpg\" alt=\"Yuexiu Park\"></p>\n<p>After the Nanyue King Museum, we went to a noodle shop that our friend's childhood friend said was good. Unexpectedly, the bamboo-pressed noodles were made with fish-bone broth. I didn't eat a single bite (I don't like seafood 😟), and we continued on.</p>\n<p>After wandering for a long time, none of us knew what to do next, so I suggested going to the gym (our friend's childhood friend is incredibly fit!). We found a cheap gym, but it required reservation and wouldn't let us train on the spot. So we switched to a better one. The service was excellent, and the equipment was new and great. I learned a lot from him. I updated my training plan, clarified my training goals, learned many new movements and details, and also checked whether my existing form was standard. I benefited a lot.</p>\n<p>Then we went to Canton Tower. Another day ended in complete exhaustion.</p>\n<h2>Train Delay</h2>\n<p>We got up early and walked again to the Guangzhou Museum of Art, because we had forgotten to reserve Guangdong Provincial Museum and couldn't get in. After a quick visit, we arrived at the gate of Sun Yat-sen University.</p>\n<p>I had already contacted another close friend studying there. He told us about a side gate where we could enter directly without prior reservation (since we failed to reserve in advance). He also kindly told us which places on campus were worth visiting. Seeing him study in such a beautiful place made me really envious. I started imagining that one day I could study there too.</p>\n<p>Then we took a taxi to the APM line, where we heard we could see flowers, but the queue was too long. So we refunded and switched to a snack street. We tried many new foods there. One snack called bo bo gui looked like rice cake with brown sugar syrup, but it turned out to be garlic oil. It left a bad smell in our mouths and was salty, not very tasty.</p>\n<p>After eating, we rushed to the train station, expecting to arrive on time according to plan, but ran into unexpected traffic and delays. We even cut into line at the station and sprinted all the way to the gate, but were still stopped. If we had arrived just five minutes earlier, we could have made it (such a pity).</p>\n<p>Then came ticket changes. My buddy couldn't get a refund after the train departure and lost 388 RMB. I paid over 110 RMB in service fees to change mine. We didn't get back to Xiamen until 10 PM.</p>\n<p><img src=\"https://img.markxu.icu/imgGuangzhouArtMuseum.jpg\" alt=\"Guangzhou Museum of Art\"></p>\n<p>Overall, those three days were full and joyful, with rich experiences, both ups and downs. This season is actually a great time to visit Guangzhou. The sun is strong, but it's not unbearably hot. We walked so much that we were completely worn out, averaging 38,000 steps per day. I sincerely hope that one day I can study in Guangzhou, or even stay there for a while and truly live there.</p>\n",
      "summary": "This is my travel note in Guangzhou. (translated by ai)",
      "date_published": "2024-03-22T00:00:00.000Z",
      "date_modified": "2026-03-03T00:00:00.000Z",
      "image": "https://img.markxu.icu/imgIMG_20240323_广州tower.png",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/Xian-trip-notes-en",
      "url": "https://markxu.icu/blog/Xian-trip-notes-en",
      "title": "Notes from Xi'an",
      "content_text": "AI translation notice: This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original. October 27: Wake up at 4:30 a.m. and go to the airport → arrive in Xi'an at 9:50 → first lunch stop Cantonese beef hot pot → Terracotta Warriors Museum → Bell Tower → Muslim Quarter → Drum Tower → Bell Tower → homestay October 28: Wake up at 6:50 a.m. → buy medicine → meet Le-ge → walk on the city wall → Beilin Museum → Shaanxi History Museum → homestay → Giant Wild Goose Pagoda → Great Tang All Day Mall → homestay October 29: Wake up at 7:00 a.m. → Small Wild Goose Pagoda → airport An Exciting Day — October 27 Our flight left at 6:50 in the morning, so I needed to get up around 4:30 and go to bed early. I lay down at 11:30 but did not fall asleep until around one. All night I kept thinking that the next morning I would finally feel the autumn air of a place that felt like home. I was incredibly excited. When it was time to get up, I was surprisingly not tired at all. I set off for Xi'an full of energy. After arriving in Xi'an, we took the metro and followed our plan toward the Terracotta Warriors. We stopped to eat along the way because a restaurant's flyer looked interesting. The food was fine, but coming all the way to Xi'an to eat Cantonese food made the Guangdong member of our group feel personally defeated. Xi'an is very good at tourism. The metro stations had notes explaining which public-transport route to take to famous attractions, as well as how much a taxi should cost and how long it should take. This helps prevent visitors from being overcharged by unscrupulous drivers. I was genuinely moved by the effort this city had made to think about its visitors. We then reached the Terracotta Warriors . There was a little disappointment: two of the three people in our group decided not to hire a guide, so we went without one. After quickly walking through the three pits, we ran out of time and missed the Mausoleum of Qin Shi Huang. We also did not make it to Huaqing Palace, which is supposedly not that impressive anyway. Dinner that night was quite good. The restaurant was neither famous nor conspicuous, but according to the owner of the osmanthus-cake shop, it was authentic. When we asked for “Lao Liu's Lamb Paomo,” the shopkeeper's eyes lit up as she happily explained how to get there. Perhaps it really was good. The Wind on the City Wall — October 28 I woke up with irritated eyes because the homestay was not very clean, so I activated the Sharingan first thing in the morning. I went out to buy medicine and wasted more than ¥80. From now on, I will always bring some common medicines when traveling. After meeting Le-ge, we bought an egg-stuffed flatbread from a street stall. Eating a simple, ordinary, piping-hot breakfast in the slightly chilly autumn air felt like a small piece of human happiness. It was even more satisfying than eating a full meal in the warm cafeteria at my university in Xiamen. There were not many people on the northern section of the city wall, so we rode bicycles. The air was not very humid and felt much lighter, almost as though I did not need to inhale because it would flow into my lungs by itself and fill me up. My body seemed lighter too, as if buoyed by water. From the wall, the entire layout of Xi'an unfolded in the distance. The feeling of having one's expectations fulfilled was wonderful. The autumn wind refreshed me and blew away my stress; for a young person, perhaps this wind could not yet carry melancholy. Watching the kindergarten children running a “marathon” along the wall made the whole scene feel even more energetic. The Shaanxi History Museum was also completely without a guide. It was crowded, the space was relatively small, and the stuffy air made it difficult to settle down and appreciate the exhibits. The artifacts were all national treasures, and the guides seemed professional, but after one pass I did not retain a particularly deep impression. It was hard not to compare the museum with those I had visited before, especially since it is already such a remarkable institution. It might be more exciting if museums could exchange artifacts with other countries, allowing visitors to see the histories of different regions from the same period side by side. The British Museum offers something like that. As for the building itself, it is also hard to compare with the National Museum of China: one is narrow and crowded, the other grand and spacious. Despite these shortcomings, the Shaanxi History Museum left a strong impression on me. I hope I can return someday and listen to a proper guided tour. The Giant Wild Goose Pagoda and the Great Tang All Day Mall at night were genuinely awe-inspiring. The street design showed a lot of thought and encouraged visitors to interact with it: piano-key paving stones, dance-floor-style tiles that changed color, and fountains whose flow was controlled by the sound of the crowd. There were many performances along the street, including a viral “Mimou” show and a concert. The street was broad enough that even with imposing historical sculptures in the middle, snack stalls on both sides, and girls in Tang-style clothing taking photos along the edges, everyone could still walk around freely. The buildings along the street were thoughtfully designed as well. The books displayed in the Xinhua Bookstore's reading room were niche without being bizarre, numerous without feeling disorderly, and ornate without being gaudy. Someone was playing the piano, and the space was perfect for one person to calm down after the bustle outside and read without interruption. The books also suited Xi'an as a city. A bookstore really can be a city's soul. The Osmanthus Rice Ferment drink from Chahua Nong that night was genuinely delicious. When I had something similar from Chabaidao on October 30, the flavor was nowhere near as impressive. Chahua Nong emphasized the rice ferment and tea, while Chabaidao pursued the sweetness of osmanthus so single-mindedly that it lost the flower's clean fragrance. The Journey Home — October 29 We woke up a little late today and everyone slept comfortably. My body, however, had begun to react to the change of season, and my nose felt terrible. We only took a quick look at the Small Wild Goose Pagoda from the entrance because we were out of time, then continued to the airport. We did not talk much on the way. We were savoring this spontaneous, slightly fantastical journey as we boarded the plane back to Xiamen. It felt as though I had gained a little more sense of belonging to Xiamen. I still had more than two years left there, but I wondered when I would truly feel that I was also a part of the city.",
      "content_html": "<blockquote>\n<p><strong>AI translation notice:</strong> This English version was translated from the original Chinese article with AI assistance. Some nuances may differ from the original.</p>\n</blockquote>\n<p>October 27: Wake up at 4:30 a.m. and go to the airport → arrive in Xi'an at 9:50 → first lunch stop (Cantonese beef hot pot) → Terracotta Warriors Museum → Bell Tower → Muslim Quarter → Drum Tower → Bell Tower → homestay</p>\n<p>October 28: Wake up at 6:50 a.m. → buy medicine → meet Le-ge → walk on the city wall → Beilin Museum → Shaanxi History Museum → homestay → Giant Wild Goose Pagoda → Great Tang All Day Mall → homestay</p>\n<p>October 29: Wake up at 7:00 a.m. → Small Wild Goose Pagoda → airport</p>\n<h1>An Exciting Day — October 27</h1>\n<p>Our flight left at 6:50 in the morning, so I needed to get up around 4:30 and go to bed early. I lay down at 11:30 but did not fall asleep until around one. All night I kept thinking that the next morning I would finally feel the autumn air of a place that felt like home. I was incredibly excited. When it was time to get up, I was surprisingly not tired at all. I set off for Xi'an full of energy.</p>\n<p>After arriving in Xi'an, we took the metro and followed our plan toward the Terracotta Warriors. We stopped to eat along the way because a restaurant's flyer looked interesting. The food was fine, but coming all the way to Xi'an to eat Cantonese food made the Guangdong member of our group feel personally defeated.</p>\n<p><img\n  src=\"https://img.markxu.icu/imgIMG_20231027_120440.jpg\"\n  alt=\"An attractive flyer\"\n  style=\"width: min(100%, 360px); height: auto; display: block; margin: 1.5rem auto;\"\n/></p>\n<p>Xi'an is very good at tourism. The metro stations had notes explaining which public-transport route to take to famous attractions, as well as how much a taxi should cost and how long it should take. This helps prevent visitors from being overcharged by unscrupulous drivers. I was genuinely moved by the effort this city had made to think about its visitors.</p>\n<p>We then reached the <em>Terracotta Warriors</em>. There was a little disappointment: two of the three people in our group decided not to hire a guide, so we went without one. After quickly walking through the three pits, we ran out of time and missed the Mausoleum of Qin Shi Huang. We also did not make it to Huaqing Palace, which is supposedly not that impressive anyway.</p>\n<p>Dinner that night was quite good. The restaurant was neither famous nor conspicuous, but according to the owner of the osmanthus-cake shop, it was authentic. When we asked for “Lao Liu's Lamb Paomo,” the shopkeeper's eyes lit up as she happily explained how to get there. Perhaps it really was good.</p>\n<h1>The Wind on the City Wall — October 28</h1>\n<p>I woke up with irritated eyes because the homestay was not very clean, so I activated the Sharingan first thing in the morning. I went out to buy medicine and wasted more than ¥80. From now on, I will always bring some common medicines when traveling.</p>\n<p>After meeting Le-ge, we bought an egg-stuffed flatbread from a street stall. Eating a simple, ordinary, piping-hot breakfast in the slightly chilly autumn air felt like a small piece of human happiness. It was even more satisfying than eating a full meal in the warm cafeteria at my university in Xiamen.</p>\n<p>There were not many people on the northern section of the city wall, so we rode bicycles. The air was not very humid and felt much lighter, almost as though I did not need to inhale because it would flow into my lungs by itself and fill me up. My body seemed lighter too, as if buoyed by water. From the wall, the entire layout of Xi'an unfolded in the distance. The feeling of having one's expectations fulfilled was wonderful. The autumn wind refreshed me and blew away my stress; for a young person, perhaps this wind could not yet carry melancholy. Watching the kindergarten children running a “marathon” along the wall made the whole scene feel even more energetic.</p>\n<p><img\n  src=\"https://img.markxu.icu/imgIMG_20231028_091700.jpg\"\n  alt=\"We spent a little money to look through a telescope 🔭\"\n  style=\"width: min(100%, 360px); height: auto; display: block; margin: 1.5rem auto;\"\n/></p>\n<p>The Shaanxi History Museum was also completely without a guide. It was crowded, the space was relatively small, and the stuffy air made it difficult to settle down and appreciate the exhibits. The artifacts were all national treasures, and the guides seemed professional, but after one pass I did not retain a particularly deep impression.</p>\n<p>It was hard not to compare the museum with those I had visited before, especially since it is already such a remarkable institution. It might be more exciting if museums could exchange artifacts with other countries, allowing visitors to see the histories of different regions from the same period side by side. The British Museum offers something like that. As for the building itself, it is also hard to compare with the National Museum of China: one is narrow and crowded, the other grand and spacious.</p>\n<p>Despite these shortcomings, the Shaanxi History Museum left a strong impression on me. I hope I can return someday and listen to a proper guided tour.</p>\n<p><img src=\"https://img.markxu.icu/img03947BD4DA13A06C044082A55B46DB0C.jpg\" alt=\"An artifact whose name I have forgotten\"></p>\n<p>The Giant Wild Goose Pagoda and the Great Tang All Day Mall at night were genuinely awe-inspiring. The street design showed a lot of thought and encouraged visitors to interact with it: piano-key paving stones, dance-floor-style tiles that changed color, and fountains whose flow was controlled by the sound of the crowd. There were many performances along the street, including a viral “Mimou” show and a concert. The street was broad enough that even with imposing historical sculptures in the middle, snack stalls on both sides, and girls in Tang-style clothing taking photos along the edges, everyone could still walk around freely.</p>\n<p>The buildings along the street were thoughtfully designed as well. The books displayed in the Xinhua Bookstore's reading room were niche without being bizarre, numerous without feeling disorderly, and ornate without being gaudy. Someone was playing the piano, and the space was perfect for one person to calm down after the bustle outside and read without interruption. The books also suited Xi'an as a city. A bookstore really can be a city's soul.</p>\n<p>The Osmanthus Rice Ferment drink from Chahua Nong that night was genuinely delicious. When I had something similar from Chabaidao on October 30, the flavor was nowhere near as impressive. Chahua Nong emphasized the rice ferment and tea, while Chabaidao pursued the sweetness of osmanthus so single-mindedly that it lost the flower's clean fragrance.</p>\n<h1>The Journey Home — October 29</h1>\n<p>We woke up a little late today and everyone slept comfortably. My body, however, had begun to react to the change of season, and my nose felt terrible.</p>\n<p>We only took a quick look at the Small Wild Goose Pagoda from the entrance because we were out of time, then continued to the airport. We did not talk much on the way. We were savoring this spontaneous, slightly fantastical journey as we boarded the plane back to Xiamen. It felt as though I had gained a little more sense of belonging to Xiamen. I still had more than two years left there, but I wondered when I would truly feel that I was also a part of the city.</p>\n<p><img src=\"https://img.markxu.icu/imgBBBADE1069239D8271E46821A343C364.jpg\" alt=\"\"></p>\n",
      "summary": "[AI-translated] I happened to find the travel diary I wrote after visiting Xi'an. It is still a simple day-by-day record, but it brings the trip back to me, so I am publishing it here before finding it by accident all over again.",
      "date_published": "2023-10-29T00:00:00.000Z",
      "date_modified": "2026-07-01T00:00:00.000Z",
      "image": "https://img.markxu.icu/img西安.jpg",
      "tags": [
        "blog",
        "en"
      ]
    },
    {
      "id": "https://markxu.icu/blog/chaoshan-trip-notes-en",
      "url": "https://markxu.icu/blog/chaoshan-trip-notes-en",
      "title": "Chaoshan Travel Notes",
      "content_text": "Travel log: 9/24: Station - Chaoshan Station - K2 line - Central Hospital - walk - Xu Consort's Mansion passed by - Kapok Mansion, check-in video call / \"live\" sharing 9/25: Wake up at 6:00 AM, leave at 7:00 AM - Rent an e-scooter - Stroll Archway Street - Breakfast inside Archway Street - Kaiyuan Temple - Guangji Bridge - Guangji Gate - Chaozhou Ancient City - Library - Wet market lunch: beef kway teow - Return the scooter - Cross Guangji Bridge - Hanwengong Ancestral Hall - Thai Buddha Hall - Huiru Park - Take bus after changing ticket to Shantou - Check in to apartment - MixC Mall - Watch movie Creation of the Gods 9/26: Eye swelling - Register at Shantou hospital - Breakfast rice rolls - Shantou Museum - Zhongshan Park - Lunch guozhi - Pick up medicine at hospital - Shantou Small Park area - Movie Museum - Shantou Port Opening Culture Exhibition Hall - Post Office - Overseas Chinese Letters Relics Museum - Shantou Hotel - Xidi Park - People's Square, square ferry - MixC Mall - Dinner beef hotpot - Chengchuan Tea 2023.9.22 Chaozhou A train station gives different feelings to different people. To me, train stations are romantic. They mean the future, freedom, hometown, and possibilities. On Friday night, I boarded a train to Chaoshan. On the BRT, I noticed the clouds looked better than usual and the sunlight felt brighter. Maybe travel makes your eyes scan the world more carefully and discover better things. I arrived in Chaozhou at night. I waited a long time for K2, then spent another hour on the bus. It took real effort to reach my destination. During that time I made a video call to my family, basically \"livestreaming\" while walking to my guesthouse. Without that call, I would have felt much more tired. But the call made me feel emotionally closer to home, and I walked with more energy. The guesthouse was unexpectedly beautiful, like a mansion from an early Southeast Asian TV drama. 9.23 \"Diligence perfects skills, play wastes them; action succeeds through thought, and fails through conformity.\" — Han Yu I got up early and rode the e-scooter. Honestly, it saved me a lot of energy. The rental was a bit expensive, but worth it. With it, I zipped through Chaozhou Ancient City Archway Street and also visited the market area. It gave me a much deeper understanding of Chaozhou. The archways on Archway Street commemorate all kinds of outstanding successful exam candidates. It reminded me of what I had recently read in Those Things in the Ming Dynasty . I felt I understood history a bit more deeply at least that no matter what official position, once you passed the imperial exam, it was enough to erect an archway and be remembered by later generations . Guangji Gate, Guangji Bridge. Only after walking the entire bridge did I understand the origin of \"Guangji\": it was built through donations from wealthy people and institutions at home and abroad. Because I got up early, I became the first person to step onto Guangji Bridge that day, and the first to reach the broken-bridge section. Watching how the floating bridge connects was truly interesting. No wonder it's known as one of China's four great ancient bridges. I visited many places that day. The biggest disappointment was Thai Buddha Temple and Huiru Park. They face each other across a road, both in poor maintenance condition. Around them were many aggressive touts trying to squeeze out the last bit of value, which made it hard on many tourists who followed online guides. If I had to choose one unforgettable memory today, aside from the sights, it would be a photo I didn't take I was tasting herbal tea then, and it was really hard to drink ☹️ . The photo should have been a quote on the steps at the entrance of Hanwengong Ancestral Hall: Diligence perfects skills, play wastes them; action succeeds through thought, and fails through conformity. The five words \"perfect,\" \"waste,\" \"play,\" \"fail,\" and \"conform\" kept me thinking. I couldn't help admiring Han Yu's precise wording. No wonder he is considered the foremost of the Eight Great Masters of Tang and Song. Even though I didn't take that photo, I pretended to read the line once before leaving, then kept thinking about it all the way. Isn't that line describing me? Diligence — something I haven't reached yet, but have always wanted. Wasted by play — exactly what I can see myself doing. Especially the word \"play\": it's not simply the opposite of diligence, like laziness or indecision, but indulgence. When someone is busy indulging, it's hard to realize one's own faults. \"Half the soldiers die before the battle, while beauties still sing and dance in the tents.\" \"Warm winds intoxicate travelers, and they mistake Hangzhou for Bianzhou.\" Ruined by conformity — \"Those who think by following public opinion are people who block their own ears and eyes.\" This immediately reminded me of Nietzsche. I keep reminding myself to seek perspectives from different angles. But following public opinion is just too easy, and it spreads responsibility so thin that no one seems accountable. Even if you're wrong, you may not be mocked, and you often bear no real consequence. The hardest part is that you may not even notice you've been trapped by it. 9.24 If you don't use time well, how can you know how precious it is? When I got to Shantou, I was honestly overwhelmed by what I had seen the night before. I stayed in Shantou's commercial center, and there were crowds everywhere. I wandered MixC again and saw all kinds of people and many shops I'd never seen before. What struck me most was seeing some elementary-school-aged kids walking around the mall without parents when I was their age, I was only riding a bike around my neighborhood . They casually took out their phones, shot photos, posted content, spoke in internet memes, and little girls copied influencer makeup and photo poses in front of escalators. It made me feel just how different this era is. I became really curious about what their childhood will feel like when they look back in the future. All these scenes made me think the city was very developed. In the morning I went to the hospital my eye got inexplicably inflamed the day before and swelled badly . Seeing a familiar hospital environment calmed me down. Last night had been too dark to see clearly, and the lights had dazzled me. This city is, in some ways, like many cities: very livable, but still just a city. The day passed in a blur. Worried about getting back too late, I changed my ticket early and returned to school. This trip made me use time fully. If I had stayed in my dorm that weekend, I probably would have just browsed the web, complained a little, and studied inefficiently. But this time, during waiting-for-registration time alone two and a half hours , I visited an entire museum, walked through an entire park, and tried guozhi for the first time. It had been a long time since time felt this slow or maybe this full . In the past, this amount of time would have been just waking up, eating, and staring at a computer. Only when you truly use each slice of time well do you realize how rich and precious time is. Traveling Alone In the past, traveling alone was something I didn't dare do. Online, people also try to explain how lonely they are with examples: watching a movie alone, eating hotpot alone, going to the hospital alone, traveling alone. After this trip, I had done all of those. Loneliness is still hard to avoid. Whether at scenic spots or restaurants, seeing couples, groups of friends, and happy families naturally stirs envy. Luckily, WeChat sharing helped. I shared my travel photos and videos with my parents. When someone is there to share with, even a boring trip still carries meaning. Freedom is the biggest feature of traveling alone. If my eye hurts, I go to the hospital without worrying about delaying anyone, and I get to see what local hospitals are like . I don't need to consider other people's appetite or thoughts. Once I decide, I just do it. That saves a lot of energy, since the only person I need to convince is myself. So my pace became much faster. I went wherever I wanted. If I got bored, I moved on; if not, I stayed and daydreamed. And without someone beside me to constantly joke and complain with, my mind became more focused. It was no longer noisy group playfulness that makes you miss details. I had more time to observe my surroundings and try to understand them. It felt like a more mature state of mind.",
      "content_html": "<p>Travel log:</p>\n<p>9/24: Station -&gt; Chaoshan Station -&gt; (K2 line) -&gt; Central Hospital -&gt; (walk) -&gt; Xu Consort's Mansion (passed by) -&gt; Kapok Mansion, check-in (video call / &quot;live&quot; sharing)</p>\n<p>9/25: Wake up at 6:00 AM, leave at 7:00 AM -&gt; Rent an e-scooter -&gt; Stroll Archway Street -&gt; Breakfast (inside Archway Street) -&gt; Kaiyuan Temple -&gt; Guangji Bridge -&gt; Guangji Gate -&gt; Chaozhou Ancient City -&gt; Library -&gt; Wet market (lunch: beef kway teow) -&gt; Return the scooter -&gt; Cross Guangji Bridge -&gt; Hanwengong Ancestral Hall -&gt; Thai Buddha Hall -&gt; Huiru Park -&gt; Take bus (after changing ticket) to Shantou -&gt; Check in to apartment -&gt; MixC Mall -&gt; Watch movie (Creation of the Gods)</p>\n<p>9/26: Eye swelling -&gt; Register at Shantou hospital -&gt; Breakfast (rice rolls) -&gt; Shantou Museum -&gt; Zhongshan Park -&gt; Lunch (guozhi) -&gt; Pick up medicine at hospital -&gt; Shantou Small Park area -&gt; Movie Museum -&gt; Shantou Port Opening Culture Exhibition Hall -&gt; Post Office -&gt; Overseas Chinese Letters Relics Museum -&gt; Shantou Hotel -&gt; Xidi Park -&gt; People's Square, square ferry -&gt; MixC Mall -&gt; Dinner (beef hotpot) -&gt; Chengchuan Tea</p>\n<h2>2023.9.22 Chaozhou</h2>\n<h4>A train station gives different feelings to different people. To me, train stations are romantic. They mean the future, freedom, hometown, and possibilities.</h4>\n<p><img src=\"https://img.markxu.icu/imgIMG_20230922_173547.jpg\" alt=\"Xiamen North Station\"></p>\n<p>On Friday night, I boarded a train to Chaoshan. On the BRT, I noticed the clouds looked better than usual and the sunlight felt brighter. Maybe travel makes your eyes scan the world more carefully and discover better things.</p>\n<p>I arrived in Chaozhou at night. I waited a long time for K2, then spent another hour on the bus. It took real effort to reach my destination. During that time I made a video call to my family, basically &quot;livestreaming&quot; while walking to my guesthouse. Without that call, I would have felt much more tired. But the call made me feel emotionally closer to home, and I walked with more energy.</p>\n<p><img src=\"https://img.markxu.icu/imgIMG_20230922_221454.jpg\" alt=\"Night in Chaozhou\"></p>\n<p>The guesthouse was unexpectedly beautiful, like a mansion from an early Southeast Asian TV drama.</p>\n<p><img src=\"https://img.markxu.icu/imgIMG_20230922_230309.jpg\" alt=\"Guesthouse at Night\"></p>\n<h2>9.23 &quot;Diligence perfects skills, play wastes them; action succeeds through thought, and fails through conformity.&quot; — Han Yu</h2>\n<p>I got up early and rode the e-scooter. Honestly, it saved me a lot of energy. The rental was a bit expensive, but worth it. With it, I zipped through Chaozhou Ancient City (Archway Street) and also visited the market area. It gave me a much deeper understanding of Chaozhou.</p>\n<p>The archways on Archway Street commemorate all kinds of outstanding successful exam candidates. It reminded me of what I had recently read in <em>Those Things in the Ming Dynasty</em>. I felt I understood history a bit more deeply (at least that no matter what official position, once you passed the imperial exam, it was enough to erect an archway and be remembered by later generations).</p>\n<p>Guangji Gate, Guangji Bridge. Only after walking the entire bridge did I understand the origin of &quot;Guangji&quot;: it was built through donations from wealthy people and institutions at home and abroad. Because I got up early, I became the first person to step onto Guangji Bridge that day, and the first to reach the broken-bridge section. Watching how the floating bridge connects was truly interesting. No wonder it's known as one of China's four great ancient bridges.</p>\n<div class=\"img-grid-2\">\n  <img src=\"https://img.markxu.icu/imgIMG_20230923_075529.jpg\" alt=\"Chaozhou Ancient City\" />\n  <img src=\"https://img.markxu.icu/imgIMG_20230923_091203.jpg\" alt=\"Before connecting the floating section of Guangji Bridge\" />\n</div>\n<p>I visited many places that day. The biggest disappointment was Thai Buddha Temple and Huiru Park. They face each other across a road, both in poor maintenance condition. Around them were many aggressive touts trying to squeeze out the last bit of value, which made it hard on many tourists who followed online guides.</p>\n<p>If I had to choose one unforgettable memory today, aside from the sights, it would be a photo I didn't take (I was tasting herbal tea then, and it was really hard to drink ☹️). The photo should have been a quote on the steps at the entrance of Hanwengong Ancestral Hall:</p>\n<p><strong>Diligence perfects skills, play wastes them; action succeeds through thought, and fails through conformity.</strong></p>\n<p>The five words &quot;perfect,&quot; &quot;waste,&quot; &quot;play,&quot; &quot;fail,&quot; and &quot;conform&quot; kept me thinking. I couldn't help admiring Han Yu's precise wording. No wonder he is considered the foremost of the Eight Great Masters of Tang and Song.</p>\n<p>Even though I didn't take that photo, I pretended to read the line once before leaving, then kept thinking about it all the way. Isn't that line describing me?</p>\n<p>Diligence — something I haven't reached yet, but have always wanted.</p>\n<p>Wasted by play — exactly what I can see myself doing. Especially the word &quot;play&quot;: it's not simply the opposite of diligence, like laziness or indecision, but indulgence. When someone is busy indulging, it's hard to realize one's own faults. &quot;Half the soldiers die before the battle, while beauties still sing and dance in the tents.&quot; &quot;Warm winds intoxicate travelers, and they mistake Hangzhou for Bianzhou.&quot;</p>\n<p>Ruined by conformity — &quot;Those who think by following public opinion are people who block their own ears and eyes.&quot; This immediately reminded me of Nietzsche. I keep reminding myself to seek perspectives from different angles. But following public opinion is just too easy, and it spreads responsibility so thin that no one seems accountable. Even if you're wrong, you may not be mocked, and you often bear no real consequence. The hardest part is that you may not even notice you've been trapped by it.</p>\n<h2>9.24 If you don't use time well, how can you know how precious it is?</h2>\n<p>When I got to Shantou, I was honestly overwhelmed by what I had seen the night before. I stayed in Shantou's commercial center, and there were crowds everywhere. I wandered MixC again and saw all kinds of people and many shops I'd never seen before.</p>\n<p>What struck me most was seeing some elementary-school-aged kids walking around the mall without parents (when I was their age, I was only riding a bike around my neighborhood). They casually took out their phones, shot photos, posted content, spoke in internet memes, and little girls copied influencer makeup and photo poses in front of escalators. It made me feel just how different this era is. I became really curious about what their childhood will feel like when they look back in the future.</p>\n<p>All these scenes made me think the city was very developed. In the morning I went to the hospital (my eye got inexplicably inflamed the day before and swelled badly). Seeing a familiar hospital environment calmed me down. Last night had been too dark to see clearly, and the lights had dazzled me. This city is, in some ways, like many cities: very livable, but still just a city.</p>\n<p>The day passed in a blur. Worried about getting back too late, I changed my ticket early and returned to school.</p>\n<p>This trip made me use time fully. If I had stayed in my dorm that weekend, I probably would have just browsed the web, complained a little, and studied inefficiently. But this time, during waiting-for-registration time alone (two and a half hours), I visited an entire museum, walked through an entire park, and tried guozhi for the first time.</p>\n<p><img src=\"https://img.markxu.icu/imgIMG_20230924_100815.jpg\" alt=\"Zhongshan Park, looks like every city has one\"></p>\n<p>It had been a long time since time felt this slow (or maybe this full). In the past, this amount of time would have been just waking up, eating, and staring at a computer. Only when you truly use each slice of time well do you realize how rich and precious time is.</p>\n<h2>Traveling Alone</h2>\n<p>In the past, traveling alone was something I didn't dare do. Online, people also try to explain how lonely they are with examples: watching a movie alone, eating hotpot alone, going to the hospital alone, traveling alone. After this trip, I had done all of those.</p>\n<p>Loneliness is still hard to avoid. Whether at scenic spots or restaurants, seeing couples, groups of friends, and happy families naturally stirs envy.</p>\n<p>Luckily, WeChat sharing helped. I shared my travel photos and videos with my parents. When someone is there to share with, even a boring trip still carries meaning.</p>\n<p>Freedom is the biggest feature of traveling alone. If my eye hurts, I go to the hospital (without worrying about delaying anyone, and I get to see what local hospitals are like). I don't need to consider other people's appetite or thoughts. Once I decide, I just do it. That saves a lot of energy, since the only person I need to convince is myself.</p>\n<p>So my pace became much faster. I went wherever I wanted. If I got bored, I moved on; if not, I stayed and daydreamed. And without someone beside me to constantly joke and complain with, my mind became more focused. It was no longer noisy group playfulness that makes you miss details. I had more time to observe my surroundings and try to understand them. It felt like a more mature state of mind.</p>\n",
      "summary": "This is my travel note in Chaoshan. Tracked my first experience of travelling by myself. (translated by ai)",
      "date_published": "2023-09-27T00:00:00.000Z",
      "date_modified": "2026-02-25T00:00:00.000Z",
      "image": "https://img.markxu.icu/imgIMG_20230922_汕头.png",
      "tags": [
        "blog",
        "en"
      ]
    }
  ]
}
