<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel>
<title>Sibo&apos;s Blog</title><description>Engineering, reading, and life.</description><link>https://wxxlamp.cn/en/</link><language>en</language><lastBuildDate>Sun, 06 Sep 2026 07:21:43 GMT</lastBuildDate>
<atom:link href="https://wxxlamp.cn/en/rss.xml" rel="self" type="application/rss+xml"/>
<item><title>Agents, Skills, and Models: Reflections from Alibaba AI Night School</title><link>https://wxxlamp.cn/en/2026/08/18/ali-ai-night-school-agent-skill-model/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2026/08/18/ali-ai-night-school-agent-skill-model/</guid><pubDate>Tue, 18 Aug 2026 15:29:47 GMT</pubDate><description>Drawing on Taotian Technology AI Night School and Agent-development practice, this article discusses the boundaries among models, Agents, and Skills, along with business choices and evaluation methods.</description><content:encoded>&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/60fce5c0_cover.png&quot; alt=&quot;Engineering thoughts after an evening AI class&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;1-前言&quot;&gt;1. Preface&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/1bbb01e7_section-1-v2.png&quot; alt=&quot;From AI Night School to seven Agent engineering questions&quot;&gt;&lt;/p&gt;
&lt;p&gt;I recently attended AI Night School training organized by Taotian’s business technology organization. As I have been working on Agent-related development lately, I enrolled in the two-month course. Last Thursday’s first overview session was excellent, and I want to combine my recent experience building Agent applications with some thoughts on Agents, Skills, and models.&lt;/p&gt;
&lt;p&gt;This article has been declassified and contains no commercial secrets. It is highly personal in its thinking. Some sections assume readers understand the basic concepts of LLMs and Agents; without that experience, they may be a little demanding to read.&lt;/p&gt;
&lt;h1 id=&quot;2-Agent-可以代替一切吗？&quot;&gt;2. Can Agents replace everything?&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/d544d05d_section-2-v2.png&quot; alt=&quot;Probabilistic models and deterministic programs complement each other in production systems&quot;&gt;&lt;/p&gt;
&lt;p&gt;From Agents emerging in 2024, to their concentrated explosion in 2025, to xxClaw triggering “everyone raising shrimp” in 2026, LLM-powered AI appears able to do anything. Quite a few AI bloggers even make the bold claim: “In the future there will be no software, only AI.”&lt;/p&gt;
&lt;p&gt;Reality is not that tidy. Many people have never used office Agents such as WorkBuddy, Manus, or QwenWork, and their understanding of AI remains at the level of chatting with Doubao. Even if monthly active users of office Agents truly grow from millions to hundreds of millions, can AI replace all SaaS software?&lt;/p&gt;
&lt;p&gt;From a software-engineering perspective, I think that conclusion is overly optimistic.&lt;/p&gt;
&lt;p&gt;An LLM can be roughly understood as an enormous “idiom-chain game” system. Even top algorithm researchers currently struggle to clearly attribute every output. When calling an LLM, even with &lt;code&gt;temperature&lt;/code&gt; set to 0, multiple responses may not be entirely identical. Difficulty of attribution and uncertain output are unavoidable characteristics of models. Model capability alone has difficulty directly taking on highly sensitive, high-risk business operations.&lt;/p&gt;
&lt;p&gt;The real change LLMs bring is the ability to handle unstructured data. After training on massive data and parameters, they seemingly understand almost every kind of natural-language expression and give reasonable feedback. This happens to be what traditional structured programs are not good at. Workflows that once required users to repeatedly operate across multiple interfaces can be turned into more natural conversational interaction through an LLM, but that does not mean existing software will be completely replaced.&lt;/p&gt;
&lt;p&gt;In production applications, AI and programs are more like complementary roles. Models understand fuzzy intent and handle unstructured information; programs perform deterministic and structured operations. A simple example is that production environments commonly equip LLMs with a calculator tool to calculate directly. Models can calculate too, but calculator results are more stable, cheaper, and easier to verify.&lt;/p&gt;
&lt;p&gt;Cost is also very real. Context and Tokens are not cheap. Assigning a deterministic task to a normal program may consume little CPU and memory; giving every step to an LLM significantly increases resource cost and response time.&lt;/p&gt;
&lt;p&gt;Of course, one can think more aggressively: if deterministic programs themselves are written by AI and then called directly by AI after being written, does that mean AI can ultimately take over the whole digital world? In one sense, it can be understood this way, but the process will be very long. Complex software architecture and continually emerging incremental requirements remain difficult to automate completely.&lt;/p&gt;
&lt;h1 id=&quot;3-Agent-的本质是什么？&quot;&gt;3. What is the essence of an Agent?&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/8cffabe2_section-3-v2.png&quot; alt=&quot;The three-layer responsibilities of Model, Agent, and Harness&quot;&gt;&lt;/p&gt;
&lt;p&gt;While browsing the Jike community previously, I saw many bloggers with “xx_AI” in their names evaluating new models such as DeepSeek, MiniMax, and GLM. Some even judged whether a base model was capable by “how many Subagents it starts” or “whether it can generate attractive videos.” The latter at least corresponds to multimodal capability; the former is strange: starting multiple Subagents is an engineering implementation, not a base-model capability.&lt;/p&gt;
&lt;p&gt;So what is an Agent? Where is the difference between an Agent and a Model?&lt;/p&gt;
&lt;p&gt;For models, refer to my &lt;a href=&quot;https://wxxlamp.cn/en/2025/12/24/ai-ml-getting-started/&quot;&gt;introductory LLM article&lt;/a&gt; from last year. Put simply, the core of a model still resembles an “idiom-chain game”: based on enormous parameters, it uses vector and matrix operations, takes Prompt Tokens as input, and progressively generates output. Multimodal models work similarly, except their inputs and outputs become specially encoded image, audio, or video Tokens.&lt;/p&gt;
&lt;p&gt;Anyone who has read the source code of open-source Agent tools such as Codex and OpenCode will find that an Agent is essentially an engineering wrapper around calls to a model API. It assembles user input, descriptions of tools and Skills, system information, and other content into context and sends it to the model API; the model then returns Thinking Delta, Text Delta, and the tool name and input parameters to execute, token by token.&lt;/p&gt;
&lt;p&gt;An Agent’s value lies in uniformly managing the runtime for LLM API calls and providing invocation patterns such as ReAct and Handoff externally. Developers mainly focus on an Agent’s Prompt, Tool Config, and Skill, without repeatedly adapting to protocols and parameters of downstream model interfaces.&lt;/p&gt;
&lt;p&gt;One layer outward, the boundaries of today’s large Agent platforms have gone beyond a single Agent and are closer to a Harness suite. Beyond model invocation and context assembly, they handle memory extraction and storage, state restoration, tool execution, and even self-iterative optimization. These are all parts of Harness engineering.&lt;/p&gt;
&lt;p&gt;Developers need not obey the Agent abstraction in every scenario, however. Sometimes we only want to customize one model-interface parameter; having to create a whole Agent Prompt, Tool, and Skill is somewhat like using a cannon to kill a mosquito. Abstraction should resolve complexity, not create it in reverse.&lt;/p&gt;
&lt;p&gt;Many base models emphasize their Agentic capabilities. An important reason is that during training they encountered massive amounts of data in formats such as Function Call, Thinking Delta, JSON Schema, and System Prompt. Thus, once in an Agent environment, they more easily generate tool-call information that meets conventions. Such models are usually called Agentic Models. Tool-orchestration approaches, the number of Subagents, and runtime recovery capabilities still belong to engineering systems outside the model.&lt;/p&gt;
&lt;h1 id=&quot;4-关于-Skill-的思考&quot;&gt;4. Thoughts on Skills&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/e9159408_section-4.png&quot; alt=&quot;Engineering choices among Programs, Skills, and Subagents&quot;&gt;&lt;/p&gt;
&lt;p&gt;At the beginning of this year, after Anthropic proposed and popularized the concept of Skills, many friends—especially those who do not develop software—felt that everything could be solved by writing a Skill.&lt;/p&gt;
&lt;p&gt;When talking with product colleagues, I often hear: “Quickly abstract this into a Skill, then I will not need to ask you again.” There is a correct part to that statement, but also an obvious boundary. Skills can indeed accumulate experience, but many problems will not resolve themselves simply because a Skill has been written.&lt;/p&gt;
&lt;p&gt;Anyone familiar with Agent development knows that a Skill is essentially a working SOP. To prevent model context from becoming overloaded, Skill content is usually loaded progressively. A Skill can therefore be viewed as a larger, more structured Prompt. In essence it is no different from instructions typed into a Doubao input box; the principal difference is that its full text is not inserted into context at the beginning. Instead, it is progressively loaded based on the task, saving context and reducing interference with reasoning.&lt;/p&gt;
&lt;p&gt;Since a Skill describes an SOP, can everything be made into a Skill? Theoretically yes; in practice, there is no need.&lt;/p&gt;
&lt;p&gt;We should not repackage every existing SOP and Workflow as a Skill just for AI. On the one hand, programmatic Workflows cost little to execute and are more stable. On the other, the same Skill in different Agents is affected by context style, base-model capability, and recovery mechanisms, making results hard to make as precise as program flows. Skill text is deterministic, but execution results still carry model uncertainty. The market also currently lacks sufficiently mature, unified tools to evaluate and optimize Skills. The road to precisely using Skills at scale is still long.&lt;/p&gt;
&lt;p&gt;Another easily missed issue is that an Agent can read a Skill in full. Opening a Skill is, to some extent, exposing all accumulated experience within it. This risk cannot be ignored when sensitive experience or internal knowledge is involved.&lt;/p&gt;
&lt;p&gt;Therefore, for most deterministic tasks, replacing programs with Skills is often merely “AI for AI’s sake.” For example, uploading an image to GitHub can call the GitHub interface directly through a script, or use a Skill to have a model operate a page through Browser Use. Clearly, the script is faster, more stable, and more trouble-free.&lt;/p&gt;
&lt;p&gt;Skills are better suited to business processes that cannot be fully programmed but whose operational experience can be summarized. They can combine MCP and tools for overall orchestration. For example, in a handover scenario, a colleague about to leave can organize an operating manual as a Skill, and the incoming colleague can then use AI to complete tasks that previously required repeated questions. The value here is making tacit experience executable; deterministic steps still go to programs.&lt;/p&gt;
&lt;p&gt;There is another, more technical question: what exactly is the difference between Subagents and Skills? For nontechnical colleagues who directly use Agent tools such as WorkBuddy, this may sound odd—Subagent is an Agent, Skill is an Agent’s SOP, so why compare them?&lt;/p&gt;
&lt;p&gt;From an engineering implementation perspective, Agents and Skills both ultimately become sets of context passed to the model interface. Thus actual development often faces the same choice: should a task be placed as a Skill in the primary Agent, or assigned separately to a Subagent?&lt;/p&gt;
&lt;p&gt;My judgment is that Subagents suit tasks that can be completed independently, need independent context, or can run in parallel. A primary Agent plus Skill suits general flows requiring continuous shared context. When using Subagents, pay special attention to how intermediate artifacts are handed off; otherwise the benefits of isolated context may become information loss.&lt;/p&gt;
&lt;h1 id=&quot;5-Agent-的常见业务场景&quot;&gt;5. Common business scenarios for Agents&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/a37b6bc8_section-5-v2.png&quot; alt=&quot;Agent scenarios in development, service, learning, and requirement understanding&quot;&gt;&lt;/p&gt;
&lt;p&gt;The discussion so far shows that Agents can hardly replace software for all work; having Agents also does not mean business revenue and profit will grow naturally.&lt;/p&gt;
&lt;p&gt;Agents are first and foremost efficiency tools. They can help programmers with end-to-end development from requirement understanding to code implementation, and help nontechnical colleagues investigate tickets, process Excel, and write weekly reports. More importantly, many of these tasks can run in parallel. Work that is highly standardized but complex to process can go to Agents, leaving people’s energy for the parts requiring judgment and creativity.&lt;/p&gt;
&lt;p&gt;Because they can handle large amounts of unstructured data, Agents are also naturally suitable for customer service, sales, and QA. New-hire landing and work handovers are common scenarios as well. I saw at WAIC that financial systems are nearly all experimenting with Agent-based Q&amp;amp;A customer service. These practices still sound early-stage, but the direction is straightforward: first connect scattered knowledge and operational processes, then progressively raise task-completion rates.&lt;/p&gt;
&lt;p&gt;Learning is also very suitable for Agents. An Agent does not lose patience due to repeated questions, can adjust explanations to learners’ understanding levels, and has broad enough knowledge coverage. Knowledge accuracy and teaching pace still need evaluation, however, especially in specialized domains where fluent answers cannot simply be assumed correct.&lt;/p&gt;
&lt;p&gt;Moving further toward algorithms, LLM reasoning can be used to understand user needs more finely, supplementing information traditional structured features struggle to express. This can form more detailed user profiles and then serve search and recommendation scenarios.&lt;/p&gt;
&lt;p&gt;At present, the more certain value of most Agent scenarios is still efficiency improvement. Teams hoping AI will directly relieve all business pressure may need first to re-examine their business model: does the problem come from repetitive work, information processing, or the product itself? Placing every expectation on AI makes it easy to miss the problem that should actually be solved.&lt;/p&gt;
&lt;h1 id=&quot;6-Agent-开发的选型和注意点&quot;&gt;6. Agent-development choices and considerations&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/b16dbcdd_section-6.png&quot; alt=&quot;Trade-offs in Agent development among runtime location, flows, and interaction&quot;&gt;&lt;/p&gt;
&lt;p&gt;For technical colleagues, many specific business needs cannot be completed directly through general desktop Agents such as WorkBuddy and QwenWork. It is often still necessary to call model-vendor interfaces and manage context independently. In this situation, several matters need to be thought through first.&lt;/p&gt;
&lt;p&gt;First is whether the Agent runs locally for the user or in the cloud. With local operation, message disconnection and service recovery are relatively simple. If it runs in the cloud, exceptions such as message disconnection, server restarts, system outages, context storage, and scenario memory must be handled. The cloud also has obvious advantages: it can centrally collect user data and run business 24×7. Many vendors have recognized this; Alibaba’s earlier MuleRun and Qoder Agent Cloud are products that package Agents in the cloud for users to call through APIs.&lt;/p&gt;
&lt;p&gt;Second is the choice between ReAct and Workflow. Nearly all current general Agents show traces of ReAct, and implementations such as Codex (OpenAI also open-sourced a framework with the same name), DeepSeek Harness, and AgentScope have appeared. ReAct lets a model autonomously call tools through “thought—action—observation,” making it suitable for open problems. But ReAct alone is often insufficient, and developers still need to provide more deterministic Workflows.&lt;/p&gt;
&lt;p&gt;The advantage of Workflow is high determinism, more controllable cost, and faster execution. A common approach is to have ReAct first generate a Dynamic Workflow, letting AI decide the flow, then handing execution to a program. The common Plan pattern can also be seen as a variant of this form. During actual selection, clearly separate “what the model decides” from “what the program guarantees.”&lt;/p&gt;
&lt;p&gt;When truly writing an Agent, context management, memory, and caching are also important. How should user and system messages be positioned and inserted? When should user memories be extracted? Where are memories stored and how are they retrieved? How should context be assembled to make better use of Model Cache? How should tool inputs and outputs be compressed? How should every session be managed? These questions are difficult to solve completely with one unified framework, and usually must be designed one by one with the product form in mind. I only list the questions here; I may expand on particular solutions later.&lt;/p&gt;
&lt;p&gt;Finally, do not become constrained by Chat when developing Agent applications. Using a chat box does not mean a product is more intelligent. Agents truly excel at handling unstructured information and understanding fuzzy intent; interaction should still begin from the product perspective: when a button clearly solves something, why force the user to type a paragraph?&lt;/p&gt;
&lt;h1 id=&quot;7-如何做-Agent-评测&quot;&gt;7. How to evaluate Agents&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/7e849ca7_section-7.png&quot; alt=&quot;A closed loop of Agent runtime metrics, business standards, issue diagnosis, and historical regression&quot;&gt;&lt;/p&gt;
&lt;p&gt;In traditional software engineering, development and testing are both important stages; Agent development likewise cannot do without evaluation. Verifying that an Agent can complete basic functions is only the first step. The harder questions are whether it is actually useful in unstructured scenarios, can be used stably, is worth using, how Bad Cases can be found, and how problems can be quickly located and optimized after they appear.&lt;/p&gt;
&lt;p&gt;I think Agent evaluation must solve at least three problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Basic functions: ensure the Agent’s product functions meet business requirements.&lt;/li&gt;
&lt;li&gt;Stability: cover different inputs, different times, and every kind of Bad Case, checking whether output consistently meets expectations. LLM output is probabilistic, so evaluation cannot rely only on one manual experience; Agent evaluation needs to be as automated as possible.&lt;/li&gt;
&lt;li&gt;Optimization suggestions: beyond showing whether an Agent meets expectations, evaluation results should locate places to optimize in the execution chain, such as Prompt, Cache hit rate, Skill, or tool description. It is best to evaluate the cost of each execution too, to determine whether real ROI meets business expectations.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To accomplish this, an evaluation system also needs several foundational capabilities:&lt;/p&gt;
&lt;p&gt;First, collect runtime metrics such as &lt;code&gt;cache hit rate&lt;/code&gt;, &lt;code&gt;TTFT&lt;/code&gt;, &lt;code&gt;token cost&lt;/code&gt;, and &lt;code&gt;time cost&lt;/code&gt;. Without them, it is difficult to judge whether an optimization improved results or merely increased cost.&lt;/p&gt;
&lt;p&gt;Second, design evaluation criteria around business expectations. Agent output is difficult to measure with a single universal score; evaluation items must answer whether it completed a specific business goal.&lt;/p&gt;
&lt;p&gt;Third, identify and locate Bad Cases. After finding results that fail to meet expectations, continue determining whether the problem lies in Prompt, Skill, tool, context assembly, or the model itself.&lt;/p&gt;
&lt;p&gt;Fourth, retain historical evaluation results so every change can be traced and associated with previous versions. Otherwise, after adjusting a Prompt or Skill, it is easy to fix the current Case while bringing old problems back.&lt;/p&gt;
&lt;p&gt;For now, this section is more a summary of problems and directions. This article does not yet expand on concrete evaluation implementation. Once later practice is more complete, I will write a separate article.&lt;/p&gt;
</content:encoded></item>
<item><title>Building a Codex Plugin for Multichannel Publishing from Yuque</title><link>https://wxxlamp.cn/en/2026/08/02/yuque-multichannel-publisher/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2026/08/02/yuque-multichannel-publisher/</guid><pubDate>Sun, 02 Aug 2026 09:28:50 GMT</pubDate><description>I bundled polishing Yuque long-form articles, image creation, three-platform adaptation, quality checks, and draft delivery into a Codex Plugin, while documenting the real limits of platform automation.</description><content:encoded>&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/b2c5bb2c_cover.png&quot; alt=&quot;A long article passes through a resumable workflow to become publication packages for three channels&quot;&gt;&lt;/p&gt;
&lt;p&gt;I usually finish writing an article in Yuque first.&lt;/p&gt;
&lt;p&gt;But after the final sentence, the work is not over: a blog, WeChat Official Account, and RedNote each have their own content format. I still need to reformat, illustrate, split, and check it, which takes roughly another two hours.&lt;/p&gt;
&lt;p&gt;Recently, I made this workflow into a &lt;a href=&quot;https://github.com/wxxlamp/ai-coding-config/tree/main/plugins/yuque-multichannel-publisher&quot;&gt;Codex Plugin&lt;/a&gt;. It reads the Yuque original, lets AI polish it, create images, and adapt it for platforms, then uses scripts for uploading, validation, persistence, resume support, and pre-publication checks. This article is its first complete test. I want to record how it was made and clarify what can truly be automated and what still needs a person to confirm on each platform page.&lt;/p&gt;
&lt;h1 id=&quot;1-为什么要把内容分发做成插件&quot;&gt;1. Why make content distribution a plugin?&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/69c25415_section-1.png&quot; alt=&quot;The repetitive flow from one Yuque long-form article to a blog, WeChat Official Account, and RedNote&quot;&gt;&lt;/p&gt;
&lt;p&gt;I have always kept a blog, and usually put everyday writing in Yuque first. Once an article is finished, I publish it on my website, then prepare WeChat Official Account and RedNote versions. All three backends appear to accept pasted Markdown, but in practice each brings a pile of work.&lt;/p&gt;
&lt;p&gt;A blog needs complete structure, code blocks, and Hexo front matter. A WeChat article cares more about mobile reading and needs a separate 2.35:1 cover. RedNote needs the long article reorganized into a short note and a set of vertical cards. Images cannot simply be reused either: a lead image, section image, WeChat cover, and RedNote card each have different jobs.&lt;/p&gt;
&lt;p&gt;At first I gradually wrote several Skills under &lt;code&gt;.agents/skills/&lt;/code&gt; in the repository: fetching Yuque, migrating images, uploading them to image hosting, and polishing Markdown. Each worked alone, but managing them together became increasingly difficult.&lt;/p&gt;
&lt;p&gt;For example, the Yuque Skill knew only how to download an article locally, not where it should ultimately go. The polishing Skill could learn a voice, but did not know how much blog content WeChat should retain. The image-upload Skill certainly could not tell whether an image truly explained a section. Each run forced AI to reassemble those capabilities, and directories, parameters, and context were easily omitted. Copying the repository to someone else could also leave out an &lt;code&gt;.agents/skills&lt;/code&gt; dependency.&lt;/p&gt;
&lt;p&gt;The chain is long too. Yuque reading, polishing, image generation, upload, three-platform adaptation, and publishing can all be interrupted by a network or page problem. If progress exists only in the current session, the next run must reconstruct everything.&lt;/p&gt;
&lt;p&gt;These issues convinced me that the whole workflow needed one Plugin. It should carry its own instructions, scripts, references, tests, and artifact contract, while keeping personal credentials and task progress in the workspace. Copying the plugin then does not copy tokens, and resuming a task does not require guessing where it stopped.&lt;/p&gt;
&lt;h1 id=&quot;2-我是如何做出这个插件的&quot;&gt;2. How I built the plugin&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/f2fe3dfa_section-2.png&quot; alt=&quot;The plugin&apos;s five-stage flow: reading, AI processing, scripting, quality checks, and three-channel output&quot;&gt;&lt;/p&gt;
&lt;p&gt;The plugin and this article were built entirely with Codex. Here is the general approach.&lt;/p&gt;
&lt;h2 id=&quot;2-1-先划清-AI-与脚本的边界&quot;&gt;2.1. First define the boundary between AI and scripts&lt;/h2&gt;&lt;p&gt;Voice judgment, content additions, image concepts, and platform adaptation need context, so I gave them to AI. AI also performs the final review, checking factual boundaries, heading numbering, image semantics, and platform differences.&lt;/p&gt;
&lt;p&gt;Downloading files, uploading images, checking image ratios, comparing WeChat and blog bodies, creating directories, and recording state all have definite results, so scripts handle them. Scripts judge only whether files exist, ratios are correct, and fields are complete; they never secretly rewrite an author’s body text.&lt;/p&gt;
&lt;p&gt;This boundary became the plugin’s core: AI can create, but must leave inspectable artifacts; scripts remain stable without reducing creative work to string replacement.&lt;/p&gt;
&lt;h2 id=&quot;2-2-把零散-Skill-收进一个-Plugin&quot;&gt;2.2. Bring scattered Skills into one Plugin&lt;/h2&gt;&lt;p&gt;The old Yuque fetcher, image uploader, and voice cache became internal plugin components. Only one main Skill remains public, describing the full workflow and quality requirements. AI no longer finds multiple Skills temporarily or depends on the target repository’s &lt;code&gt;.agents/&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Yuque reading prefers an already logged-in browser session. This handles private documents, non-member pages, and API-policy changes. When Markdown can be copied from the page, formatting is retained directly; when it cannot, visible structure restores headings, lists, quotations, code, and images. Original images are reuploaded to the user’s own image host.&lt;/p&gt;
&lt;h2 id=&quot;2-3-用-86-篇旧文生成作者语气基线&quot;&gt;2.3. Build an author-voice baseline from 86 earlier articles&lt;/h2&gt;&lt;p&gt;I did not want each polish to reread historical articles, so AI first read the blog’s 86 existing posts and compiled &lt;code&gt;author-voice.md&lt;/code&gt;. This personal profile lives in &lt;code&gt;.codex/yuque-multichannel-publisher/style-profiles/&lt;/code&gt; in the workspace and is not packaged into the Plugin.&lt;/p&gt;
&lt;p&gt;After others install the plugin, their first run follows the same learning process, reading only historical posts in their own workspace. If no usable samples exist, the plugin explicitly uses a restrained general voice until enough articles exist to build a personal profile. The process creates only a voice reference, never keys. Image-host tokens must be supplied by environment variables or workspace configuration; WeChat and RedNote use the user’s existing logged-in sessions.&lt;/p&gt;
&lt;p&gt;The reference records a first-person engineer viewpoint, common openings, sentence and paragraph rhythm, technical reasoning patterns, ending habits, and expressions to avoid. Later polish reads only this compact baseline. If old posts change, update the corpus fingerprint; if a category genuinely differs, add a small category profile.&lt;/p&gt;
&lt;p&gt;Voice protection has two passes. First preserve facts, adding only background, causality, examples, and boundaries needed to understand the article. Only then reorganize sentences and paragraphs according to the voice profile, retaining my first-person judgments, technical reasoning, hesitation, and natural transitions. This avoids inventing experiences merely to “sound like me.”&lt;/p&gt;
&lt;p&gt;Then perform a de-AI review, targeting template openings, empty buzzwords, neat parallelism, universal summaries, and frequent negative-contrast phrasing. The project records the voice-profile path, corpus fingerprint, checks, and review notes. Scripts verify that evidence exists and scan high-risk language; AI still makes the final language judgment.&lt;/p&gt;
&lt;h2 id=&quot;2-4-让图片先有内容，再谈风格&quot;&gt;2.4. Give images content before style&lt;/h2&gt;&lt;p&gt;The first section images had correct proportions and looked technological, but said little about their sections. I therefore split generation into three steps: write a visual brief, generate the image, and review it.&lt;/p&gt;
&lt;p&gt;The brief states the section’s core claim, required objects and relationships, appropriate information type, and elements to avoid. Processes and architecture prefer editorial infographics; real screenshots come first when material exists; personal experiences fit narrative illustration. Afterwards, inspect text, relationships, and thumbnail readability, redrawing if content does not match.&lt;/p&gt;
&lt;p&gt;The body lead image retains a warm hand-drawn animation feel. Generate the WeChat cover separately at exactly 2.35:1 with a short title in the safe area. For RedNote, organize a card narrative first—each card advances one information point—then make 3:4 images.&lt;/p&gt;
&lt;h2 id=&quot;2-5-用检查点保存长任务&quot;&gt;2.5. Use checkpoints for long tasks&lt;/h2&gt;&lt;p&gt;Every content project stores &lt;code&gt;state.json&lt;/code&gt; and event records under &lt;code&gt;content-projects/&amp;lt;slug&amp;gt;/.codex/&lt;/code&gt;. They contain the current stage, completed artifacts, hashes, and final target directory.&lt;/p&gt;
&lt;p&gt;The plugin divides work into initialization, fetching, polishing, image creation, review, and persistence. After interruption, &lt;code&gt;resume&lt;/code&gt; directly shows where it stopped. Only after the &lt;code&gt;reviewed&lt;/code&gt; checkpoint is recorded may &lt;code&gt;materialize&lt;/code&gt; write drafts into formal directories.&lt;/p&gt;
&lt;p&gt;Finally, I added contract tests to the pipeline. They check heading numbering, image ratios and briefs, author-voice fingerprints, WeChat/blog body similarity, RedNote card counts, cross-round references, final directories, and whether platforms were mistakenly recorded with the same publishing status. After this test article ran, draft validation, materialization validation, and the Hexo build all passed.&lt;/p&gt;
&lt;h1 id=&quot;3-插件的结构、产物和优点&quot;&gt;3. Plugin structure, artifacts, and benefits&lt;/h1&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/2000d4ab_section-3.png&quot; alt=&quot;A reviewed long article is persisted as publication packages for blog, WeChat, and RedNote&quot;&gt;&lt;/p&gt;
&lt;p&gt;The plugin has one public Skill and roughly this directory structure:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;yuque-multichannel-publisher/
├── .codex-plugin/
│   └── plugin.json
├── README.md
└── skills/
    └── yuque-multichannel-publisher/
        ├── SKILL.md
        ├── assets/
        │   └── config.example.json
        ├── references/
        │   ├── voice-profile.md
        │   ├── editorial-guide.md
        │   ├── visual-direction.md
        │   ├── artifact-contract.md
        │   ├── style-profile-cache.md
        │   └── publishing.md
        ├── scripts/
        │   ├── pipeline.py
        │   ├── workspace.py
        │   ├── yuque_fetcher.py
        │   ├── image_uploader.py
        │   └── style_profiles.py
        └── tests/
            └── test_pipeline.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;plugin.json&lt;/code&gt; defines plugin identity, version, and Codex display information. &lt;code&gt;SKILL.md&lt;/code&gt; tells AI how to complete the workflow. &lt;code&gt;references/&lt;/code&gt; holds general standards for voice learning, editing, images, artifacts, and publishing. &lt;code&gt;scripts/&lt;/code&gt; performs deterministic work, and &lt;code&gt;tests/&lt;/code&gt; prevents directory and validation rules from silently breaking after updates.&lt;/p&gt;
&lt;p&gt;Personal data lives separately in the workspace:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;workspace&amp;gt;/.codex/yuque-multichannel-publisher/
├── config.json
├── browser-data/
└── style-profiles/
    ├── author-voice.md
    ├── profile-index.json
    └── style-*.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At runtime, personal voice, login state, image-host tokens, category cache, and task state are not written to the plugin directory. They live in &lt;code&gt;.codex/yuque-multichannel-publisher/&lt;/code&gt; in the workspace or in a concrete content project. The plugin only checks whether credentials are configured; it does not generate keys or read browser passwords. The plugin can be updated or copied without overwriting personal configuration.&lt;/p&gt;
&lt;h2 id=&quot;3-1-三个平台如何落盘&quot;&gt;3.1. How the three platforms are persisted&lt;/h2&gt;&lt;p&gt;The blog retains full argument, code, citations, and table-of-contents structure, finally writing to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;source/_posts/&amp;lt;slug&amp;gt;.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;WeChat uses the same body as the blog with only light paragraph adaptation, generating both Markdown and HTML with inline styles:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;wechat/&amp;lt;slug&amp;gt;/article.md
wechat/&amp;lt;slug&amp;gt;/article.html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;RedNote first determines how many independent notes the original supports. Every round must reintroduce its subject and core claim, then generate its body and card plan:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;rednote/&amp;lt;slug&amp;gt;/series-plan.json
rednote/&amp;lt;slug&amp;gt;/round1/post.md
rednote/&amp;lt;slug&amp;gt;/round1/cards.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This article ultimately creates only one round. Its motivation, production process, plugin structure, and benefits are one topic; splitting it into three rounds would leave the latter two without necessary context.&lt;/p&gt;
&lt;h2 id=&quot;3-2-小红书如何决定拆成几轮&quot;&gt;3.2. How RedNote decides the number of rounds&lt;/h2&gt;&lt;p&gt;RedNote splitting considers themes first, then sections. AI extracts the article’s subject, target reader, core judgment, actionable information, and real material, then judges whether each candidate theme remains complete when separated.&lt;/p&gt;
&lt;p&gt;A round must answer three questions: what it discusses, why it is worth reading, and what readers can do. It also needs its own context, benefit, and visual material. A mere step in the long article, or content requiring “the previous round” to understand, remains in the same note.&lt;/p&gt;
&lt;p&gt;After deciding the count, each round writes its context in &lt;code&gt;series-plan.json&lt;/code&gt; before writing &lt;code&gt;cards.json&lt;/code&gt;. The first card states the topic and target reader; each middle card advances one point; the last closes the conclusion or asks a concrete question. Images prioritize real screenshots and materials, then redraws, text-and-image cards, and AI scenes. Finally, read each round’s body and cards separately as a “stranger-reader test.”&lt;/p&gt;
&lt;p&gt;This article includes motivation, process, plugin structure, and benefits, but all answer “How was this plugin made?” Separating them would remove the structure and benefits from their product context, so one round remains.&lt;/p&gt;
&lt;h2 id=&quot;3-3-先检查，再决定怎样投递草稿&quot;&gt;3.3. Inspect first, then decide how to deliver drafts&lt;/h2&gt;&lt;p&gt;Following &lt;a href=&quot;https://github.com/geekjourneyx/md2wechat-skill&quot;&gt;md2wechat&lt;/a&gt; and &lt;a href=&quot;https://github.com/white0dew/XiaohongshuSkills&quot;&gt;XiaohongshuSkills&lt;/a&gt;, I separated “generate publication packages” from “operate external platforms.” The pipeline first produces a machine-readable readiness report:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 pipeline.py inspect --project &amp;lt;slug&amp;gt; --probe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It lists body character count, heading count, remote images, summary length, AI-cliché risk, missing artifacts on all three channels, and adapter status. Subsequent actions trust only blockers in this report rather than guessing publishability from “the file was generated.”&lt;/p&gt;
&lt;p&gt;WeChat can connect to a separately installed &lt;code&gt;md2wechat&lt;/code&gt;. The plugin calls &lt;code&gt;inspect&lt;/code&gt; to check account configuration, cover, and target status, then writes the draft. AppID, Secret, and API Key remain in the external tool’s own configuration; the plugin saves only an executable path and account alias.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 pipeline.py send-draft \
  --project &amp;lt;slug&amp;gt; \
  --channel wechat \
  --confirm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;RedNote is handled more conservatively. The reference project operates its creator backend through Chrome DevTools Protocol and can upload images and fill title and body, but automation is affected by page changes, login checks, and account risk control. The plugin therefore always calls its &lt;code&gt;--preview&lt;/code&gt; mode: it fills only the editor, never clicks Publish, and never records “the page was filled” as “the platform draft was saved.”&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;python3 pipeline.py send-draft \
  --project &amp;lt;slug&amp;gt; \
  --channel rednote \
  --round round1 \
  --confirm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only after RedNote explicitly reports a successful save does it record &lt;code&gt;draft_saved&lt;/code&gt;. WeChat, RedNote, and every RedNote round each have their own status; &lt;code&gt;filled_for_review&lt;/code&gt;, &lt;code&gt;draft_saved&lt;/code&gt;, and &lt;code&gt;published&lt;/code&gt; are also completely separate. Thus, a failed round does not roll back content stages already finished on other platforms.&lt;/p&gt;
&lt;p&gt;Neither external project is packaged directly into the plugin. &lt;code&gt;md2wechat&lt;/code&gt;‘s license has extra requirements for commercial use and redistribution, while &lt;code&gt;XiaohongshuSkills&lt;/code&gt; browser selectors must follow platform updates. Keeping an adapter layer means the plugin maintains only content contracts, capability probing, and state records; external tools can be installed and upgraded independently.&lt;/p&gt;
&lt;h2 id=&quot;3-4-我觉得它目前最有价值的地方&quot;&gt;3.4. What I think is most valuable now&lt;/h2&gt;&lt;p&gt;First, the plugin is self-contained: copying the directory carries the main Skill, internal scripts, references, and tests, without reassembling multiple repository Skills.&lt;/p&gt;
&lt;p&gt;Second, AI’s creative ability and scripts’ stability have clear places. AI still creates articles, images, and channel adaptations, while scripts check directories, ratios, state, and similarity. When a problem appears, it is easier to know whether to change the prompt, reference, or code.&lt;/p&gt;
&lt;p&gt;Third, long tasks are resumable. If image generation or browser automation stops, originals, drafts, images, and checkpoints remain locally and the next session can continue from the valid stage.&lt;/p&gt;
&lt;p&gt;Fourth, the three channels share facts and a long-form draft but have independent artifact forms. WeChat does not become a summary, and RedNote is not mechanically split by first-level headings.&lt;/p&gt;
&lt;p&gt;Fifth, quality requirements leave evidence. The project files show which voice reference was used, what a section image should express, whether the WeChat-cover title was checked, and why RedNote has its current number of rounds.&lt;/p&gt;
&lt;p&gt;It still has boundaries. A WeChat draft box can be written through an external adapter, but credentials, IP allowlists, and account permissions still require user configuration. RedNote automation can reliably fill the editor, but whether a draft was actually saved must be confirmed from platform feedback. The blog repository also has its own Git rules. Before real publication, account, title, time, and visibility still need confirmation.&lt;/p&gt;
&lt;p&gt;Plugin source and instructions are in &lt;a href=&quot;https://github.com/wxxlamp/ai-coding-config&quot;&gt;wxxlamp/ai-coding-config&lt;/a&gt;, under &lt;code&gt;plugins/yuque-multichannel-publisher/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For me, the plugin has put the most tiresome transport work into a repeatable flow. After writing an article, I can keep my attention on the content, wait for local publication-package checks to pass, and then decide when to open each platform.&lt;/p&gt;
</content:encoded></item>
<item><title>Designing an AI Operations Platform with AgentScope</title><link>https://wxxlamp.cn/en/2026/07/19/agentscope-ai-operations-platform-v1/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2026/07/19/agentscope-ai-operations-platform-v1/</guid><pubDate>Sun, 19 Jul 2026 15:58:00 GMT</pubDate><description>Summarizes engineering practices for an AI operations platform built on AgentScope, covering prompts, state machines, parent and child agents, tool governance, integration validation, and user-experience improvements.</description><content:encoded>&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/52d30cd1_agentscope-ops-platform-cover.png&quot; alt=&quot;AI operations platform&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When many people begin agent development, their first instinct is to make the agent loop run: the model calls a tool, gets a result, and enters the next round. But once you make an operations-oriented AI platform genuinely usable, you find that the loop is only the starting point. Based on AgentScope, this article discusses the things we encountered and considered while building an AI operations platform beyond the loop: how prompts and skills divide work, how to guide long flows, and how to ensure parameter correctness across multiple downstream APIs. (Internal platform URLs, system names, and concrete business data have been removed; only reusable engineering practices remain.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;一、核心问题与解决方案&quot;&gt;I. Core problems and solutions&lt;/h2&gt;&lt;p&gt;Three core problems are unavoidable in this type of operations agent; the approaches we ultimately adopted are listed alongside them.&lt;/p&gt;
&lt;h3 id=&quot;核心问题&quot;&gt;Core problems&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;How can strategy defined by algorithms be effectively recognized by the model?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Split agents by domains such as audience selection, outreach, and benefits; each domain agent maintains its own context and skill.&lt;/li&gt;
&lt;li&gt;Use AgentScope to implement the agent loop and basic skill and MCP management.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How can long operational flows be guided correctly without taking arbitrary actions?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;workflow + frontend flow rendering&lt;/li&gt;
&lt;li&gt;ask user question cards&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How can multiple downstream systems be connected while ensuring parameter correctness?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Connect marketing-delivery platforms, outreach platforms, data warehouses, low-code platforms, collaboration documents, and other tools and MCPs.&lt;/li&gt;
&lt;li&gt;For tools such as coupon delivery and outreach, extract and parse core parameters from context rather than letting the model fill them in itself.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;架构与解决方案&quot;&gt;Architecture and solution&lt;/h3&gt;&lt;p&gt;The following is a sanitized overview of the architecture; you can compare it with each module discussed below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/fc72e4a1_agentscope-ops-platform-architecture.png&quot; alt=&quot;AI operations platform architecture&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;二、提示词、技能与会话&quot;&gt;II. Prompts, skills, and conversations&lt;/h2&gt;&lt;h3 id=&quot;Prompt-与-Skills-分工的思考&quot;&gt;Thinking about the division of work between prompts and skills&lt;/h3&gt;&lt;p&gt;When you are starting out, you will probably do as we did: keep the main agent’s prompt simple and put routing and workflow management in skills. Soon, however, you discover that the model sometimes does not load the relevant skill at all. It instead improvises from the system prompt, weakening the result. Two recommendations follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Skills are disclosed and loaded progressively. If data is important and used in every round, put it directly in the system prompt. Do not put it in skills merely to save tokens, or the effect will suffer badly.&lt;/li&gt;
&lt;li&gt;Although system prompts are often recommended to stay within roughly 500 lines, do not be constrained by that at the start. Write a broad, comprehensive system prompt first; once results stabilize, optimize its length through references.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;动态-prompt-防止-Lost-in-the-Middle&quot;&gt;Dynamic prompts prevent Lost in the Middle&lt;/h3&gt;&lt;p&gt;For Transformer-based LLMs, attention tends to spread toward both ends and information in the middle may be “forgotten.” Compression can also remove tool results from an agent. Remember one principle: do not leave truly important information for the model to manage by itself; intervene manually.&lt;/p&gt;
&lt;p&gt;Using AgentScope middleware, at the beginning of every turn we abstract important information from memory and forcibly append it to the system prompt with &lt;code&gt;&amp;lt;Context-Reminder&amp;gt;&amp;lt;/Context-Reminder&amp;gt;&lt;/code&gt; tags. This ensures the model gives critical information “absolute attention.”&lt;/p&gt;
&lt;p&gt;Context-Reminder normally includes:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Basic campaign information (campaign name, user ID, and so on), campaign stage and flow control, audience information, IDs from external APIs, and so on&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;如何优化-Prompt&quot;&gt;How to optimize prompts&lt;/h3&gt;&lt;p&gt;Our prompt-optimization rhythm has three steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;step1: Write the initial prompt according to the agent’s role and function.&lt;/li&gt;
&lt;li&gt;step2: Keep adding prompt content and get the effect working first.&lt;/li&gt;
&lt;li&gt;step3: Once the effect meets expectations, store parts of the prompt in references and load them progressively.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two cautions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Prompts do not need to concern themselves with tool input parameters.&lt;/li&gt;
&lt;li&gt;Do not compress prompts blindly just because of context length.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Further reading:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices&quot;&gt;Claude prompting best practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/anthropics/prompt-eng-interactive-tutorial&quot;&gt;prompt-eng-interactive-tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;上下文压缩&quot;&gt;Context compression&lt;/h3&gt;&lt;p&gt;Overlong context both slows model reasoning and affects reasoning quality. AgentScope’s default compression strategies currently include compressing thinking, tools, and model replies. You can understand their progression this way:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;With AgentScope, the model’s thinking process and tool-call results are compressed by default, retaining only the user’s and model’s Q&amp;amp;A in each round.&lt;/li&gt;
&lt;li&gt;A more advanced compression strategy retains some tool results while removing invalid conversations.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;三、状态机与-Plan-工具&quot;&gt;III. State machines and the Plan tool&lt;/h2&gt;&lt;p&gt;An operations campaign is often complex and long-running: it must answer user questions flexibly while guiding the user through the process. We therefore dynamically inject a state machine into the system prompt, which guides the user at the highest priority. One key detail: do not write state-machine rules into the prompt. Instead, use hard code to determine the model’s current step and what it must do next. Its overall form is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;currentStage&quot;: &quot;阶段标识&quot;,
  &quot;currentStep&quot;: &quot;当前步骤标识&quot;,
  &quot;nextStep&quot;: &quot;下一步骤标识&quot;,
  &quot;actionType&quot;: &quot;independentThinking | callSkill | callAgent&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Along with the current step, explicitly tell the model whether completing it requires independent thinking, calling a skill, or calling an agent.&lt;/p&gt;
&lt;p&gt;To let steps advance dynamically, we customized a plan tool. The model calls it after completing each step to update progress. The system detects memory-data changes and, in the next loop, feeds the latest step back to the model through middleware.&lt;/p&gt;
&lt;p&gt;Because these steps are persisted in a database, operators can see the workflow progress directly in the frontend, greatly improving the experience.&lt;/p&gt;
&lt;h2 id=&quot;四、主子-agent&quot;&gt;IV. Parent and child agents&lt;/h2&gt;&lt;h3 id=&quot;什么时候用子-Agent？&quot;&gt;When should you use a child agent?&lt;/h3&gt;&lt;p&gt;First dispel a misconception: an agent is essentially a contextual wrapper around a model API, and the model cannot actually recognize a “child agent” (to it, that agent is merely a tool). Compared with a skill, however, a child agent has an independent context and can execute concurrently. Decide whether to split out a child agent using these three criteria:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Is the subtask independent?&lt;/li&gt;
&lt;li&gt;Can the subtask execute in parallel?&lt;/li&gt;
&lt;li&gt;Does the subtask require too many iterations?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Division of work&lt;/strong&gt; also brings an easily overlooked organizational benefit. When several algorithm engineers participate, splitting child agents by domain lets everyone own the quality of their own agent. Clear domain boundaries make integration smoother and provide better assurance for the whole project.&lt;/p&gt;
&lt;h3 id=&quot;主子-agent-的通信与协作&quot;&gt;Communication and collaboration between parent and child agents&lt;/h3&gt;&lt;p&gt;Three practical lessons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Dynamically inject the “child agent currently to be called” into the parent agent’s system prompt. This prevents the parent agent from taking over before the child task is finished and significantly improves the parent’s recognition accuracy.&lt;/li&gt;
&lt;li&gt;Require child agents to return JSON and add an &lt;code&gt;isCompleted=true&lt;/code&gt; field to indicate completion. (Some models can enable structured JSON output by parameter.)&lt;/li&gt;
&lt;li&gt;After a child agent finishes, persist its returned result through middleware. Because the JSON format is fixed, the data can directly become inputs to subsequent tools.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;推理时间过长的优化措施&quot;&gt;Optimizing excessive reasoning time&lt;/h3&gt;&lt;p&gt;During initial tests, agents can take too long to reason. Do not simply disable thinking: that severely harms results. With LLMs, we took several measures to reduce reasoning time:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Context compression: remove child-agent skill preloading and lower the main agent’s thinking-token limit.&lt;/li&gt;
&lt;li&gt;Thinking budget: some LLMs expose a &lt;code&gt;thinking-budget&lt;/code&gt; parameter to control thinking duration.&lt;/li&gt;
&lt;li&gt;Adjust top-p and temperature.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We are continuing to explore several directions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Parallel agent invocation.&lt;/li&gt;
&lt;li&gt;Asynchronous agent invocation, with the main agent returning results to the business early.&lt;/li&gt;
&lt;li&gt;Splitting agent responsibilities more finely and matching different models and parameters to different responsibilities.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;五、工具与-MCP&quot;&gt;V. Tools and MCP&lt;/h2&gt;&lt;h3 id=&quot;如何保证接口参数的正确性&quot;&gt;How to ensure API-parameter correctness&lt;/h3&gt;&lt;p&gt;When integrating APIs such as marketing-delivery and outreach platforms, the greatest concern is that the model will not understand parameter meanings and therefore pass incorrect parameters, such as channel ID, validity period, or start time.&lt;/p&gt;
&lt;p&gt;Experience confirmed this concern. In early integration testing, the model regularly passed wrong fields; audience SQL was a typical case. Even if field definitions are clear, when many audience-selection SQL statements exist in context, the model may randomly choose the wrong one—for example, pass a production table instead of an offline table, or use a benefits SQL statement for outreach SQL.&lt;/p&gt;
&lt;p&gt;We adopted three measures:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add a “parameter definition” API on the delivery-platform side to strictly constrain input enumerations and make definitions as rich as possible.&lt;/li&gt;
&lt;li&gt;On the operations-platform side, wrap the delivery platform’s MCP as tools, then use middleware to hard-code extraction of key parameters—audience SQL, start time, validity period, campaign type, and so on. Code passes these parameters directly; the model does not need to infer them.&lt;/li&gt;
&lt;li&gt;Add monitoring: persist both the “model-inferred parameters” and the parameters ultimately passed to the delivery platform, making later audits and tracing possible.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;工具并发调用踩坑&quot;&gt;Pitfalls in concurrent tool calls&lt;/h3&gt;&lt;p&gt;Calling tools concurrently can reduce model loop count and improve results, but be careful: concurrent modifications to database fields may overwrite data and break consistency. Evaluate this before designing a concurrency plan.&lt;/p&gt;
&lt;h2 id=&quot;六、联调与验证&quot;&gt;VI. Integration and validation&lt;/h2&gt;&lt;p&gt;After changing a prompt or skill, validating the corresponding feature is often time-consuming, and it is difficult to know whether the change affected other areas.&lt;/p&gt;
&lt;p&gt;Our solution is a custom script. It uses a coding agent to call the platform’s chat API, converse with the platform through a model, and intelligently judge whether the platform’s reply meets expectations under agreed constraints. The validation uses these artifacts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An e2e script that converses with the platform through a coding agent.&lt;/li&gt;
&lt;li&gt;Output from platform conversations.&lt;/li&gt;
&lt;li&gt;Platform runtime logs.&lt;/li&gt;
&lt;li&gt;The platform model’s context (only the final two arrays).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Feed those artifacts to the coding agent and ask it to analyze whether platform behavior meets the standard in light of the feature optimized this time.&lt;/p&gt;
&lt;p&gt;Frankly, this approach is still early. We have automated the conversations, but because agents in different roles are all specialized, algorithm engineers still need to combine the logs for final analysis after the conversation ends.&lt;/p&gt;
&lt;h2 id=&quot;七、用户体验的一些措施&quot;&gt;VII. User-experience measures&lt;/h2&gt;&lt;h3 id=&quot;KeepAlive-保活机制&quot;&gt;KeepAlive mechanism&lt;/h3&gt;&lt;p&gt;When a user does not provide input for a long time, an SSE stream can easily disconnect. We added a server-side keepalive mechanism to prevent conversation interruption. If your product also uses SSE for streaming output, this is almost essential.&lt;/p&gt;
&lt;h3 id=&quot;断点重试机制&quot;&gt;Resume-and-retry mechanism&lt;/h3&gt;&lt;p&gt;Network jitter, such as backend-service deployment, can break chat. We therefore added resume-and-retry support in the frontend: it automatically retries a chat session three times, minimizing user awareness.&lt;/p&gt;
&lt;h3 id=&quot;Ask-User-Question-卡片&quot;&gt;Ask User Question cards&lt;/h3&gt;&lt;p&gt;Using AgentScope, we optimized ask user question cards. After the model calls this tool, users can choose options directly, fill blanks, and complete dates, yielding a smoother interaction.&lt;/p&gt;
&lt;h2 id=&quot;八、未来发展-规划&quot;&gt;VIII. Future development and plans&lt;/h2&gt;&lt;p&gt;Finally, here are several directions we are advancing; discussion is welcome:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An auto copilot for multiple products and campaigns.&lt;/li&gt;
&lt;li&gt;An automatic correction mechanism for different campaign types derived from user sessions: use scheduled tasks to scan user conversations, capture what users question, and automatically optimize skills and prompts.&lt;/li&gt;
&lt;li&gt;Invoke different agents concurrently to improve system response speed.&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item>
<item><title>Reflections on Writing 10,000 Lines of Code with Qoder</title><link>https://wxxlamp.cn/en/2026/06/07/qoder-vibe-coding-thoughts/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2026/06/07/qoder-vibe-coding-thoughts/</guid><pubDate>Sun, 07 Jun 2026 08:00:00 GMT</pubDate><description>Shares experience and reflections from using Qoder to develop 10,000 lines of code across two large commercial projects, examining the limits of AI Coding efficiency and how programmers&apos; capabilities must evolve.</description><content:encoded>&lt;h1 id=&quot;前言&quot;&gt;Introduction&lt;/h1&gt;&lt;p&gt;I previously used Claude Code, but recently could not use the Opus-series models for various reasons. With company promotion as well, I began using Qoder as its substitute.&lt;/p&gt;
&lt;p&gt;In April I developed nearly 10,000 lines of effective business code. Human intervention during development was below 10%, although integration and test-submission required more human work because of infrastructure.&lt;/p&gt;
&lt;p&gt;Through two key projects, this article discusses how much coding agents can improve R&amp;amp;D efficiency and what remains worth caring about in an era of organization-wide AI coding. The practical material is long; readers in a hurry can jump directly to the reflections.&lt;/p&gt;
&lt;p&gt;I briefly record practices and thoughts from the coding stage. These lessons are not limited to Qoder; I hope to extract reusable strategies for other coding agents.&lt;/p&gt;
&lt;h1 id=&quot;实践&quot;&gt;Practice&lt;/h1&gt;&lt;h2 id=&quot;【实践一】使用Qoder完成网关和外部渠道的支付流程对接&quot;&gt;[Practice 1] Using Qoder to integrate a gateway payment flow with an external channel&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;Under an existing mature architecture, this practice used the channel’s mature API documentation to integrate five payment APIs and two webhooks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;开发准备阶段&quot;&gt;Development preparation&lt;/h3&gt;&lt;p&gt;The PRD was not AI-native—it was not written in a structured form AI can understand. Feeding it directly to AI would produce a mess even with complete skills, MCPs, and rules. Before development, I therefore did the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Clarified channel APIs:&lt;/strong&gt; understood call flows and timing, and special cases such as retries and idempotency; prepared complete API documentation for Qoder.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clarified the business solution:&lt;/strong&gt; after PRD review, many points remained undecided or conflicted with existing business, so people had to align the overall requirements with PD and the business team.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clarified the technical solution:&lt;/strong&gt; AI can write a design, but Qoder did not know the infrastructure’s design preferences and priorities for channel integration. Letting it design directly from the PRD and documentation would cause much correction and rework. It was cheaper for developers to write the technical document’s skeleton and cautions, then let Qoder complete it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reviewed existing code:&lt;/strong&gt; channel integration is a mature gateway capability. Besides the design, PRD, and APIs, Qoder needed existing channel-integration code, including task-scheduling and API-rendering strategies. Do not expect it to understand code by grepping without context; that inevitably misses things.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;AI-Coding阶段&quot;&gt;AI Coding stage&lt;/h3&gt;&lt;p&gt;Before coding, I created a new-channel integration skill from prior experience. Combining it with SPEC completed design clarification, implementation, testing, and compilation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/88836131_qoder-1.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Qoder fetched Yuque links, channel API documents, and related repository code from the technical document. Because the design was clear, its reasoning and output were highly accurate. It then asked about ambiguous points:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/b74e18a5_qoder-2.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;After clarification, Qoder generated a detailed SPEC. I spent about an hour reviewing it, correcting incorrect design, and asking Qoder to optimize it again:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/675c3786_qoder-3.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once the SPEC was clear, Qoder could plan, implement, compile, and self-test autonomously.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;Although Qoder’s design needed continuous clarification, it considered things that are hard to foresee during technical-document design. For example, its questions revealed that the original design for refreshing a new channel’s token was wrong, enabling optimization.&lt;/u&gt;&lt;/p&gt;
&lt;h3 id=&quot;Code-Review阶段&quot;&gt;Code Review stage&lt;/h3&gt;&lt;p&gt;Even after repeated SPEC reviews, AI-generated code needs a code-level CR. I used both an independent-context CR agent with the technical document and manual visual checking, taking about two hours. Problems included:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Qoder’s habits did not match my own, changing some style. For example, it wrapped every request and response in a &lt;code&gt;json object&lt;/code&gt;; AI can understand this, but future maintenance may be difficult.&lt;/li&gt;
&lt;li&gt;Its &lt;strong&gt;architectural understanding&lt;/strong&gt; of the application was insufficient, so some architecture-related foundational code was not handled well.&lt;/li&gt;
&lt;li&gt;The application used much Diamond and database configuration, yet &lt;strong&gt;Qoder could not accurately obtain or add configuration&lt;/strong&gt;, creating many configuration-handling problems.&lt;/li&gt;
&lt;li&gt;Repeatedly prompting AI to fix these issues can &lt;strong&gt;pollute context and make Qoder fix things ever more incorrectly&lt;/strong&gt;, eventually requiring human intervention.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;In theory, a better harness can improve AI understanding of these issues. For item 4, subagents can reduce hallucinations caused by context consumption.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;自测-fix阶段&quot;&gt;Self-test and fixes&lt;/h3&gt;&lt;p&gt;After CR came self-testing. This could theoretically use MCPs for browser, HSF, database, and SLS configuration, but our infrastructure was incomplete, so testing remained manual.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Despite standardized documentation, the gateway did not correctly parse channel inputs and outputs. For a &lt;code&gt;data.response.result&lt;/code&gt; response, Qoder omitted the middle &lt;code&gt;response&lt;/code&gt; and parsed &lt;code&gt;data.result&lt;/code&gt;, making channel data unavailable.&lt;/li&gt;
&lt;li&gt;Channel error codes and idempotent retry mechanisms also need attention. Qoder handled error returns from the documentation but did not consider retries after errors or recovery cases.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;u&gt;These fixes were difficult to request in the old context. Many integration bugs require repeated alignment with the channel before a solution emerges. In this stage, manual modification often proved much faster than AI Coding.&lt;/u&gt;&lt;/p&gt;
&lt;h2 id=&quot;【实践二】使用Qoder完成未知代码的资金结算对接&quot;&gt;[Practice 2] Using Qoder for a fund-settlement integration in unfamiliar code&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;This practice used Qoder and SPEC to write code for an application the developer did not know well. AI wrote 100% of the code, and delivery took about one week.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;开发准备阶段-1&quot;&gt;Development preparation&lt;/h3&gt;&lt;p&gt;As in Practice 1, although I did not know the application’s architecture, I wrote a technical document from my own context and supplied it to Qoder+SPEC for design and coding. The document both let Qoder match the developer’s thinking more accurately and helped me become familiar with the system. If the developer does not understand the system, 100% AI Coding is clearly extremely difficult today.&lt;/p&gt;
&lt;h3 id=&quot;AI-Coding阶段-1&quot;&gt;AI Coding stage&lt;/h3&gt;&lt;p&gt;I completed the core technical document manually:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/0d21480a_qoder-4.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;During SPEC, Qoder revealed a major design problem:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/0cfc6174_qoder-5.png&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tax data had to be added. Under the existing business-code identity logic, the model needed two fields: tax amount and tax currency.&lt;/li&gt;
&lt;li&gt;Because I did not know the code well, Qoder also identified the inflow and outflow direction of the tax fund flow during SPEC, avoiding many pitfalls.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;Code-Review阶段-1&quot;&gt;Code Review stage&lt;/h3&gt;&lt;p&gt;After Qoder independently wrote the code and unit tests, I started a new agent for CR. It found country-validation issues: only Mexico may calculate tax; non-Mexico traffic should alert and be blocked. Since the codebase was small, around 500 lines, the new agent independently performed CR and fixes using the technical document.&lt;/p&gt;
&lt;h3 id=&quot;自测-fix阶段-1&quot;&gt;Self-test and fixes&lt;/h3&gt;&lt;p&gt;End-to-end testing then found data inconsistencies through order processing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CASE 1:&lt;/strong&gt; Persisting data during order settlement failed because code fields differed from newly added database fields. Manual configuration changes differed from Qoder’s understanding, causing database insert failure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CASE 2:&lt;/strong&gt; After partial order refunds, recalculating tax produced a settlement-total discrepancy that prevented settlement. Investigation showed that recalculating settlement after a refund did not handle the new tax fields. This was a knowledge-base and model issue, so Qoder had to be prompted to add the missing code:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/a6f6e234_qoder-6.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;These problems show that insufficient context and reasoning information can skew Qoder’s technical design. Human intervention and alignment are still necessary for non-code issues, or its coding will also go wrong.&lt;/p&gt;
&lt;h1 id=&quot;感悟&quot;&gt;Reflections&lt;/h1&gt;&lt;h2 id=&quot;善用SPEC模式&quot;&gt;Use SPEC well&lt;/h2&gt;&lt;p&gt;Qoder’s SPEC mode is powerful: enable SPEC in the conversation and Qoder uses planning and subagents with existing MCPs and skills for Q&amp;amp;A before generating a spec document. Users should focus on reviewing and repeatedly revising that document through conversation to improve accuracy and delivery quality.&lt;/p&gt;
&lt;p&gt;SPEC plus careful code review is at least twice as effective and efficient as simple conversational coding. SPEC consumes context quickly and it is unclear whether repeated compression will reduce quality, but Qoder’s current compression is good and accuracy has not visibly decayed.&lt;/p&gt;
&lt;h2 id=&quot;上下文决定一切&quot;&gt;Context determines everything&lt;/h2&gt;&lt;p&gt;Modern coding agents compress context, but we still cannot complete unlimited requirements in one session. Claude Code’s official documentation says to conserve context: results below 50% context usage are much better than those above 70%.&lt;/p&gt;
&lt;p&gt;Context is always precious. Conserve it by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Using subagents:&lt;/strong&gt; a subagent has context independent of the main agent, which only needs its result, not its process. Careful inspection shows Qoder sometimes spontaneously uses subagents itself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Using skills rather than MCPs:&lt;/strong&gt; both consume context, but MCP only tells the model that a tool exists, while a skill’s SOP is much more precise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disabling unnecessary skills and MCPs:&lt;/strong&gt; do not load all system and personal MCPs and skills in every conversation; this consumes context and burdens the model with choices.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Use Qoder’s repowiki to keep the system architecture current, and rules and hooks to update rules and handling. Repowiki and rules enter the model context in every session; only continuously updating them and maintaining the harness makes Qoder increasingly useful.&lt;/p&gt;
&lt;h2 id=&quot;非代码配置影响AI-Coding效率&quot;&gt;Non-code configuration affects AI Coding efficiency&lt;/h2&gt;&lt;p&gt;In the era of manual programming, everyone used non-code configuration to improve R&amp;amp;D efficiency. When a boss asks how efficiency improved, many answer, “What previously required code can now be done by configuration.”&lt;/p&gt;
&lt;p&gt;Deployment and release are also extremely complex, and configuration helps changes go online quickly. But in an AI Coding era, when AI can write code almost instantly, do configurations scattered through Diamond, switches, and databases still improve efficiency?&lt;/p&gt;
&lt;p&gt;They also create obstacles to AI understanding. Current infrastructure lets AI quickly read all code, but it is lost when configuration is involved: it cannot directly read configuration and therefore writes strange code. Without configuration permissions, developers also spend much time handling it themselves.&lt;/p&gt;
&lt;h2 id=&quot;人工参与不可避免&quot;&gt;Human participation is unavoidable&lt;/h2&gt;&lt;p&gt;Many people reconsider the roles of humans and AI from architecture and top-level design. Extreme designs even imagine no people: AI handles requirements, coding, tests, and releases. It is a beautiful wish, but frontline practice in large commercial systems shows that end-to-end AI still needs time. AI Coding still requires expert experience and human intervention.&lt;/p&gt;
&lt;p&gt;Simple class-level changes can unquestionably be fully given to AI. But people cannot fully state requirements during requirement design—not only PD, but developers too. Requirement creators cannot state every delivery need at once; therefore AI cannot be expected to directly write highly faithful, robust code. Today AI coding can join each atomic R&amp;amp;D stage and iterate code through Vibe Coding, but humans must still connect the entire software lifecycle.&lt;/p&gt;
&lt;p&gt;Collaboration is another familiar issue. People say most R&amp;amp;D time is spent in meetings, not coding; similarly, much software-development time is spent in integration rather than coding. In the two deep Qoder practices, it wrote nearly 85% of code in the first SPEC stage, but the 15% needing integration fixes required people; Qoder could not handle it.&lt;/p&gt;
&lt;p&gt;For channel integration, Qoder omitted defensive programming such as idempotency, retries, transactions, and consistency. These must be customized for different architectures, and developers must participate deeply for Qoder to produce expected code. This is not unsolvable: continuously improving project-specific coding habits and rules through the harness will reduce such intervention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Finally, if human participation is unavoidable, developers must still understand overall requirements, solution design, and code details. Would you dare deploy code you do not understand to serve users or operate funds?&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;程序员的能力边界在哪里？&quot;&gt;Where are programmers’ capability boundaries?&lt;/h2&gt;&lt;p&gt;If the obstacles to full-lifecycle AI coding discussed above—model capability, context, and infrastructure—are solved, will programmers’ job model change?&lt;/p&gt;
&lt;p&gt;Certainly.&lt;/p&gt;
&lt;p&gt;Programmers will no longer be constrained by languages: Java today, Python tomorrow. Once they understand a language’s core ideas, models can supply syntax and ecosystem. Nor must programmers be constrained by specialties: frontend, backend, and big data can all theoretically be completed with AI assistance.&lt;/p&gt;
&lt;p&gt;Does that mean programmers can do everything, and AI lowers requirements? I believe the opposite. AI frees programmers from rote interview knowledge and syntax so they can focus on transferable capabilities: learning, innovation, and rapid migration. They must understand more, make decisions, judgments, and guidance for AI in core domains rather than merely implement—because AI will certainly do implementation.&lt;/p&gt;
&lt;p&gt;In the near future, programmers must master coding-agent tools as they master computing fundamentals, give code development to AI, expand their ability boundary upward, and engage deeply with business requirements and product capability. That is the most certain trend.&lt;/p&gt;
&lt;p&gt;So if your present work never requires professional judgment and you only keep your head down coding, it is time to look up and reassess yourself.&lt;/p&gt;
</content:encoded></item>
<item><title>A Practical Guide to Claude Code Commands</title><link>https://wxxlamp.cn/en/2026/04/13/claude-code-command-guide/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2026/04/13/claude-code-command-guide/</guid><pubDate>Mon, 13 Apr 2026 14:00:00 GMT</pubDate><description>An in-depth explanation of Claude Code’s core concepts and common commands, covering Command, Skill, Rule, Subagent, and other key features and usage tips to help readers master modern AI coding workflows.</description><content:encoded>&lt;p&gt;Work agents were the undisputed stars of 2025, with Claude Code standing out most. Claude Code has remained at the forefront of coding agents, introducing concepts such as skills and subagents that have become de facto standards.&lt;/p&gt;
&lt;p&gt;Subsequently, a series of coding agents—including Qoder, Gemini CLI, Qwen CLI, and CodeX—have followed CC’s main design. Once one understands the principles of one agent, that understanding can transfer to the others.&lt;/p&gt;
&lt;p&gt;Meanwhile, concepts such as commands, skills, rules, and subagents exist in nearly every work agent. Once these assets are accumulated, they can be smoothly reused by other agents.&lt;/p&gt;
&lt;p&gt;So, based on Claude Code’s official documentation plus my own understanding, I have organized these slash-command-related concepts to help with later vibe coding.&lt;/p&gt;
&lt;p&gt;This is the first article in the Claude Code series. It introduces foundational concepts and operations. I expect two more articles: one on common context-optimization techniques in CC, and another on my personal thoughts about assisting programming with CC.&lt;/p&gt;
&lt;h1 id=&quot;基本概念&quot;&gt;Basic concepts&lt;/h1&gt;&lt;h2 id=&quot;Command&quot;&gt;Command&lt;/h2&gt;&lt;p&gt;A command is the most direct user prompt for driving an agent. Entering instructions directly in the CLI or GUI input box triggers CC to respond.&lt;/p&gt;
&lt;p&gt;If an instruction is too long or reusable, it can be made into a file and placed in the &lt;code&gt;.cluade/commands/&lt;/code&gt; directory. The next time it is needed, enter slash + command directly in the CLI, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/94ab8801_b76cb1b4.png&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;Skill&quot;&gt;Skill&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;For related material, see: &lt;a href=&quot;https://code.claude.com/docs/en/skills&quot;&gt;https://code.claude.com/docs/en/skills&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A Skill can be understood as a large command that turns complex or standard work into an SOP. The biggest difference between a skill and a command is that skills can be invoked proactively, while during vibe coding Claude Code can also analyze context and decide whether to invoke a skill automatically. So how do we write a skill?&lt;/p&gt;
&lt;p&gt;Writing a skill manually is certainly good, but in the AI era we can also ask CC to write it. Claude Code’s official &lt;a href=&quot;https://code.claude.com/docs/en/skills&quot;&gt;skill guide&lt;/a&gt; and &lt;a href=&quot;https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md&quot;&gt;skill instruction&lt;/a&gt; can help us create one.&lt;/p&gt;
&lt;p&gt;Once we have a skill for creating skills, we can converse with Claude Code to make other skills. For example, I wanted a skill that retrieves a document from yuque.com, with the following prompt:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Help me create a skill that retrieves documents from yuque.com. Specifically, I give you a Yuque document link (I can access it normally in Chrome because I am logged into my own Yuque account there, and the link is to my own Yuque document). Please read the document from the Yuque link, download it locally, and save it in Markdown format.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Claude Code can automatically discover the &lt;code&gt;skill-create&lt;/code&gt; skill we just wrote, as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/2b4cdca1_dcdc8b25.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;I have uploaded the generated skill to GitHub; interested readers can &lt;a href=&quot;https://github.com/wxxlamp/wxxlamp.github.io/blob/deploy/.claude/skills/yuque-fetcher/SKILL.md&quot;&gt;click here&lt;/a&gt; to obtain it.&lt;/p&gt;
&lt;p&gt;Next, using the yuque-fetcher skill generated by skill-create to save a Yuque document locally, Claude Code traverses all available skills and invokes the &lt;code&gt;yuque-doc-downloader&lt;/code&gt; skill by default to download the article.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/b62727ad_4db8f1a8.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;The result is excellent too. Although the images did not display successfully, the basic content was all there.&lt;/p&gt;
&lt;p&gt;One question is how to discover whether a skill has been called. Check &lt;code&gt;~/.claude.json&lt;/code&gt; for the number of times a skill has been invoked in the background:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/006b47f7_c3232358.png&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;Rule&quot;&gt;Rule&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;For related material, see: &lt;a href=&quot;https://code.claude.com/docs/en/memory#&quot;&gt;https://code.claude.com/docs/zh-CN/memory&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The essence of a rule is also a kind of memory. CC supplies the complete rule to the LLM in every model request. We can inspect it with &lt;code&gt;/memory&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/c09f471b_3f295084.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;The most common use of a rule is constraining Claude’s behavior. For example, code conventions can be written as rules for CC to use as reference. Project-level rules are located as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;your-project/
├── .claude/
│   ├── CLAUDE.md           # 主项目指令
│   └── rules/
│       ├── code-style.md   # 代码样式指南
│       ├── testing.md      # 测试约定
│       └── security.md     # 安全要求
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the &lt;code&gt;paths&lt;/code&gt; property can choose which files a rule applies to, as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;---
paths:
  - &quot;src/api/**/*.ts&quot;
---

# API 开发规则

- 所有 API 端点必须包括输入验证
- 使用标准错误响应格式
- 包括 OpenAPI 文档注释
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;Hooks&quot;&gt;Hooks&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;For related material, see: &lt;a href=&quot;https://code.claude.com/docs/en/hooks-guide&quot;&gt;https://code.claude.com/docs/en/hooks-guide&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Hook mechanism gives users more precise control over Claude Code’s behavior. We can run various commands before and after sessions, and before and after scripts execute.&lt;/p&gt;
&lt;p&gt;According to &lt;a href=&quot;https://code.claude.com/docs/en/hooks-guide&quot;&gt;CC’s official documentation&lt;/a&gt;, common hook phases are as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/0700f2c0_ab484b5d.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;A classic hook use case is context compression. For a project, we can have Claude compress context into local storage every time a Claude session closes, then automatically read it on the next launch.&lt;/p&gt;
&lt;p&gt;Refer to &lt;a href=&quot;https://code.claude.com/docs/en/hooks-guide#get-notified-when-claude-needs-input&quot;&gt;Claude Code’s official example&lt;/a&gt;: whenever human intervention is needed, show a prompt so the task does not keep holding.&lt;br&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/799f3c38_07e56628.png&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;MCP&quot;&gt;MCP&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;For related material, see: &lt;a href=&quot;https://code.claude.com/docs/en/mcp&quot;&gt;https://code.claude.com/docs/en/mcp&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When Claude receives instructions, it lists not only all skills and rules, but all MCPs as well. Claude Code chooses the most appropriate MCP based on the user’s instruction.&lt;/p&gt;
&lt;p&gt;Note, however, that MCP locations differ from those of skills and similar assets. Both user-level and project-level MCP configuration are in &lt;code&gt;~/.claude.json&lt;/code&gt;. Common MCPs include:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Main capabilities&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;sequential-thinking&lt;/td&gt;
&lt;td&gt;Sequential reasoning for complex problems. When solving multi-step problems, performing logical analysis, or planning complex work, this server helps build a chain of reasoning step by step.&lt;/td&gt;
&lt;td&gt;Multi-step logical analysis, task planning, step-by-step derivation of conclusions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;jetbrains&lt;/td&gt;
&lt;td&gt;Integrates with JetBrains IDEs, such as IntelliJ IDEA, PyCharm, and WebStorm.&lt;/td&gt;
&lt;td&gt;Lets AI read code directly, refactor, run tests, access project structure, and perform other IDE-level operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;browsermcp&lt;/td&gt;
&lt;td&gt;Browser automation that lets AI control browser behavior.&lt;/td&gt;
&lt;td&gt;Navigate pages, click elements, fill forms, take screenshots; suitable for web testing, data collection, and application debugging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppeteer&lt;/td&gt;
&lt;td&gt;Browser automation based on Puppeteer, with capabilities similar to browsermcp.&lt;/td&gt;
&lt;td&gt;Page control, element operations, JavaScript execution, PDF/screenshot generation; lower-level and more programmable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;context7&lt;/td&gt;
&lt;td&gt;Real-time access to current programming documentation and code examples, beyond the timeliness limits of AI training data.&lt;/td&gt;
&lt;td&gt;Query API usage, obtain current library documentation, retrieve code examples, and support lookup of version updates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: browsermcp and puppeteer overlap in function, but browsermcp is a general browser-MCP interface, while puppeteer usually refers specifically to an implementation based on Chromium and Node.js automation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Of course, MCP can also be configured at project level; use this command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;claude mcp add --transport http hubspot --scope user https://mcp.hubspot.com/anthropic
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;Subagents&quot;&gt;Subagents&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;For related material, see: &lt;a href=&quot;https://code.claude.com/docs/en/sub-agents&quot;&gt;https://code.claude.com/docs/en/sub-agents&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Different agents can use different skills and MCPs, permissions, models, and context. In my view, the most important feature of a subagent is context isolation: different tasks can be isolated in separate contexts. This saves many tokens and makes model responses more precise.&lt;/p&gt;
&lt;p&gt;Because a subagent is still an agent, its capabilities are almost the same as those of the primary agent. A subagent also has independent memory for maintaining long-term context; when creating one, we can explicitly enable memory storage.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/d98bd464_7ce7a25c.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;How do we activate a subagent? There are two approaches. One is natural language in the CLI, such as &lt;code&gt;Use code reviewer to help me complete a code review&lt;/code&gt;; the other is &lt;code&gt;@&lt;/code&gt; to explicitly require Claude Code to use an agent, such as &lt;code&gt;Use @‘code reviewer(agent)’ to help me complete a code review&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After creating a subagent, its storage location is the same as skills and commands. At project level, it is stored in &lt;code&gt;project/.claude/agents/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, we can create a &lt;code&gt;code-reviewer&lt;/code&gt; subagent to review code independently of the primary context.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/93981e05_7290ccbd.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;As shown, the system can select the appropriate subagent based on natural language. But if the system can automatically find subagents to use, they also occupy a place in memory. In other words, more subagents are not always better; too many can, like MCPs, exhaust the context-token budget.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/5e29caec_bd6aeec9.png&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;Plugins&quot;&gt;Plugins&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;For related material, see: &lt;a href=&quot;https://code.claude.com/docs/en/plugins&quot;&gt;https://code.claude.com/docs/en/plugins&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A Plugin, as the name suggests, is a plugin. It is a collection of commands, skills, subagents, and so on. A Plugin is a solution for addressing one kind of work.&lt;/p&gt;
&lt;p&gt;This article will not elaborate on it for now. Later I will create a plugin that pulls blog posts from the Yuque website and automatically publishes them to a WeChat Official Account, a personal website, and Xiaohongshu, then demonstrate its packaging capability.&lt;/p&gt;
</content:encoded></item>
<item><title>My 2025: An Annual Review</title><link>https://wxxlamp.cn/en/2026/04/05/my-2025/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2026/04/05/my-2025/</guid><pubDate>Sun, 05 Apr 2026 09:13:00 GMT</pubDate><description>An in-depth review of personal growth in 2025, covering graduation from a Hong Kong master&apos;s program, career development, technical learning, progress against goals, lessons learned, and plans ahead.</description><content:encoded>&lt;p&gt;Lunar New Year arrived rather late this year, indirectly delaying my 2025 review.&lt;/p&gt;
&lt;p&gt;Sitting at my desk with a mixture of anxiety, regret, and confusion, I worked through last year’s diary and tried to recover its memories. I discovered far too many mistaken decisions.&lt;/p&gt;
&lt;p&gt;Overall, 2025 fell somewhat short of expectations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Although I graduated successfully with a Hong Kong master’s degree, failing to extract its full value was an absolute strategic mistake. On the positive side, it did broaden my perspective, including a deeper understanding of &lt;a href=&quot;https://wxxlamp.cn/en/2025/12/24/ai-ml-getting-started/&quot;&gt;LLM fundamentals&lt;/a&gt; and &lt;a href=&quot;https://wxxlamp.cn/en/2025/09/07/what-is-bitcion-and-blockchain/&quot;&gt;blockchain&lt;/a&gt;, though I failed to convert that knowledge into concrete outcomes.&lt;/li&gt;
&lt;li&gt;At work, I thought more deeply about the &lt;a href=&quot;https://wxxlamp.cn/en/2026/01/04/cross-border-ecommerce-transaction-flow/&quot;&gt;global-sales business&lt;/a&gt;, the connection between myself and the business, technology-driven progress, and AI enablement. I can feel that my understanding of work has advanced again compared with &lt;a href=&quot;https://wxxlamp.cn/en/2025/02/16/ali-3-years-thought/&quot;&gt;three years ago&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Regarding the future, I now understand my abilities more clearly. I used to want to chase every trend: self-media, videos, selling courses, and everything else. After flailing around for a long time, I realized that my skill tree simply does not point in that direction, so I have finally let it go. Concentrating on writing suits me better.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As usual, I will begin with a detailed review of 2025.&lt;/p&gt;
&lt;h1 id=&quot;2025年目标达成情况&quot;&gt;Progress on My 2025 Goals&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;At work, organize the full business chain and knowledge of cross-border transactions and fund fulfillment, and write one or two documents.&lt;/strong&gt; Essentially complete. I spent substantial time documenting transaction flows, fund accounting, and other business logic. This should become a meaningful asset for my future.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In development, complete my approach to hot-spot systems under high concurrency and write two to four documents.&lt;/strong&gt; 20%. At the end of 2025, I did read many relevant articles, including material on consistency and performance in concurrent scenarios, but I never turned the learning into documents.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Technically, master the foundations from ML through DL, NLP, and LLMs; build a mature product with Spring AI or LangChain; and write one or two documents.&lt;/strong&gt; 40%. I produced one document on LLM fundamentals but did not create a mature product.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In education, earn my Hong Kong master’s degree.&lt;/strong&gt; 100%. I graduated on October 15. A year of master’s study, IELTS preparation, and applications now feels like a dream.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Professionally, reach the next level, measured through performance. With no other major distraction, go all in at work.&lt;/strong&gt; Basically complete. My S1 result was good, and my annual rating should be at least 3.5+.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In personal finance, average a 3% annual return, specifically by converting part of my funds into US dollars for fixed-term deposits.&lt;/strong&gt; I was cautious about the unknown. The renminbi depreciated in the second half, while most of my principal remained in the bank, leaving a miserable return below 2%.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In mindset, focus on one or two important things and stop scattering my attention. Put concrete plans into action immediately.&lt;/strong&gt; Looking back, this goal was too vague to have much value.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In routine, sleep at 12:30 a.m. and wake at 8:30 a.m.&lt;/strong&gt; A complete 0%. My schedule was terrible and even affected my energy during the day.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In exercise, run 700 km during the year.&lt;/strong&gt; 90%. I ran 650 km; the target was slightly misjudged.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&quot;2025年复盘总结&quot;&gt;Reviewing 2025&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;There were only two main missions in 2025: graduate successfully from UST and produce better results at work.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;2025 was the Year of the Snake. On New Year’s Day, I hiked Hong Kong’s Snake Hill with Brother Ji. I no longer remember exactly where it was, but we had a good time. January always has the highest forgetting coefficient, so I recall only being surrounded by chores: buying New Year supplies, arranging a Hong Kong bank card, and worrying about my younger brother’s affairs. A business trip to Hangzhou at the end of the month at least gave me a few quiet days before the holiday.&lt;/p&gt;
&lt;p&gt;Then I went home for New Year. The 2025 Spring Festival was hardly relaxing: as soon as I arrived, every kind of matter began revolving around me, and many things I had planned for the holiday went unfinished. At first, I was deeply frustrated by my failure to follow the plan, but gradually I became numb and silently permitted myself not to finish it—which is not a good outcome. During the holiday, I thought: &lt;em&gt;&lt;u&gt;I often attribute flaws in my personality to family education, but after thirty that may no longer be a good excuse. By then, I will have spent half my life away from home and must bear the cost of every outcome myself.&lt;/u&gt;&lt;/em&gt; The middle and latter parts of February were extremely difficult, chiefly because of the Batan PayPal project. Going from a beginner to launching the entire chain—from merchant onboarding and payment through settlement and accounting—nearly overwhelmed me. Yet I also gained a great deal from it.&lt;/p&gt;
&lt;p&gt;In March, I reconsidered my direction and hoped to shift the center of my work toward AI while keeping up self-media operations. I did little concrete work, however. Looking back from 2026, I give the decision itself full marks, but my execution after March lagged badly. Other than bringing my WeChat public account close to one thousand followers, neither initiative produced substantial progress, especially AI.&lt;/p&gt;
&lt;p&gt;March and April brought not only work but weekly travel between Hong Kong and Shenzhen for classes. The large-model and NLP courses nearly broke me. As a beginner who could not even configure a Python environment, I suddenly had to run LLM inference in Python. During early assignments, almost half my time went into learning prerequisites and configuring the environment. Worse, I ran the code on a Thunderobot laptop bought in 2017. Its fan mixed with the hot cicadas outside and repeatedly made me restless. Fortunately, I could use the company’s GPU resources for free. Thanks to Alibaba and Bailian, I obtained a 32-core GPU on weekends to finish the assignments. In the process, I learned how to &lt;a href=&quot;https://github.com/wxxlamp/llm-fine-tuning&quot;&gt;perform SFT on a model&lt;/a&gt; and about &lt;a href=&quot;https://github.com/wxxlamp/nlp_individual_project&quot;&gt;model classification&lt;/a&gt;. The gains were substantial.&lt;/p&gt;
&lt;p&gt;May was filled with deadlines for projects across all my courses. I spent almost every day on assignments and, for the first time, felt that I might not withstand the load. Staying up late every night made it impossible to focus all my energy on work. When the assignments ended in late May, I fell back into indecision: what was my career plan? Should I take advantage of studying in Hong Kong to remain there for work, or go to Singapore?&lt;/p&gt;
&lt;p&gt;From June through September, I shifted my attention back to work. Several major events occurred:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The department’s collaboration model changed. Our group brought in many outsourced employees, and I had to coordinate with and train two or three of them while completing my own work. During this period, I worked intensely from 9:50 a.m. to 10:30 p.m. almost every day and felt physically drained. Rather than passively enduring it, I proactively asked my manager to clarify how many person-days belonged to outsourced work and loosen the allocation for my own business. This gave me room to breathe and let me train the outsourced colleagues more effectively to accept requirements.&lt;/li&gt;
&lt;li&gt;My business scope also changed. Because another colleague planned to leave, I additionally took over fund-related requirements covering payments and settlement. It was challenging, but the new scenarios taught me more business knowledge. This was one important reason I earned a good S1 rating.&lt;/li&gt;
&lt;li&gt;I began formally mentoring someone for the first time, four years after graduation. The intern was only a year younger than me but carried the unmistakably dazed air of a student. I believe I guided him attentively, and he successfully passed his conversion defense in September.&lt;/li&gt;
&lt;li&gt;I used company travel funding for a business trip to South Korea, my first trip abroad. Shenzhen is a remarkable place. Since moving there in 2024, I have visited Hong Kong and South Korea and encountered different lifestyles and working cultures. The world feels full of possibilities.&lt;/li&gt;
&lt;li&gt;I organized a team self-driving trip for the first time. The intern, outsourced colleagues, other teammates, and I drove Guangdong’s Route 318—an amazing journey.&lt;/li&gt;
&lt;li&gt;I wrote several popular articles. &lt;a href=&quot;https://wxxlamp.cn/en/2025/08/15/how-to-use-mac/&quot;&gt;How Programmers Should Configure a MacBook Pro&lt;/a&gt; produced more than 30,000 views on my WeChat public account for the first time, a small breakthrough. I followed it with articles on blockchain and the digital renminbi, which also performed well.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;October brought graduation, finally closing my year as a Hong Kong master’s student. I even received the school’s Top Student Reward scholarship. At work, I entered the S1 review and received a decent result. I had wanted to use that momentum to keep charging ahead, but at the end of the month I learned that our business had been assigned to another manager, who brought several trusted lieutenants with him. The situation instantly felt complicated.&lt;/p&gt;
&lt;p&gt;I spent the remaining time preparing again for the early-November professional software exam. The result shocked me: I passed comprehensive knowledge and case analysis, but failed the essay section I had considered certain. I was upset for a long time. It felt as if my effort had brought no return, and I even formed the illusion that no amount of work would let me succeed. My confidence took a real blow. Must everyone pass through such a stage in life? I remembered a sentence by Wang Xiaobo and the person I was on my twentieth birthday. Back then, I wanted to become a cloud in the sky. Now I seemed to have been slowly hammered flat.&lt;/p&gt;
&lt;p&gt;I should have studied AI seriously and prepared paid courses on agent development. Curiosity distracted me instead: in late November and early December, I served as a teaching assistant for UST’s blockchain lecturer. He was Indian and had a strong accent, making communication difficult. The experience also led me to revisit &lt;a href=&quot;https://wxxlamp.cn/en/2025/09/07/what-is-bitcion-and-blockchain/&quot;&gt;blockchain and Ethereum&lt;/a&gt;, and overall I learned something. In hindsight, though, the path had drifted. I should have gone all in on agents then and at least completed &lt;a href=&quot;https://wxxlamp.cn/en/2025/03/07/my-2024/&quot;&gt;the goal I set in 2024&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To summarize simply, 2025 had too many branching paths. I departed from my 2024 goals and seriously lost touch with AI agents, though I understood LLM fundamentals reasonably well. This affected a series of decisions in 2026.&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&quot;2026年Flag&quot;&gt;Goals for 2026&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;At work, find a new direction and land it successfully, integrating AI coding and AI-assisted work fully into my job.&lt;/li&gt;
&lt;li&gt;Technically, discover and resolve latent transaction-consistency problems in the system.&lt;/li&gt;
&lt;li&gt;In AI, master the internals of one agent framework, such as Spring AI, LangChain, or Eino, and understand how coding agents work.&lt;/li&gt;
&lt;li&gt;In writing, force myself to collaborate and publish two WeChat public-account articles each month. &lt;strong&gt;This is the most important goal.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;In education, obtain the professional software certificate.&lt;/li&gt;
&lt;li&gt;In personal finance, allocate 10% of my assets to US stocks and target a 3% return.&lt;/li&gt;
&lt;li&gt;In exercise, run 700 km.&lt;/li&gt;
&lt;li&gt;In health, undergo gastroscopy and colonoscopy and establish a 12:30 a.m.–8:30 a.m. sleep schedule. Stop sitting for long periods and reduce the fat around my lower abdomen.&lt;/li&gt;
&lt;li&gt;In focus, keep phone use below two hours on more than twenty working days.&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item>
<item><title>How Large Models and Agents Are Democratizing Technology</title><link>https://wxxlamp.cn/en/2026/01/10/llm-agent-tech-equality/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2026/01/10/llm-agent-tech-equality/</guid><pubDate>Sat, 10 Jan 2026 07:42:00 GMT</pubDate><description>A Git branch-contamination incident illustrates how large models and agents can democratize technology, enabling non-specialists to solve complex technical problems efficiently.</description><content:encoded>&lt;p&gt;I really do not know Git very well.&lt;/p&gt;
&lt;p&gt;During campus-recruitment interviews, I could talk circles around interviewers and casually reel off commands such as &lt;code&gt;merge&lt;/code&gt; and &lt;code&gt;revert&lt;/code&gt; over the phone. But honestly, I had barely used them.&lt;/p&gt;
&lt;p&gt;After more than four years of work, I still knew only the five basic moves: &lt;code&gt;pull&lt;/code&gt;, &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;commit&lt;/code&gt;, &lt;code&gt;checkout&lt;/code&gt;, and &lt;code&gt;push&lt;/code&gt;. I rarely encountered complex Git commands or collaboration workflows in daily work, so I had little motivation to study them carefully.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/d951595f_f58e3da3.png&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;But if you often walk beside the river, eventually your shoes get wet.&lt;/p&gt;
&lt;p&gt;At around 10 p.m. last night, a teammate, Xiao A, committed and pushed the code for his feature branch as usual. After pushing to the test branch, he predictably encountered conflicts: with year-end approaching, no fewer than five feature branches had been merged into the test branch while awaiting deployment to the test environment.&lt;/p&gt;
&lt;p&gt;This situation is common. Xiao A pulled the remote test branch, intending to merge his feature branch into it. Instead, with one slip, he merged hundreds of commits from the test branch into his own feature branch.&lt;/p&gt;
&lt;p&gt;He did not yet realize how serious the situation was—or even that there was a problem—and continued committing code locally on his feature branch.&lt;/p&gt;
&lt;p&gt;After several rounds of local commits, once he was satisfied that the code worked and prepared to &lt;code&gt;push&lt;/code&gt; it to the remote test branch, he suddenly discovered that the test branch had contaminated his branch. His feature was due for testing and release the next day; he could hardly deploy everyone else’s test code along with it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/65798b24_d3d68755.png&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Xiao A began to panic.&lt;/p&gt;
&lt;p&gt;Like me, he did not know Git particularly well. After struggling for an hour, he still could not remove the unwanted commits from his local feature branch.&lt;/p&gt;
&lt;p&gt;With no better option, he created a new branch and &lt;code&gt;cherry-pick&lt;/code&gt;ed his earlier commits onto it. By the time he finished, it was already past midnight. Exhausted, he took a taxi home under the moonlight.&lt;/p&gt;
&lt;p&gt;The incident caused no major damage, but I was sure there had to be a way to remove the junk &lt;code&gt;commit&lt;/code&gt; history from the original branch. The next morning, I opened GPT.&lt;/p&gt;
&lt;p&gt;As I typed, “Branch A accidentally &lt;code&gt;merge&lt;/code&gt;d branch B. How can I remove multiple &lt;code&gt;commit&lt;/code&gt;s from B?” and prepared to press Enter, a thought occurred to me: “Could I just ask Claude to operate on it directly?”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/26bf29ab_3e790e50.png&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;That led to the following conversation with Claude.&lt;/p&gt;
&lt;p&gt;Me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;My Git branch accidentally merged another branch. Please roll it back to the latest commit whose committer is Xiao A.&lt;/font&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;CLAUDE:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Understood. I first need to find Xiao A’s latest commit hash and inspect the current working-tree state.&lt;/font&gt;&lt;br&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;The target commit is &lt;/font&gt;&lt;font style=&quot;color:rgb(38, 38, 38);background-color:rgba(0, 0, 0, 0.06);&quot;&gt;commit_hash_A&lt;/font&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;. I also found that an interactive rebase is currently in progress and has merge conflicts. To roll back safely, I need to abort the current rebase and then perform a hard reset.&lt;/font&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Roll me back to this commit: a3a8f43dfa8eac801e804f76cf2c36450c717479.&lt;/font&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;CLAUDE:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;No problem. I will reset HEAD directly to that commit.&lt;/font&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Me:&lt;/font&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Can you force-push my local branch to the remote repository?&lt;/font&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;CLAUDE:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Yes. I have confirmed that the current branch is &lt;/font&gt;&lt;font style=&quot;color:rgb(38, 38, 38);background-color:rgba(0, 0, 0, 0.06);&quot;&gt;feature_1&lt;/font&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;. Because we just rolled it back, the local branch is behind the remote and requires a force push.&lt;/font&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Me:&lt;/font&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;The remote repository still contains the earlier merge commits and differs from my local branch. How do I remove them?&lt;/font&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;CLAUDE:&lt;/font&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Let me compare the commit-history differences between the local &lt;/font&gt;&lt;font style=&quot;color:rgb(38, 38, 38);background-color:rgba(0, 0, 0, 0.06);&quot;&gt;feature_1&lt;/font&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt; and the remote branch.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;The analysis shows that although the local pointer is correct, the remote history contains many nonlinear merge commits automatically produced by the build system. We need to clean up the timeline.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Please confirm your cleanup strategy:&lt;/font&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Completely discard&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt; every commit after the target?&lt;/font&gt;&lt;/li&gt;
&lt;li&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Or &lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;retain&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt; normal code commits after the target while removing only the merge commits?&lt;/font&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;Me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;I want to retain the normal commits after a3a8f43dfa and remove only the merge commits—the commits not made by Xiao A.&lt;/font&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;CLAUDE:&lt;/font&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Understood. This is a complex “rebase cleanup” task. &lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Strategy&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;: create a temporary branch from the clean commit, “pick out” (&lt;code&gt;cherry-pick&lt;/code&gt;) Xiao A’s valid commits from the old branch, append them to the new branch, and finally replace the original branch.&lt;/font&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Create a backup and temporary branch&lt;/font&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Select and migrate Xiao A’s commits&lt;/font&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.88);&quot;&gt;Replace the main branch and push&lt;/font&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/33d938e1_d745243d.png&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;From asking Claude the first question to its completing the entire job took less than ten minutes. It was fast and effective. At that moment, I truly felt that technology had been democratized.&lt;/p&gt;
&lt;p&gt;Our use of large models should not stop at conversation. With capabilities such as MCP and RAG integrated into agents, a well-written prompt can enable them to complete a great many tasks for us.&lt;/p&gt;
&lt;p&gt;Inside the company, we have also used large models and coding agents to generate code autonomously. Many teams are actively exploring autonomous ticket diagnosis, integration testing, and other capabilities based on large models and agents.&lt;/p&gt;
&lt;p&gt;Individuals should likewise use agents to automate routine chores. Our working model may eventually change: learning how to talk with an agent and supervise it may become an important subject.&lt;/p&gt;
&lt;p&gt;The foundational capabilities of large models—what we usually call pre-training—matter greatly. Some people say post-training matters too, and I do not disagree. &lt;strong&gt;In the consumer market, however, agent capability will be the decisive factor for many AI products.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I believe that helps explain why Meta spent a billion dollars acquiring a company that did no model training and focused only on agents. It also helps explain why Alibaba’s Qwen, despite becoming the leading open-source model worldwide, still cannot outperform Doubao in the consumer market.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/5599e669_fb6d2a35.png&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Finally, returning to today’s topic: how can you quickly remove a branch that was merged into your Git history? See the image below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/94da7da8_f317e49c.png&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: the images in this article were generated with Gemini Nano Banana Pro.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item>
<item><title>How Cross-Border E-Commerce Transactions Work</title><link>https://wxxlamp.cn/en/2026/01/04/cross-border-ecommerce-transaction-flow/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2026/01/04/cross-border-ecommerce-transaction-flow/</guid><pubDate>Sun, 04 Jan 2026 06:16:00 GMT</pubDate><description>An in-depth analysis of the full fulfillment flow on cross-border e-commerce platforms. It examines how information, funds, and logistics work together from buyer and seller perspectives, and reveals platform profit models and fund-settlement systems.</description><content:encoded>&lt;p&gt;As someone who spends time surfing the Internet, I use online shopping platforms such as Taobao, JD, and Pinduoduo for everyday purchases.&lt;/p&gt;
&lt;p&gt;Since changing roles in August 2024, I have been responsible for online-shopping business similar to Taobao and JD, but with two differences. First, Taobao and JD are B2C shopping platforms for ordinary consumers, while the code I write serves B2B trade platforms. Second, Taobao and JD serve domestic merchants and consumers, whereas I work with overseas merchants and overseas consumers every day.&lt;/p&gt;
&lt;p&gt;Over the past year, the code I developed has covered merchant onboarding, transactions, orders, safeguards, payments, settlement, liquidity, accounting, gateways, and other domains. As my first article of 2026, this is a summary of roughly the past year of work. It also explains the complete cross-border transaction-fulfillment flow.&lt;/p&gt;
&lt;h1 id=&quot;买卖家在电商平台要干啥&quot;&gt;What buyers and sellers do on e-commerce platforms&lt;/h1&gt;&lt;p&gt;The essence of online transactions on e-commerce platforms is managing the flow of information, funds, and logistics: 1) transactions carry the information agreed by buyers and sellers; 2) funds legally and compliantly transfer buyers’ payment to sellers’ cards; and 3) logistics safely transports sellers’ goods to the locations buyers specify.&lt;/p&gt;
&lt;p&gt;Below is a brief summary from buyer-seller and platform perspectives.&lt;/p&gt;
&lt;h2 id=&quot;买卖家视角——交易的生命周期&quot;&gt;Buyer and seller perspective — the transaction lifecycle&lt;/h2&gt;&lt;p&gt;The end-to-end view from buyers’ and sellers’ perspective is as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/5606db74_ab1a042e.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;The preceding diagram shows a &lt;strong&gt;release of funds&lt;/strong&gt; action after the buyer completes payment. Payment cannot go directly into the seller’s account because we cannot guarantee that the seller will fulfill normally afterward. If you have noticed, when you confirm receipt on Taobao, you are asked to enter your payment password (or complete Alipay facial verification). That is because for Taobao, your money is truly paid to the merchant only when you confirm receipt.&lt;/p&gt;
&lt;p&gt;From buyer and seller perspectives, the following are the main action points:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/59b28626_e8a5f5e5.png&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;平台视角——平台在交易中的角色&quot;&gt;Platform perspective — the platform’s role in a transaction&lt;/h2&gt;&lt;p&gt;From the platform’s perspective, the key is to connect the flows in different business domains across transaction and fund stages. A simplified flowchart is as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/3ebf4508_2a52ab59.png&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;电商的参与主体和盈利模式&quot;&gt;E-commerce participants and profit models&lt;/h1&gt;&lt;h2 id=&quot;参与主体——交易背后的很多双眼睛&quot;&gt;Participants — many pairs of eyes behind a transaction&lt;/h2&gt;&lt;p&gt;For ordinary consumers, this is simply a transaction completed by buyer and seller on a platform. From the platform’s perspective, though, transaction participants include not only the platform and buyer and seller, but also various intermediary funding channels.&lt;/p&gt;
&lt;p&gt;Taking a basic cross-border online trading platform as an example: if the platform is not licensed to handle buyers’ funds, then in addition to buyers, sellers, and the platform, an extra funding center is needed. The arrangement is as follows:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/1afff81c_7693194e.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once a platform has the license to hold funds, it often has the acquiring institution settle funds to the platform to strengthen its control over funds; the platform then settles those funds to sellers worldwide through a payout institution.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/367b905e_407deea6.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Every participant should be bound by an agreement with the others.&lt;/p&gt;
&lt;p&gt;Buyers need an agreement with the platform to purchase goods and pay for them; merchants need an agreement with the platform to trade goods and withdraw funds; Adyen, as the acquiring channel, also needs a corresponding acquiring agreement with the platform; Payout, as the payout channel, needs corresponding payment-service agreements with both the platform and merchants. If there is an FX institution (used to exchange foreign currencies into USD), that institution must also sign an agreement with the platform.&lt;/p&gt;
&lt;h2 id=&quot;盈利模式——平台是如何赚钱的&quot;&gt;Profit models — how platforms make money&lt;/h2&gt;&lt;p&gt;From a consumer perspective, online transactions can charge merchants a transaction service fee on every transaction. In cross-border scenarios, however, platforms have many ways to profit:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Transaction dimension&lt;ol&gt;
&lt;li&gt;Transaction service fee: roughly a 3% technical service fee is charged to merchants for each transaction. This revenue belongs entirely to the platform; accordingly, the platform must issue an invoice to the merchant.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Payment dimension&lt;ol&gt;
&lt;li&gt;Payment processing fee: each payment charges the payer (buyer) a 3% fee (depending on the payment instrument). Unlike domestic payments, overseas payment instruments generally charge the payer a percentage on every payment (credit cards are basically 2%, for example). The platform shares revenue with the payment institution under a fixed rule.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Withdrawal dimension&lt;ol&gt;
&lt;li&gt;Withdrawal fee: every time a merchant withdraws funds from its platform balance account to a bank card, it is charged a fee at a fixed rate or amount. The platform generally collects it on behalf of the provider, then shares it with the payout institution. This applies both domestically and internationally: merchants pay fees whenever they withdraw. For example, Alipay and WeChat both charge a 0.1% withdrawal fee.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Value-added services&lt;ol&gt;
&lt;li&gt;Insurance premium: the platform can help merchants insure goods and share premiums with the insurance company.&lt;/li&gt;
&lt;li&gt;Loan interest: mature platforms partner with banks to offer loans to merchants, splitting loan interest with the channel.&lt;/li&gt;
&lt;li&gt;Collection service fee: by providing factoring institutions, platforms help merchants collect funds rapidly, sharing the service fee with the institution.&lt;/li&gt;
&lt;li&gt;Deposit: merchants often pay the platform a deposit when opening a store, used for deductions in subsequent after-sales disputes.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Merchant dimension&lt;ol&gt;
&lt;li&gt;Merchants joining Alibaba International Station as members need to pay a membership fee.&lt;/li&gt;
&lt;li&gt;Merchants can purchase many kinds of services to increase the exposure of their stores and themselves, thereby improving conversion. This is the profit core of virtually every online platform, such as Taobao’s Alimama and Douyin’s Ocean Engine.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Logistics dimension&lt;ol&gt;
&lt;li&gt;Merchants pay the platform a logistics fee when shipping; the fee is shared with logistics companies.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/88f308d8_55e4f92c.png&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;跨境资金清结算&quot;&gt;Cross-border fund clearing and settlement&lt;/h1&gt;&lt;p&gt;Transactions represent information flow; another important part is fund flow. At the lowest level, fund flow is the movement of money among financial institutions, accompanied by debit-credit relationships among accounts. Fund transfers and accounting give rise upstream to business scenarios such as merchant onboarding, payment, settlement, withdrawal, transfers, and foreign exchange.&lt;/p&gt;
&lt;h2 id=&quot;资金链路——你的钱是怎么到商家口袋的&quot;&gt;Fund flow — how your money reaches the merchant’s pocket&lt;/h2&gt;&lt;p&gt;For a cross-border platform in its early construction phase, an acquiring platform can provide USD settlement capability to get the site running quickly, so there is no need to introduce an FX institution. Note that because this is a cross-border platform, under the current system of U.S.-dollar dominance, USD is the default preferred settlement currency.&lt;/p&gt;
&lt;p&gt;The fund flow is shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/129fbdba_99a72499.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase one:&lt;/strong&gt; After placing orders, buyers around the world enter the payment process. They pay the acquiring institution (for example, Adyen) through CREDIT CARD, GOOGLE PAY, APPLY PAY, PAYPAL, and other local payment methods. Adyen then performs internal currency conversion and settles the funds to the platform entity at T+1.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&gt; &lt;strong&gt;Phase two:&lt;/strong&gt; Through a T+1 transfer, the platform moves money settled to it by the acquiring institution into the account it opened with the payout institution, in fixed amounts according to merchants’ withdrawal amounts. Merchants can then withdraw money from their platform balance accounts directly into their own accounts. To ensure merchants can withdraw quickly, the project uses prefunding: before the project operates, funds are advanced to the payout institution through a treasury account.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After the platform develops and matures, it will certainly add multiple acquiring institutions to introduce more payment instruments. When an acquirer does not support USD settlement, the platform needs an FX institution to convert other currencies to USD and settle them to the platform. The fund flow then becomes:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.nlark.com/yuque/0/2026/png/719664/1767518918849-342aa652-3e86-4fbb-ac9c-4fd212fede98.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;As the diagram shows, the platform must now recognize the user’s payment currency and complete its own rate lock and conversion from LCY to USD.&lt;/p&gt;
&lt;h2 id=&quot;账务能力——平台是如何记钱的&quot;&gt;Accounting capabilities — how a platform records money&lt;/h2&gt;&lt;p&gt;Since money must move and be transferred N times from buyers to merchants, how do we ensure that it is recorded correctly? This requires understanding a platform’s accounting capability, namely how it records money. The platform creates N virtual accounts internally to represent flows among different accounts. Internet companies generally use double-entry bookkeeping; interested readers can explore it further. Here are several classic account types:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pending-association account&lt;/strong&gt;: after a buyer pays, funds are recorded in the pending-association account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Escrow account&lt;/strong&gt;: after the system associates the funds with an order, the merchandise-payment portion moves from the pending-association account to the escrow account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Revenue account&lt;/strong&gt;: the system moves the platform’s receivable portion (transaction service fees, processing fees, and so on) to the revenue account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tax account&lt;/strong&gt;: the system moves taxes withheld and remitted for merchants (such as EPR and GST) to the tax account and reports them to the corresponding authorities on schedule.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Merchant balance account&lt;/strong&gt;: after a merchant fulfills, money moves from the escrow account to the merchant balance account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dedicated withdrawal account&lt;/strong&gt;: after a merchant initiates a withdrawal, money in the balance account moves to the dedicated withdrawal account.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refund transit account&lt;/strong&gt;: after a buyer requests a refund, funds are gathered into the refund transit account through fund preparation and paid to the payment channel.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is a classic flowchart (including forward and reverse flows) for a more detailed view:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/b01fb88d_b3430345.png&quot;&gt;&lt;/p&gt;
</content:encoded></item>
<item><title>Machine Learning for Backend Developers in the Age of Large Models</title><link>https://wxxlamp.cn/en/2025/12/24/ai-ml-getting-started/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2025/12/24/ai-ml-getting-started/</guid><pubDate>Wed, 24 Dec 2025 13:50:00 GMT</pubDate><description>An introduction to machine learning from a backend developer&apos;s perspective, clarifying the relationships among AI, ML, DL, and LLMs; explaining neural networks, CNNs, RNNs, embeddings, forward propagation, backpropagation, and other core concepts; with Python examples.</description><content:encoded>&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/0faf3c20_img1.png&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;前言&quot;&gt;Preface&lt;/h1&gt;&lt;p&gt;From the emergence of the Transformer architecture in 2017 to the arrival of GPT in 2022, people have increasingly recognized the importance of large models, to the point that every year seems to be proclaimed the inaugural year of LLMs. As we gradually enter the AI era, I feel both deeply anxious, worried that large models might replace me, and excited by the prospect of doing more with LMs.&lt;/p&gt;
&lt;p&gt;But what can I do with LLMs? And what do I need to understand?&lt;/p&gt;
&lt;p&gt;Before the age of large models, backend developers could generally start building software once they had mastered a programming language such as Java, Go, or JavaScript, along with architectural knowledge of MySQL and Redis. In the AI era—or, more precisely, the age of large models—knowing only conventional programming languages is clearly no longer enough.&lt;/p&gt;
&lt;p&gt;LLM engineering can be summed up in one sentence: using all kinds of prompts to call LLMs, accompanied by engineering capabilities such as MCP and RAG. If you want to quickly embed LM capabilities into a business scenario, it is essentially the same as being a CRUD developer in the old days: simply call the open APIs provided by large-model vendors.&lt;/p&gt;
&lt;p&gt;Yet merely calling a model’s API is like looking at the moon reflected in water or flowers reflected in a mirror: you never truly see the essence or the whole system. In my view, for backend developers who want to become part of the AI era, the first step should not be calling LLM-related APIs. It should be understanding the concepts and principles behind traditional artificial intelligence and machine learning. What exactly is the relationship between ML and DL? How did architectures such as CNNs, RNNs, and Transformers evolve? Without this basic knowledge, it is like programming without understanding operating systems or computer networks—you can complete basic development tasks, but cannot go further and glimpse the wonders of the computing world.&lt;/p&gt;
&lt;p&gt;So I spent some time organizing my understanding of artificial intelligence and machine learning around a number of common questions.&lt;/p&gt;
&lt;h1 id=&quot;你的AI我的AI好像不一样&quot;&gt;Your AI and My AI Seem to Be Different&lt;/h1&gt;&lt;p&gt;&lt;font style=&quot;color:rgb(31, 31, 31);&quot;&gt;In recent years, with the rise of large models, everything has become AI. In the narrow sense, AI means artificial intelligence, and the people primarily responsible for implementing it are called “algorithm engineers” (often jokingly called parameter tuners). In the age of large models, many people use large models as a synonym for AI. Luo Yonghao, for example, has consistently argued that the true AI era did not arrive until 2023. So what are the relationships and differences among artificial intelligence, machine learning, deep learning, and large models?&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(31, 31, 31);&quot;&gt;According to Hung-yi Lee’s framework, we generally regard Artificial Intelligence (AI) as the ultimate goal and Machine Learning (ML) as the primary means of achieving it. Computer Vision (CV), Natural Language Processing (NLP), and similar fields are specific applications.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(31, 31, 31);&quot;&gt;From a layperson’s perspective, ML is an umbrella term whose core logic is to “train models with data so that computers learn patterns independently and complete specific tasks.” Among the technical branches of ML, Deep Learning (DL) is currently the most central and mainstream direction. Deep learning uses “deep neural networks” as its core architecture, with multiple layers containing many neurons that learn. In increasing order of complexity, neural networks include basic Feedforward Neural Networks (FNNs), Recurrent Neural Networks (RNNs) for sequential data, and Convolutional Neural Networks (CNNs) for spatial data. Google’s Transformer architecture restructured the sequential processing principles used by RNNs and CNNs. By adopting a Self-Attention mechanism, it addressed sequence dependency problems, allowing a model both to perceive relationships among tokens and to train in parallel.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(31, 31, 31);&quot;&gt;As for Large Models (LMs), “large” refers not only to their number of parameters but also to the volume of data required for training. It was the Transformer that made scaling models to this size possible. One of the earliest branches of large models was the Large Language Model (LLM), which originated in NLP and is suited to conventional NLP tasks such as text generation. With the development of multimodal LMs, however, large models are no longer confined to NLP; in pursuit of the broader goal of AI, they have reached into every kind of application.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/7b498439_img2.png&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;机器学习能干什么事&quot;&gt;What Can Machine Learning Do?&lt;/h1&gt;&lt;p&gt;At the application level, ML-based LLMs can generate text from text, images from text, text from images, and more. At their root, however, all application tasks ultimately return to two categories: “regression” and “classification.”&lt;/p&gt;
&lt;p&gt;Regression is similar to y=kx+b: different inputs produce different values. Regression tasks are commonly used for prediction, such as weather forecasts, house prices, and stock trends. Classification builds on regression and uses an activation or classification function to divide results into categories, such as positive and negative. Classification tasks are commonly used for labeling.&lt;/p&gt;
&lt;p&gt;The recommendation component in search, recommendation, and advertising systems is a classification task. As is widely known, although a large model generates text according to predicted probabilities, it is essentially predicting the next word (Token) from a vocabulary of, say, 50,000 words. Large models therefore perform classification tasks.&lt;/p&gt;
&lt;h1 id=&quot;模型到底是个什么东东&quot;&gt;What Exactly Is a Model?&lt;/h1&gt;&lt;p&gt;With the widespread adoption of large models, almost everyone has heard the word “model.” But what exactly is one?&lt;/p&gt;
&lt;p&gt;At a high level, a model is a black box. You provide input to the model, computations take place inside it, and it ultimately returns an output.&lt;/p&gt;
&lt;p&gt;Looking at the open-source model repository on Hugging Face, a model looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/a97f77f1_img3.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Inside a model is a collection of parameters (weights). What we call the model’s thinking process is essentially a series of vector operations—&lt;strong&gt;mainly matrix multiplication&lt;/strong&gt;—performed together with the input tokens. In the age of large models, we can narrow our focus and think of the model as a neural network composed of many neurons. So what exactly is a neuron?&lt;/p&gt;
&lt;h1 id=&quot;神经元和神经网络&quot;&gt;Neurons and Neural Networks&lt;/h1&gt;&lt;h2 id=&quot;神经元&quot;&gt;Neurons&lt;/h2&gt;&lt;p&gt;A neuron can be understood simply as a computational unit. Given an input x, the neuron might compute wx+b, and its calculated output can be y. The difference is that x is not a single variable of the kind encountered in high-school mathematics, but a one-dimensional vector made up of n variables related to the input:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(y = \sigma(w_1 x_1 + w_2 x_2 + b)\)&lt;/span&gt;, that is:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(y = \sigma([w_1, w_2] \cdot \begin{bmatrix} x_1 \\ x_2 \end{bmatrix} + b)\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In the expression above, &lt;span class=&quot;math-inline&quot;&gt;\(x_1\)&lt;/span&gt; and &lt;span class=&quot;math-inline&quot;&gt;\(x_2\)&lt;/span&gt; are input features. For example, in a weather forecasting model, &lt;span class=&quot;math-inline&quot;&gt;\(x_1\)&lt;/span&gt; might be humidity and &lt;span class=&quot;math-inline&quot;&gt;\(x_2\)&lt;/span&gt; might be temperature. &lt;span class=&quot;math-inline&quot;&gt;\(w_1\)&lt;/span&gt; and &lt;span class=&quot;math-inline&quot;&gt;\(w_2\)&lt;/span&gt; are the variables’ weights, also known as “parameters.” Training means continually adjusting the values of &lt;span class=&quot;math-inline&quot;&gt;\(w\)&lt;/span&gt; (and other parameters) so that the output &lt;span class=&quot;math-inline&quot;&gt;\(y\)&lt;/span&gt; approaches the true value and the loss becomes lower. &lt;span class=&quot;math-inline&quot;&gt;\(b\)&lt;/span&gt; is the bias—or, in simpler terms, the intercept—which can increase or decrease the computed value. The symbol outside the parentheses is sigma (&lt;span class=&quot;math-inline&quot;&gt;\(\sigma\)&lt;/span&gt;), representing the activation function. Common activation functions include Sigmoid (&lt;span class=&quot;math-inline&quot;&gt;\(\sigma(x) \in (0,1)\)&lt;/span&gt;) and ReLU (&lt;span class=&quot;math-inline&quot;&gt;\(relu(x) = max(0,x)\)&lt;/span&gt;). Activation functions control the output and make the result nonlinear.&lt;/p&gt;
&lt;p&gt;A visual representation of a neuron is shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/f8365a77_img4.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;A simple neuron can be expressed in Python as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import torch

def simple_neuron_torch(x, w, b, activation=torch.sigmoid):
    z = torch.dot(x, w) + b  # 点积是标量
    return activation(z) # 通过sigmoid激活

x = torch.tensor([28.0, 75.0])  # 今天：温度28°C，湿度75%
w = torch.tensor([0.5, 0.5])   # 权重：高温抑制下雨(-0.5)，高湿度促进下雨(+0.8)
b = torch.tensor(-15.0)         # 偏置：基准阈值

output = simple_neuron_torch(x, w, b)  # 得到最后的下雨概率（0.9999）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Suppose the trained parameters are &lt;span class=&quot;math-inline&quot;&gt;\(w_1=0.5, w_2=0.5, b=-40\)&lt;/span&gt;, giving &lt;span class=&quot;math-inline&quot;&gt;\(y = \sigma(0.5 x_1 + 0.5 x_2 - 40)\)&lt;/span&gt;. We obtain the prediction trend shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/b849170f_img5.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;From the diagram, we would conclude that higher temperature and higher humidity make rain more likely. Let us examine the cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Case A (sauna-like weather): &lt;span class=&quot;math-inline&quot;&gt;\(0.9 \times 0.5 + 0.9 \times 0.5 = 0.9\)&lt;/span&gt; (predicts rain, correct ✅)&lt;/li&gt;
&lt;li&gt;Case B (desert): &lt;span class=&quot;math-inline&quot;&gt;\(0.9 \times 0.5 + 0.0 \times 0.5 = 0.45\)&lt;/span&gt; (predicts no rain, correct ✅)&lt;/li&gt;
&lt;li&gt;Case C (extreme heat and dryness): suppose the temperature is exceptionally high, &lt;span class=&quot;math-inline&quot;&gt;\(x_1=2.0\)&lt;/span&gt;:&lt;ul&gt;
&lt;li&gt;&lt;span class=&quot;math-inline&quot;&gt;\(2.0 \times 0.5 + 0.0 \times 0.5 = 1.0\)&lt;/span&gt; (predicts rain, incorrect ❌)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We find that even if the trained w and b are optimal, the result is necessarily linear. Weather forecasting, however, is not a linear prediction problem. We therefore need to try other tools to solve it.&lt;/p&gt;
&lt;h2 id=&quot;全连接神经网络&quot;&gt;Fully Connected Neural Networks&lt;/h2&gt;&lt;p&gt;As we can see, a simple neuron can only perform linear regression. The real world is complex, so we need to connect multiple neurons together to perform more complex tasks.&lt;/p&gt;
&lt;p&gt;The weather forecasting example tells us that forecasting weather is not a linear prediction problem. The weather features are related, yet a single neuron cannot capture those relationships. From an engineering perspective, when in doubt, add an intermediate layer. Since temperature and humidity are related, we can associate them to form multiple intermediate features, then use those intermediate features to produce the final result, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/e0204d08_img6.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Converted into simple mathematical formulas, this becomes:&lt;/p&gt;
&lt;div class=&quot;math-display&quot;&gt;\[h_1 = \sigma(w_{11} x_1 + w_{12} x_2 + b_1) \\
h_2 = \sigma(w_{21} x_1 + w_{22} x_2 + b_2) \\
h_3 = \sigma(w_{31} x_1 + w_{32} x_2 + b_3) \\
y_{final} = \sigma(v_1 h_1 + v_{2} h_2 + v_3 h_3 + b_4)\]&lt;/div&gt;&lt;p&gt;Because the formulas above connect &lt;span class=&quot;math-inline&quot;&gt;\(x_1, x_2\)&lt;/span&gt; as completely as possible, we call this network a Fully Connected Network (FCN), which forms the foundation of modern deep learning. Applying this FCN to our humidity and temperature example, let &lt;span class=&quot;math-inline&quot;&gt;\(h_1\)&lt;/span&gt; represent sauna-like conditions, &lt;span class=&quot;math-inline&quot;&gt;\(h_2\)&lt;/span&gt; cold and humid conditions, and &lt;span class=&quot;math-inline&quot;&gt;\(h_3\)&lt;/span&gt; hot and dry conditions. Suppose training produces the following result:&lt;/p&gt;
&lt;div class=&quot;math-display&quot;&gt;\[h_1 = \sigma(x_1 + x_2 -1.2) \\
h_2 = \sigma(-x_1 + x_2 -0.5) \\
h_3 = \sigma(x_1 - x_2 - 1.0) \\
y_{final} = \sigma(2 h_1 + 0.5 h_2 - h_3 -1)\]&lt;/div&gt;&lt;p&gt;We obtain the prediction diagram below. As you can see, after adding the intermediate layer, the model no longer predicts rain under “hot and dry” conditions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/53f822d5_img7.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Returning to the mathematical formulas, if there are many features and multiple layers of neurons, the preceding notation becomes far too complicated. Using the spatial transformation properties of matrix multiplication—rotation, scaling, and distortion—we can rewrite the hidden layer as follows:&lt;/p&gt;
&lt;div class=&quot;math-display&quot;&gt;\[h = \begin{bmatrix} h_1 \\ h_2 \\ h_3 \end{bmatrix} = \begin{bmatrix}
\sigma(w_{1,1}x_1 + w_{1,2}x_2 + b_1) \\
\sigma(w_{2,1}x_1 + w_{2,2}x_2 + b_2) \\
\sigma(w_{3,1}x_1 + w_{3,2}x_2 + b_3)
\end{bmatrix} = \sigma(\begin{bmatrix}
w_{1,1} &amp;amp; w_{1,2} \\
w_{2,1} &amp;amp; w_{2,2} \\
w_{3,1} &amp;amp; w_{3,2}
\end{bmatrix} \cdot \begin{bmatrix} x_1 \\ x_2 \end{bmatrix} + \begin{bmatrix} b_1 \\ b_2 \\ b_3 \end{bmatrix}) = \sigma(Wx + b)\]&lt;/div&gt;&lt;p&gt;,&lt;/p&gt;
&lt;p&gt;The final output is therefore:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(y_{final} = \sigma( \underbrace{\begin{bmatrix} v_1 &amp;amp; v_2 &amp;amp; v_3 \end{bmatrix}}_{W_2} \cdot \underbrace{\begin{bmatrix} h_1 \\ h_2 \\ h_3 \end{bmatrix}}_{h} + b_4 )\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;When training in code, however, we want to parallelize as much of the work as possible, which introduces the concept of batching. Instead of calculating one feature sample at a time, we group samples into a batch and calculate several at once, using the following formula:&lt;/p&gt;
&lt;div class=&quot;math-display&quot;&gt;\[\begin{bmatrix}
h_{1,1} &amp;amp; h_{1,2} &amp;amp; h_{1,3} \\
h_{2,1} &amp;amp; h_{2,2} &amp;amp; h_{2,3} \\
h_{3,1} &amp;amp; h_{3,2} &amp;amp; h_{3,3} \\
h_{4,1} &amp;amp; h_{4,2} &amp;amp; h_{4,3} \\
h_{5,1} &amp;amp; h_{5,2} &amp;amp; h_{5,3}
\end{bmatrix} = \begin{bmatrix}
x_{1,1} &amp;amp; x_{1,2} \\
x_{2,1} &amp;amp; x_{2,2} \\
x_{3,1} &amp;amp; x_{3,2} \\
x_{4,1} &amp;amp; x_{4,2} \\
x_{5,1} &amp;amp; x_{5,2}
\end{bmatrix}_{\text{Data (5 days)}}
\cdot
\begin{bmatrix}
w_{1,1} &amp;amp; w_{2,1} &amp;amp; w_{3,1} \\
w_{1,2} &amp;amp; w_{2,2} &amp;amp; w_{3,2}
\end{bmatrix}_{W^T}
+
\begin{bmatrix}
b_1 &amp;amp; b_2 &amp;amp; b_3
\end{bmatrix}_{\text{Bias}}\]&lt;/div&gt;&lt;p&gt;,&lt;/p&gt;
&lt;p&gt;That is:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(h_{\text{单层输出}} = xW^T + b\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class SimpleFCN(nn.Module):
    def __init__(self):
        super(SimpleFCN, self).__init__()
        self.fc1 = nn.Linear(2, 3)
        self.fc2 = nn.Linear(3, 1)

    def forward(self, x):
        x = torch.sigmoid(self.fc1(x))
        x = torch.sigmoid(self.fc2(x))
        return x
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;CNN和RNN&quot;&gt;CNNs and RNNs&lt;/h2&gt;&lt;p&gt;With FCNs, we can theoretically use enough data to train for any result we want. But because FCNs are fully connected, they have &lt;strong&gt;three&lt;/strong&gt; major drawbacks:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Parameter redundancy and high computational cost&lt;/strong&gt;: By default, every neuron connects to every neuron in the previous layer, causing the number of parameters to rise sharply. When the &lt;strong&gt;input dimensionality is high&lt;/strong&gt;, as in high-resolution images, or the network has many layers, this can cause a &lt;strong&gt;parameter explosion&lt;/strong&gt; that exhausts the computer’s memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Local features are ignored&lt;/strong&gt;: Because every FCN computation must consider all inputs, it has no concept of “&lt;strong&gt;spatial locality&lt;/strong&gt;.” It cannot behave like the human eye, first focusing on local features such as lines and textures and then combining them into a whole. Instead, it processes all information in one undifferentiated gulp.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No awareness of “order”&lt;/strong&gt;: In an FCN, all input features are parallel, so the network cannot perceive which came “before” and which came “after.” For example, when we feed a Sentence into an FCN as Input, the FCN &lt;strong&gt;cannot effectively capture temporal dependencies among Tokens&lt;/strong&gt;, making it difficult to understand contextual meaning.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;CNN&quot;&gt;CNN&lt;/h3&gt;&lt;p&gt;A CNN, or Convolutional Neural Network, centers on a convolutional layer. Unlike an FCN, this layer does not connect every neuron to all parameters; it connects them “selectively.”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/8f5c1f10_img8.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Suppose we have a 3&lt;em&gt;3-pixel image. If we use an FCN to recognize it, we must flatten the 3&lt;/em&gt;3 pixels into nine parameters and perform fully connected linear operations on them. A single neuron can be expressed mathematically as follows:&lt;/p&gt;
&lt;div class=&quot;math-display&quot;&gt;\[\begin{bmatrix}
  x_1 &amp;amp; x_2 &amp;amp; x_3 \\
  x_4 &amp;amp; x_5 &amp;amp; x_6 \\
  x_7 &amp;amp; x_8 &amp;amp; x_9
  \end{bmatrix}
  \xrightarrow{\text{Flatten}}
  [x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8, x_9]\]&lt;/div&gt;&lt;p&gt;,&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(h = \sigma(w_{1} x_1 + w_{2} x_2 + ... + w_n x_n + ... + w_9 x_9 + b) \\\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Flattening almost completely removes positional and spatial information from the parameters. Using an FCN therefore causes not only a parameter explosion, but also a reduction in recognition accuracy.&lt;/p&gt;
&lt;p&gt;For images, preserving positional information means we cannot flatten the input pixels. We must consider the relationships among pixel values. At the same time, to avoid computing every pixel each time, we need a window so that each neuron calculates only the values inside that window. If the window is 2x2, it can be represented as follows:&lt;/p&gt;
&lt;div class=&quot;math-display&quot;&gt;\[\text{Kernel} =
  \begin{bmatrix}
  w_{1,1} &amp;amp; w_{1,2} \\
  w_{2,1} &amp;amp; w_{2,2}
  \end{bmatrix}\]&lt;/div&gt;&lt;p&gt;This Kernel is the convolution kernel. The neuron can now be expressed as:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(h = \sigma(w_{1,1} x_1 + w_{1,2} x_2 + w_{2,1} x_4 + w_{2,2} x_5 + b) \\\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As we can see, a convolution-kernel neuron has two characteristics: first, it does not process all parameters; second, it can precisely perceive position-related information.&lt;/p&gt;
&lt;p&gt;The essence of a convolution kernel is that it focuses on local features instead of all features. We can think of a convolution kernel as a local feature detector. Suppose &lt;span class=&quot;math-inline&quot;&gt;\(K_h\)&lt;/span&gt; has 2&lt;em&gt;2 parameters and is specifically responsible for recognizing “vertical lines.” &lt;span class=&quot;math-inline&quot;&gt;\(K_h\)&lt;/span&gt; slides up, down, left, and right through every 2&lt;/em&gt;2 block of pixels to find blocks that might contain a “vertical line.”&lt;/p&gt;
&lt;p&gt;An image has more than one feature, so we use multiple convolution kernels (&lt;span class=&quot;math-inline&quot;&gt;\(K_1,..., K_n\)&lt;/span&gt;), each representing a different feature. Some kernels may recognize lines, others colors, and so on.&lt;/p&gt;
&lt;p&gt;A simple CNN is shown below:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import torch
import torch.nn as nn

class CNN_Model(nn.Module):
    def __init__(self):
        super().__init__()

        # 1. 卷积层 (眼睛)
        # in_channels=1: 因为是黑白图，只有1层深
        # out_channels=32: 我们用 32 个不同的卷积核(手电筒)去扫描，提取32种不同的特征
        # kernel_size=3: 卷积核大小是 3x3
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3)

        # 2. 激活函数 (当然还要用 ReLU)
        self.relu = nn.ReLU()

        # 3. 池化层 (压缩)
        # 2x2 的窗口，把图片长宽各缩小一半
        self.pool = nn.MaxPool2d(kernel_size=2)

        # 4. 全连接层 (大脑)
        # 经过上面的卷积和池化，图片变小了，但在深度上变厚了(32层)
        # 这里需要算一下剩下的特征数量，连接到分类器
        self.fc = nn.Linear(in_features=32 * 13 * 13, out_features=10)

    def forward(self, x):
        # 保持图片的 2D 结构进行处理
        x = self.conv1(x)  # 扫描
        x = self.relu(x)   # 激活
        x = self.pool(x)   # 压缩

        # 展平，交给全连接层做最后决定
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

model = CNN_Model()
print(model)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;RNN&quot;&gt;RNN&lt;/h3&gt;&lt;p&gt;FCNs transform user-provided features into more general features, while CNNs go further by handling certain relationships among features. Neither FCNs nor CNNs, however, address relationships among features across time. Consider this scenario:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;While performing machine translation, we try to translate “Book a hotel” and “I like book” into Chinese.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If we directly train an FCN on the sentences above, each token in “Book a hotel” must be used as a training parameter. That is certainly feasible, but once the sentence to translate changes to “I like book,” the original FCN becomes completely unusable because its input parameters have a fixed shape. An FCN alone is therefore wholly inadequate when the input varies.&lt;/p&gt;
&lt;p&gt;Since training the complete sentence is impractical, we must split it into words and train on them separately. In other words, the input cannot be fed all at once as it is in an FCN; it must be fed incrementally. Yet because “book” has different meanings in different sentences, when we input the current word we must also retain some preceding words and provide them to the model as input.&lt;/p&gt;
&lt;p&gt;Can we therefore design a network that 1) dynamically perceives input tokens and 2) perceives not only the current token but previous tokens as well? That is precisely an &lt;strong&gt;RNN (Recurrent Neural Network)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/25f4cfb9_img9.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;At its core, an RNN adds a recurrent mechanism to an FCN, enabling it to perceive the preceding output. Its mathematical expression is:&lt;/p&gt;
&lt;div class=&quot;math-display&quot;&gt;\[h_{t-1} = \sigma(W_{xh} \cdot x_{t-1} + b )\\
h_t = \sigma(\underbrace{W_{xh} \cdot x_t}_{\text{处理当前输入}} + \underbrace{W_{hh} \cdot h_{t-1}}_{\text{处理过去记忆}} + b)\]&lt;/div&gt;&lt;p&gt;The formula shows that the essence of an RNN is to feed the result calculated from the previous input into the next computation as another input. The Python code is as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import torch
import torch.nn as nn

# --- 设定参数 ---
input_size = 10   # 输入特征维度 (比如今天的10个财经指标)
hidden_size = 20  # 记忆容量 (隐藏层神经元数量)

# --- 定义权重 (这就是 RNN 模型本体) ---
# 1. 处理输入的权重 (对应 x_t)
W_xh = nn.Linear(input_size, hidden_size)

# 2. 处理记忆的权重 (对应 h_t-1) &amp;lt;--- RNN 独有的！
W_hh = nn.Linear(hidden_size, hidden_size)

# --- 模拟时间序列数据 (Sequence) ---
# 假设有 5 天的数据 (Time steps = 5)
inputs = [torch.randn(1, input_size) for _ in range(5)]

# --- 初始化记忆 (h_0) ---
# 一开始脑子是空的
h_t = torch.zeros(1, hidden_size)

print(&quot;开始时间循环...\n&quot;)

# --- RNN 的核心循环 ---
for i, x_t in enumerate(inputs):
    # 公式: h_t = tanh( W_xh * x + W_hh * h_prev )

    # 1. 现在的输入产生的反应
    current_input_effect = W_xh(x_t)

    # 2. 过去的记忆产生的反应
    past_memory_effect = W_hh(h_t) # 注意这里用的是上一轮的 h_t

    # 3. 融合 (相加) 并 激活
    # Tanh 是 RNN 最常用的激活函数，因为它输出在 -1 到 1 之间，能防止数值爆炸
    h_t = torch.tanh(current_input_effect + past_memory_effect)

    print(f&quot;第 {i+1} 天: 更新了隐藏状态 (记忆)。部分数值: {h_t[0][:3].detach().numpy()}...&quot;)

print(&quot;\n循环结束。最终的 h_t 包含了过去 5 天的所有累积信息。&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There is one important detail: &lt;strong&gt;if the weight is 0.9&lt;/strong&gt;, then as the number of propagation layers grows, &lt;span class=&quot;math-inline&quot;&gt;\(0.9^{n}\)&lt;/span&gt; &lt;strong&gt;approaches 0&lt;/strong&gt;. This means an RNN cannot remember very long inputs; overly long inputs cause it to “forget,” a phenomenon known as the vanishing-gradient problem. This led to the development of &lt;strong&gt;LSTM&lt;/strong&gt;, which uses an &lt;strong&gt;Input Gate, Output Gate, and Forget Gate&lt;/strong&gt; to decide when to store memories and to control the model’s forgetting more precisely.&lt;/p&gt;
&lt;h1 id=&quot;模型的参数究竟是什么？&quot;&gt;What Exactly Are a Model’s Parameters?&lt;/h1&gt;&lt;p&gt;From the FCN, CNN, and RNN models above, we can see that a model is essentially &lt;span class=&quot;math-inline&quot;&gt;\(y = \sigma(kx+b)\)&lt;/span&gt;. Its so-called parameters are therefore &lt;span class=&quot;math-inline&quot;&gt;\(k\)&lt;/span&gt; (the weight, or w) and b (the bias). A real model simply has n$ values of x and m values of y. As n and m (as well as the neurons in the intermediate hidden layers) increase, there are more weights and biases, and the trained model becomes &lt;strong&gt;more&lt;/strong&gt; “intelligent.” This is the popular industry understanding of the “large” in “large model.”&lt;/p&gt;
&lt;p&gt;Training a model means using various mathematical methods to calculate the most effective weights and biases. Take GPT-3 as an example: it has 175B (&lt;strong&gt;175 billion&lt;/strong&gt;) parameters, meaning that the w and b values across all its neurons add up to 175 billion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TODO-Hyperparameters.&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id=&quot;模型是如何理解自然语言的？&quot;&gt;How Does a Model Understand Natural Language?&lt;/h1&gt;&lt;p&gt;So far, we have learned how a model performs inference: given an input x, where x is a tensor, it applies a series of formula-based operations and produces an output. This raises a question. Whether we use an FCN, RNN, or CNN, the model can only perform floating-point operations, so its input must be a group of numbers. That works well enough for ordinary mathematical predictions. But for today’s flourishing field of natural language processing, how do we turn natural language into numbers that a model can understand?&lt;/p&gt;
&lt;h2 id=&quot;One-Hot&quot;&gt;One-Hot&lt;/h2&gt;&lt;p&gt;The simplest approach is to use a Boolean-array mapping. Given a Boolean array of fixed length, we identify different tokens by marking different indexes as true. Suppose your vocabulary contains only five words: &lt;code&gt;[&quot;我&quot;, &quot;爱&quot;, &quot;学习&quot;, &quot;AI&quot;, &quot;猫&quot;]&lt;/code&gt;. The word “学习” is represented as &lt;code&gt;[0, 0, 1, 0, 0]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;One-hot encoding has two fatal problems that prevent it from being used in LLMs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Sparsity and the curse of dimensionality:&lt;/strong&gt; A real vocabulary contains tens or even hundreds of thousands of words. Each word becomes a vector of length 50,000 containing 49,999 zeros. This wastes enormous computational resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lack of semantic relationships (Semantic Meaning):&lt;/strong&gt; In One-Hot space, the distance between every pair of words is identical. The distance between &lt;strong&gt;“cat”&lt;/strong&gt; and &lt;strong&gt;“dog”&lt;/strong&gt; is the same as the distance between &lt;strong&gt;“cat”&lt;/strong&gt; and &lt;strong&gt;“refrigerator.”&lt;/strong&gt; The model cannot know that “cat” and “dog” are similar.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;Embedding&quot;&gt;Embedding&lt;/h2&gt;&lt;p&gt;Embedding lets us compress one-hot’s high-dimensional sparse vectors into low-dimensional dense vectors. For example, we can map “cat” to a 512-dimensional vector:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(\text{Embedding(cat)} = [0.21, -0.54, 0.03, ..., 0.99]\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this new vector space, &lt;strong&gt;words with similar meanings are closer together geometrically&lt;/strong&gt;. For example, we can approximately say:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(&quot;国王&quot; - &quot;男人&quot; + &quot;女人&quot; \approx &quot;女王&quot;\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;How do we obtain a set of dense vectors capable of retaining relationships among words? The method is straightforward: perform unsupervised training and transformation with an FCN. Here is an example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Calculate the five-dimensional one-hot vectors for “机器 学习 改变 了 世界” and convert them into three-dimensional dense vectors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Define one-hot vectors for the five tokens. For example, the one-hot vector for &lt;em&gt;学习&lt;/em&gt; is &lt;span class=&quot;math-inline&quot;&gt;\(x = [0, 1, 0, 0, 0]\)&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Define a linear regression network in which W has a 5x3 structure, then substitute the x for “学习” as follows:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;math-display&quot;&gt;\[h = x \cdot W_{in} = [0, 1, 0, 0, 0] \cdot
\begin{bmatrix}
w_{0,0} &amp;amp; w_{0,1} &amp;amp; w_{0,2} \\
\mathbf{w_{1,0}} &amp;amp; \mathbf{w_{1,1}} &amp;amp; \mathbf{w_{1,2}} \\
w_{2,0} &amp;amp; w_{2,1} &amp;amp; w_{2,2} \\
\vdots &amp;amp; \vdots &amp;amp; \vdots \\
w_{4,0} &amp;amp; w_{4,1} &amp;amp; w_{4,2}
\end{bmatrix}_{w初始为随机浮点数}\]&lt;/div&gt;&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Next, calculate the dot product of &lt;span class=&quot;math-inline&quot;&gt;\(h\)&lt;/span&gt; and another transposed matrix, &lt;span class=&quot;math-inline&quot;&gt;\(W_{out}\)&lt;/span&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(z = \sigma(h \cdot W_{out} = h \cdot [u_0, u_1, u_2, u_3, u_4])\)&lt;/span&gt;&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;This gives the probabilities relating “学习” to each token in the original sentence. Suppose the calculated dot-product result is:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(\hat{z} = [0.05, \quad 0.02, \quad \mathbf{0.80}, \quad 0.01, \quad 0.12]\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This means there is an 80% probability that “改变” follows “学习,” but we want that probability to be 100%. We therefore need to update the values in W through backpropagation. After multiple rounds of training, &lt;span class=&quot;math-inline&quot;&gt;\(W_{input}\)&lt;/span&gt; becomes our final embedding vectors.&lt;/p&gt;
&lt;h1 id=&quot;机器是如何学习的？&quot;&gt;How Does a Machine Learn?&lt;/h1&gt;&lt;p&gt;We have repeatedly emphasized that we need to train models so they can learn independently. How, then, does a model learn?&lt;/p&gt;
&lt;p&gt;Before examining the learning process in depth, we need to understand two terms: Forward Propagation and Backpropagation.&lt;/p&gt;
&lt;h2 id=&quot;前向传播&quot;&gt;Forward Propagation&lt;/h2&gt;&lt;p&gt;Forward propagation is quite simple to explain. The FCN, CNN, and RNN computations described above are all forward propagation, expressed as:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(y_{pred} = \text{Model}(x)\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;During forward propagation, the parameters (Weight and bias) are known and given. In fact, when we use GPT or Gemini, the process by which the model produces an answer is forward propagation.&lt;/p&gt;
&lt;h2 id=&quot;反向传播&quot;&gt;Backpropagation&lt;/h2&gt;&lt;p&gt;Backpropagation is the reverse of forward propagation. Given &lt;span class=&quot;math-inline&quot;&gt;\(y_{pred},y_{true},LearningRate\)&lt;/span&gt;, it is the process of calculating the parameters &lt;span class=&quot;math-inline&quot;&gt;\(Weight, Bais\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;To define exactly how far apart &lt;span class=&quot;math-inline&quot;&gt;\(y_{pred}\)&lt;/span&gt; and &lt;span class=&quot;math-inline&quot;&gt;\(y_{true}\)&lt;/span&gt; are, we generally use a Loss Function. This is the Loss commonly discussed when training models: the larger the Loss, the worse the model’s result. A common loss function is Mean Squared Error:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(Loss = (y_{pred} - y_{true})^2\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To reduce Loss, we use backpropagation together with the Gradient Descent algorithm, calculating new weights through differentiation according to this formula:&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;math-inline&quot;&gt;\(W_{new} = W_{old} - \text{learning\_rate} \times \text{Gradient}\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Learning_rate is the step size, which determines how much the weights change in each training iteration. You can search for details on how the Gradient is calculated; I will not cover them here. An example of backpropagation follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt

# --- 1. 准备数据 (真值) ---
# 假设真实规律是: y = x * 0.5 + 0.2
# 我们生成 100 个数据点
X = torch.rand(100, 1)  # 随机生成 0~1 之间的温度
y_true = X * 0.5 + 0.2  # 这是我们要逼近的&quot;靶心&quot;

# --- 2. 搭建模型 (射手) ---
# 一个最简单的线性层: y = w * x + b
# 初始时，w 和 b 都是随机生成的，它根本不知道 0.5 和 0.2 是什么
model = nn.Linear(1, 1)

print(f&quot;初始随机权重 w: {model.weight.item():.4f}, 偏置 b: {model.bias.item():.4f}&quot;)

# --- 3. 定义工具 ---
# Loss Function (裁判): 均方误差
criterion = nn.MSELoss()
# Optimizer (教练): 随机梯度下降 (SGD)，学习率 lr=0.1
optimizer = optim.SGD(model.parameters(), lr=0.1)

# --- 4. 开始训练循环 (练习射箭 100 次) ---
loss_history = []

for epoch in range(100):
    # A. 前向传播 (射出一箭)
    y_pred = model(X)

    # B. 计算 Loss (测量差距)
    loss = criterion(y_pred, y_true)
    loss_history.append(loss.item())

    # C. 反向传播 (寻找原因)
    # 这行代码会自动计算所有权重的梯度
    optimizer.zero_grad() # 清空上一次的梯度
    loss.backward()       # &amp;lt;--- 核心！反向传播发生在这里

    # D. 更新权重 (纠正姿势)
    optimizer.step()

# --- 5. 结果展示 ---
print(f&quot;训练后学到的权重 w: {model.weight.item():.4f} (真实值是 0.5)&quot;)
print(f&quot;训练后学到的偏置 b: {model.bias.item():.4f} (真实值是 0.2)&quot;)

# 画图：Loss 是如何下降的
plt.figure(figsize=(8, 4))
plt.plot(loss_history, color=&apos;red&apos;)
plt.title(&apos;Loss Curve: Error drops as the model learns&apos;)
plt.xlabel(&apos;Training Iterations (Epochs)&apos;)
plt.ylabel(&apos;Loss (Error)&apos;)
plt.grid(True)
plt.show()
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;为什么得显卡者得天下？&quot;&gt;Why Do GPUs Rule the World?&lt;/h1&gt;&lt;p&gt;Before exploring this question, let us distinguish CPUs from GPUs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;CPU&lt;/strong&gt;: A CPU has a small number of powerful cores—our typical servers, for example, may have eight cores. It handles complex computations and logical branches, adding caches, control units, and other components to its computational units.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPU&lt;/strong&gt;: A GPU has many simpler cores—even an ordinary one may have 4,096. Each core has limited capabilities and can perform only simple arithmetic rather than complex logic.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;From model inference and training, we know that both forward propagation and backpropagation involve vast numbers of vector-product operations and differentiations. At heart, these are simple floating-point operations. Such operations basically do not &lt;strong&gt;interfere with one another&lt;/strong&gt;, so they are naturally suited to “parallel” computation, making them an excellent fit for GPU architecture.&lt;/p&gt;
&lt;p&gt;For example, multiplying two &lt;strong&gt;1000x1000&lt;/strong&gt; matrices requires roughly &lt;strong&gt;one billion&lt;/strong&gt; multiply-add operations. Even with eight parallel threads, an eight-thread CPU will take a long time to process them. A GPU has many more cores, so it is naturally much faster.&lt;/p&gt;
&lt;h1 id=&quot;参考资料&quot;&gt;References&lt;/h1&gt;&lt;ol&gt;
&lt;li&gt;Hung-yi Lee’s materials: &lt;a href=&quot;https://www.youtube.com/playlist?list=PLJV_el3uVTsMhtt7_Y6sgTHGHp1Vb2P2J&quot;&gt;YouTube (in Chinese)&lt;/a&gt;, &lt;a href=&quot;https://www.bilibili.com/video/BV1TAtwzTE1S/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=f22e07c1e9ae0985f92cf3a4f47ea7f8&quot;&gt;Bilibili (in Chinese)&lt;/a&gt;&lt;ol&gt;
&lt;li&gt;ml:&lt;a href=&quot;https://speech.ee.ntu.edu.tw/~hylee/ml/2021-spring.php&quot;&gt;https://speech.ee.ntu.edu.tw/~hylee/ml/2021-spring.php (in Chinese)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;generative: &lt;a href=&quot;https://speech.ee.ntu.edu.tw/~hylee/genai/2024-spring.php&quot;&gt;https://speech.ee.ntu.edu.tw/~hylee/genai/2024-spring.php (in Chinese)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ML 2025: &lt;a href=&quot;https://speech.ee.ntu.edu.tw/~hylee/ml/2025-spring.php&quot;&gt;https://speech.ee.ntu.edu.tw/~hylee/ml/2025-spring.php (in Chinese)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Gemini&lt;/li&gt;
&lt;li&gt;Datawhale: &lt;a href=&quot;https://github.com/datawhalechina&quot;&gt;https://github.com/datawhalechina&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item>
<item><title>Why So Many Students Choose to Study in Hong Kong</title><link>https://wxxlamp.cn/en/2025/10/07/why-graduated-to-hkust/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2025/10/07/why-graduated-to-hkust/</guid><pubDate>Tue, 07 Oct 2025 02:36:00 GMT</pubDate><description>Marking one year since beginning a post-work degree at HKUST, this article compares Hong Kong and mainland China in institutions, finance, and social welfare. It focuses on differences between their university education systems and presents a genuine experience of studying in Hong Kong.</description><content:encoded>&lt;h1 id=&quot;前言&quot;&gt;Preface&lt;/h1&gt;&lt;p&gt;Time flies. It has been a full year since I went to Hong Kong for a master’s degree in September 2024. Although I have not spent that much time in Hong Kong during the year—perhaps only several dozen days in total—the city’s distinctive character has deeply impressed me.&lt;/p&gt;
&lt;p&gt;I am a typical small-town test taker. I never left the county town for study in junior or senior high school. As I recall, my first visit to the provincial capital was for a special-admission examination at a Project 985 university after the college entrance examination. I first left the province only after entering university, on a trip to Shanghai with classmates during the summer of my freshman year at age eighteen. As for going abroad or outside the mainland, embarrassingly, I had never done it before working and then studying for a master’s degree.&lt;/p&gt;
&lt;p&gt;So, taking this first anniversary of study as an opportunity, I would like to talk about my year of overseas study from the perspective of a worker who returned to education after working. This is the first article in a series. It mainly introduces differences between Hong Kong—especially its education—and mainland China. Friends interested in studying in Hong Kong are also welcome to message me for details.&lt;/p&gt;
&lt;h1 id=&quot;香港与内地的差异在哪里？&quot;&gt;Where do Hong Kong and mainland China differ?&lt;/h1&gt;&lt;p&gt;This section will not focus on simple differences in daily life, such as language and transport. Its focus is on the different positioning of Hong Kong and the background of “one country, two systems.”&lt;/p&gt;
&lt;h2 id=&quot;从一国两制看香港&quot;&gt;Hong Kong through “one country, two systems”&lt;/h2&gt;&lt;p&gt;After I went to Hong Kong for the start of term last year, I was initially just curious about a different environment—for example, signage everywhere is in traditional Chinese and English, and I never saw Mandarin or Pinyin. Later I found that Hong Kong’s institutions are indeed fundamentally different from those on the mainland.&lt;/p&gt;
&lt;p&gt;Under “one country, two systems,” Hong Kong’s foreign affairs, sovereignty, and military are managed centrally, while other systems—such as legislation, justice, prosecution, speech, and finance—are decided by the Hong Kong government itself. After entering Hong Kong, one finds that speech controls and ideology are completely different from the mainland. As I recall, Luo Yonghao and Zhou Hongyi both had experiences of being summoned by mainland police and fleeing to Hong Kong, because Hong Kong’s judicial independence prevents mainland police from enforcing law across the border. Hong Kong’s financial system is also closer to Western systems; its payment habits are nearly identical to Europe and the United States. Similarity in financial systems is one reason there are more foreigners in Hong Kong. A Hong Kong identity is also more readily recognized by other countries and regions, so many classmates find applications for Japanese, European, and American visas especially easy using a Hong Kong identity card.&lt;/p&gt;
&lt;p&gt;High-school textbooks tell us that we are a socialist country with Chinese characteristics, whereas after 1997 Hong Kong continued the capitalist system from the period of British colonial rule. In government elections, people from all sectors of society who meet legal qualifications—who comply with the Basic Law—can participate according to law. I often saw candidates posting “pick me”-style advertisements on the minibus to and from school. Hong Kong residents can also express views on government measures through various channels, provided they do not violate the Basic Law: “what the law does not prohibit may be done.” As a capitalist region, many Hong Kong matters operate through capital. Employees’ Mandatory Provident Fund, for example, is managed by qualified private MPF trustees, fundamentally unlike mainland state-owned enterprises and direct government management. Even the Hong Kong dollar is issued by three private banks authorized by the government and pegged to the U.S. dollar: banks remit U.S. dollars to the Hong Kong government, receive permission, and issue Hong Kong dollars at a fixed exchange rate. This is fundamentally different from the mainland’s unified issuance of RMB by the central bank.&lt;/p&gt;
&lt;h2 id=&quot;东方之珠为什么是香港&quot;&gt;Why Hong Kong is the Pearl of the Orient&lt;/h2&gt;&lt;p&gt;Hong Kong is known as the “Pearl of the Orient” and is a financial center for exchange between the mainland and the outside world. Because of its previous British colonization, combined with intentional government promotion in recent years, Hong Kong has become a top-tier Asian financial center. Nearly all major global financial institutions establish their Asian headquarters there. Of course, in recent years some institutions have moved headquarters to Singapore for various reasons; Singapore is challenging Hong Kong and establishing a new financial order.&lt;/p&gt;
&lt;p&gt;Every year, a continuous stream of foreign-exchange reserves is delivered to the mainland through Hong Kong, while much RMB is exchanged for U.S. dollars through Hong Kong’s financial center. Nearly all internationalized mainland companies—including familiar Internet companies—open Hong Kong branches, using its international settlement network and multi-currency financing channels to conduct business. Most transaction-settlement funds circulate through Hong Kong’s banking system. In recent years, many companies have sought to go overseas. One could even say that Hong Kong is necessarily the first stop for mainland companies going global—not to sell goods in Hong Kong, but to use the absence of foreign-exchange controls to allocate cross-border funds or convert them into Hong Kong dollars, U.S. dollars, and other currencies for international settlement and asset allocation.&lt;/p&gt;
&lt;p&gt;Consequently, Hong Kong’s financial industry is extremely developed. Many large mainland companies establish finance, investment, and legal departments there. I once heard from a friend of a friend inside Tencent that he goes to Hong Kong every year to report investments to Pony; that also demonstrates Hong Kong’s financial position. But for the IT work I do, Hong Kong is much weaker: old technology stacks and low salaries characterize Hong Kong IT. Only bank technology departments or quantitative technology are a way forward in Hong Kong IT, and how much does getting into such firms actually have to do with technical skill?&lt;/p&gt;
&lt;p&gt;Benefiting from Hong Kong’s position as a financial center, Shenzhen has developed increasingly fast because it faces Hong Kong across the sea. A quick look at the job market shows an enormous number of Shenzhen cross-border trade and cross-border payment companies. Because Hong Kong has no foreign-exchange controls and customs clearance with Shenzhen is convenient, many Shenzhen colleagues experienced in stock trading exchange a certain amount of RMB for Hong Kong dollars or U.S. dollars to further optimize asset allocation.&lt;/p&gt;
&lt;h2 id=&quot;香港工作和生活&quot;&gt;Working and living in Hong Kong&lt;/h2&gt;&lt;p&gt;After visiting Hong Kong many times, one finds that its social welfare and facilities for an aging society do differ from the mainland’s.&lt;/p&gt;
&lt;p&gt;For example, it provides Old Age Allowance and Old Age Living Allowance to elderly people. Taxes paid by workers are relatively low—progressive taxation, with a maximum of 17%—and beyond tax, only a 5% Mandatory Provident Fund contribution is needed. The MPF system is similar to the mainland pension system. What impressed me most is that Hong Kong’s infrastructure is very friendly to older people and people with disabilities. Buses have wheelchair boarding ramps; when a wheelchair user is waiting at a stop, the driver immediately gets out to open the dedicated ramp. Hong Kong’s tactile paving is also comprehensive. On my way to and from school I saw blind people walking with canes more than once. Consideration for aging is not merely infrastructure; it has entered ordinary people’s minds, and many would rather stand than occupy priority seats on the metro or bus.&lt;/p&gt;
&lt;p&gt;What astonished me was Hong Kong people’s strong awareness of rules. Even when lifts are crowded, people queue on the right and leave the left passage for people in a hurry, except in extremely crowded cases. I remember once queuing for a toilet stall: people in Hong Kong began queuing at the entrance, ensuring that whoever arrived first used a stall first, rather than everyone standing at the doors of separate stalls. When everyone follows one set of rules, ordinary people enjoy basic services more fairly and society operates more efficiently.&lt;/p&gt;
&lt;p&gt;But no place is perfect. Many people know average salaries in Hong Kong are very high; even restaurant waiters seen everywhere earn roughly HKD 15,000–25,000 per month. Yet consumption is extremely expensive. Leaving housing aside, even a simple three-dish meal at my school cafeteria costs around HKD 40–50. At a modest restaurant outside, ordering a few dishes can start at HKD 200 per person, unaffordable for ordinary people. At times, a taxi from school back to Shenzhen—a trip of about 50 km—costs HKD 400. That said, Hong Kong’s public transport is very developed, and basic travel can largely be completed by metro and bus.&lt;/p&gt;
&lt;p&gt;Some mainland people may have experienced discrimination from Hong Kong people. Once, on a minibus home, I misheard Cantonese spoken by the driver and he said contemptuously, “This is Hong Kong, not the mainland.” I cannot say I was not angry in that moment, but I also knew it was only the driver’s impotent rage. Friends who know Hong Kong told me that with faster implementation of one country, two systems and a continuous inflow of high-end talent, many older blue-collar workers still have hostility toward mainland people. In their minds, the mainland remains the mainland of twenty years ago. Little do they realize that times change unpredictably and China is becoming stronger than ever.&lt;/p&gt;
&lt;p&gt;Still, it must be admitted objectively that there are obvious gaps between the mainland and Hong Kong in some respects. I believe behind these gaps is the difference between 1.4 billion people and 7 million people: as population differs, the difficulty of management rises exponentially. Times are developing; the Party’s strong leadership and the superiority of the socialist system will both become engines for the Eastern dragon’s development. Our motherland will certainly become better!&lt;/p&gt;
&lt;h1 id=&quot;香港的大学教育和内地有区别吗？&quot;&gt;Does university education in Hong Kong differ from the mainland?&lt;/h1&gt;&lt;p&gt;Although I graduated from undergraduate study three years ago, returning to Hong Kong to study still lets me feel huge differences between the two education systems.&lt;/p&gt;
&lt;p&gt;Mainland education emphasizes universal access more. The government’s first goal is enabling every child, no matter how poor, to afford university, and it also devotes considerable effort to making up for educational shortcomings for people with disabilities. But mainland China has too many students, and because it has developed so quickly, the teacher-student ratio is relatively low. Hong Kong performs a little better on this ratio and, as is well known, its education is more “elite-oriented,” allowing students more diverse development.&lt;/p&gt;
&lt;p&gt;Below, from my own experience, I briefly compare educational systems at mainland Project 211 universities and Hong Kong’s top-three universities.&lt;/p&gt;
&lt;h2 id=&quot;学校信息系统建设&quot;&gt;University information systems&lt;/h2&gt;&lt;p&gt;During my whole undergraduate career, whether notices from the university or school, events, or course assignments, everything was communicated through QQ group chats—perhaps WeChat now. But because Hong Kong study and work are more Western-oriented, many matters are notified by email: post-holiday shuttle-bus arrangements, holiday periods, professors’ lectures, maintenance information, and every other detail. It is normal for students to receive dozens of relevant emails a day.&lt;/p&gt;
&lt;p&gt;Whether group notices or email are better is a matter of opinion, but Hong Kong classrooms are much more digitalized than mainland ones. Teachers’ contacts, course information, and assigned work—including deadlines—are all available on websites or Canvas. Students upload completed electronic assignments directly through Canvas. This made a particularly deep impression on me. In undergraduate study, almost every assignment—calculation problems, coding, and so on—was written on paper and handed to the class study representative to submit. If one missed the representative’s collection time, one had to submit it directly to the teacher; students sometimes missed deadlines because they could not find the representative. As I recall, undergraduate grades and rankings were sent to the class monitor, who posted them directly as group announcements, causing students who performed poorly to publicly lose face. When studying for my master’s in Hong Kong, teachers put grades, rankings, and the class’s top and bottom quarter score lines only on each student’s own Canvas, protecting everyone’s privacy.&lt;/p&gt;
&lt;p&gt;Besides notices and classes, Hong Kong university websites are especially well digitalized. Any information can be found on the intranet, including course results, employment information, tuition payments, and graduation information. Because I am not often in Hong Kong during term, what I find most convenient is being able to check my elective credits at any time and directly check my graduation progress through the website.&lt;/p&gt;
&lt;h2 id=&quot;课堂教学质量&quot;&gt;Teaching quality&lt;/h2&gt;&lt;p&gt;Because my Hong Kong degree is an MSc, mainly taught courses, I also made a simple comparison of classroom quality in the mainland and Hong Kong.&lt;/p&gt;
&lt;p&gt;In Hong Kong I took easy courses I had already studied, such as Computer Networks and Software Engineering, as well as comparatively cutting-edge courses such as Blockchain, LLM, and NLP. What impressed me most was how current the teachers’ ideas were. In all these computing-related courses, professors mentioned large models, and each course specifically spent one or two classes explaining the relationship between LLMs and the course, as well as current LLM development. Another discovery was that many knowledge points came with links to relevant papers for students to explore. I never had that experience as a mainland undergraduate. I never knew what a paper was in undergraduate classes; only after studying in Hong Kong did I discover how close research was to me. I once spoke with an associate professor from my undergraduate university, who candidly said he had not learned for a long time and, being older now, basically did not read papers in related fields. Hong Kong instructors are generally at the research frontier, so students can better grasp the idea of “papers.”&lt;/p&gt;
&lt;p&gt;In addition, I found many part-time teachers while studying in Hong Kong. They do not hold university “establishment” positions and only undertake teaching duties. They generally have PhDs and other jobs in society. For example, my first-semester Blockchain teacher and second-semester FinTech teacher both worked outside the university and taught on weekends as part-timers. We did have corporate-practice courses as undergraduates, but because they carried no or very few credits and were extremely superficial, I think everyone understands the overall quality. I think this Hong Kong arrangement is an especially good innovation. Compared with the possible separation from practical reality among some mainland teachers who remain in ivory towers, learning from it could very likely ease the current disconnect between mainland schools and social needs.&lt;/p&gt;
&lt;p&gt;Perhaps because the Hong Kong government and universities have money, learning facilities are exceptionally good: the library even has many free 4K monitors for students. Students can also reserve school study rooms online for group discussion. Thinking back to undergraduate group discussions, we either met in an open-air cafeteria or found an empty classroom, which is a little bittersweet.&lt;/p&gt;
&lt;h2 id=&quot;入学难度和职业发展&quot;&gt;Admission difficulty and career development&lt;/h2&gt;&lt;p&gt;It is well known that because mainland China has so many students, the college entrance examination is particularly competitive. So many mainland parents send children to Hong Kong for high school so they can apply directly to Hong Kong universities for undergraduate education. This is indeed one option. But the money and effort involved are absolutely beyond what an ordinary family can bear. I do not know much about this area, so I will not discuss it in detail.&lt;/p&gt;
&lt;p&gt;Perhaps because I am at HKUST, the entrepreneurial atmosphere is especially strong. Many students’ labels are “serial entrepreneur”; at such young ages, they have started businesses several times and even received angel rounds, which truly astonished me. At university I also learned about some Hong Kong PhD situations. People basically pursue PhDs with full scholarships, receiving roughly HKD 20,000 per month, which feels like a substantial income during doctoral study.&lt;/p&gt;
&lt;p&gt;Many people are interested in MSc employment and PhD progression. Since this article is already lengthy, I will discuss these in detail in the next one.&lt;/p&gt;
</content:encoded></item>
<item><title>Why the Digital Renminbi Matters in an Online Payment Economy</title><link>https://wxxlamp.cn/en/2025/09/17/what-is-digtal-rmb/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2025/09/17/what-is-digtal-rmb/</guid><pubDate>Wed, 17 Sep 2025 02:36:00 GMT</pubDate><description>Starting with the separation of information flow and fund flow in conventional online payments, this article clarifies transaction, clearing, settlement, and delivery, and explains how the digital renminbi synchronizes fund flow through central-bank blockchain technology and its use cases.</description><content:encoded>&lt;h1 id=&quot;前言&quot;&gt;Preface&lt;/h1&gt;&lt;p&gt;I have recently been developing cross-border payment and fund-related features. Cross-border fund movement is truly expensive and slow, although domestic consumers hardly feel those drawbacks. That led me to study China’s online-payment network, as well as the digital renminbi and stablecoins.&lt;/p&gt;
&lt;p&gt;Some readers probably have a digital-renminbi bank account. Yet most people ask the same question when using it: paying with the digital renminbi feels almost identical to Alipay. Since we already have online payment as convenient as Alipay, why is the country promoting it?&lt;/p&gt;
&lt;p&gt;Some guess it is redundant and merely creates jobs. Others think the state wants to take control of electronic payments back from Alipay and WeChat. Some even think it is only a blockchain gimmick for capital markets.&lt;/p&gt;
&lt;p&gt;In fact, its strategic value goes far beyond what most people imagine. The digital renminbi has been digital from its creation, which is very different from traditional paper RMB supported by the Internet.&lt;/p&gt;
&lt;p&gt;From a user’s perspective, the digital renminbi and Alipay are both quick and convenient online payments. But in areas such as large-value transactions, which ordinary consumers rarely encounter, conventional RMB online payment still has many pain points.&lt;/p&gt;
&lt;p&gt;This article begins with online payments, discusses the difference between conventional RMB and the digital renminbi, and finally considers the opportunity for stablecoins in mainland China.&lt;/p&gt;
&lt;h1 id=&quot;传统在线支付方式的本质&quot;&gt;The nature of conventional online payments&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;Conventional online payments complete transactions through digitized banknotes. Underneath, RMB still needs to move among banks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;信息流与资金流&quot;&gt;Information flow and fund flow&lt;/h2&gt;&lt;p&gt;Before online transactions became widespread, everyone transacted offline directly with RMB. &lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;When we hand cash to a merchant, the delivery of funds and goods is completed simultaneously. This is the familiar idea of “&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;payment and goods settled together&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;”; the transaction’s information flow and fund flow also finish together.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/91a95a98_nlark_img1.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Keeping cash on hand is inconvenient, so most money is held in banks. Withdrawing it to buy goods offline is troublesome. Naturally, we want to pay directly online through banks, as shown below.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/95ed4685_nlark_img2.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;But is the solution really that simple? What if Li Si’s account is at another bank?&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/3777b175_nlark_img3.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Bank A must actually transfer Zhang San’s 10 yuan to Bank B; this is called fund delivery. Delivery cannot be completed by a simple instruction, so interbank transfers are slow. Can they be made faster?&lt;/p&gt;
&lt;p&gt;Yes. Bank A can open an account at Bank B and deposit a reserve. When Zhang San asks Bank A to transfer 10 yuan to Bank B, Bank A only sends the instruction and Bank B debits A’s account. This is clearly much more efficient.&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;This approach still leaves at least two issues unresolved:&lt;/font&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;The obstacle of transfers among many banks&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;: China has hundreds of commercial banks. Must every bank deposit funds with every other? A bank failure or disappearance could cause a major incident, which the state does not want.&lt;/font&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Lack of supervision and restraints&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;: without effective supervision, commercial banks may face moral hazard, such as transferring Li Si’s funds to Zhang San without authorization.&lt;/font&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Therefore, a highly trusted intermediary is needed. This role is performed by the People’s Bank of China (PBOC). The PBOC requires online payments and transfers to go through UnionPay (interbank transactions) or NetsUnion (transactions involving payment institutions); those systems settle the funds. Commercial banks and payment institutions must also maintain reserve accounts at the PBOC, which completes final fund delivery.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Modern online payments can therefore be roughly divided into two steps:&lt;/font&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Information flow&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;: payment institutions or banks make internal book entries and freeze the funds, completing preliminary processing.&lt;/font&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Fund flow&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;: funds are actually moved through the central-bank clearing system; only then does ownership truly transfer.&lt;/font&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/266325f4_nlark_img4.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is the information flow and fund flow often mentioned in payments. Online payment is much more complex than it looks. Can such a complex process finish in seconds?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Clearly it cannot.&lt;/strong&gt; Every transaction must be handled by the central bank. If it updated every bank’s reserve account in real time for each transaction, its system would quickly collapse. The PBOC instead uses T+1 batch processing and netting for daily transactions between banks and payment institutions. The information flow can finish quickly, but the fund flow through the PBOC may take a day to become final.&lt;/p&gt;
&lt;p&gt;Why then does an Alipay transfer feel instantaneous to consumers? Alipay provides credit backing and advances funds. Because it has reserve accounts at partner banks, it can notify relevant banks to pre-debit funds during the information-flow stage. In general, &lt;strong&gt;when users see a transaction succeed in real time, only the information flow has succeeded&lt;/strong&gt;; the fund flow still needs central-bank settlement.&lt;/p&gt;
&lt;h2 id=&quot;资金交易、清分、结算和交割&quot;&gt;Transactions, clearing, settlement, and delivery&lt;/h2&gt;&lt;p&gt;To understand what happens behind a payment, we need to clarify transaction, clearing, settlement, and delivery.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Transaction&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;: here this does not mean buying and selling goods, but the&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;initiation of a payment instruction&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;. It specifies sender, recipient, amount, and other information, and starts the fund process. Clicking “Confirm payment” in Alipay initiates a&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(0, 0, 0) !important;&quot;&gt;fund-transfer instruction&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(0, 0, 0);&quot;&gt;.&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(0, 0, 0);&quot;&gt;From the user’s view, “Success” means the instruction has been received, not that fund transfer is over.&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(0, 0, 0);&quot;&gt; Participants include the&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(0, 0, 0) !important;&quot;&gt;user initiating it&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(0, 0, 0);&quot;&gt; and the payment institution or bank receiving it.&lt;/font&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clearing&lt;/strong&gt;: &lt;font style=&quot;color:rgba(0, 0, 0, 0.85);&quot;&gt;the&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(0, 0, 0) !important;&quot;&gt;centralized handling of many transactions over a period&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85);&quot;&gt;. It verifies transaction information, calculates each party’s receivables and payables, and establishes the claims and obligations between payment institutions and banks (the net amount). In short, it calculates who owes whom how much; an obligation is established but funds have not moved. Participants are mainly&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(0, 0, 0) !important;&quot;&gt;clearing institutions such as NetsUnion and UnionPay, banks, and payment institutions&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85);&quot;&gt;.&lt;/font&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Settlement&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;: the process of&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;final confirmation&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; of clearing results and creation of irrevocable transfer instructions. It makes obligations final. Clearing and settlement are often jointly called “clearing,” normally comprising “calculate the accounts” and “pay the money.” Participants extend to the&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;central bank&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; payment system.&lt;/font&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delivery&lt;/strong&gt; &lt;strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85);&quot;&gt;(Settlement finality)&lt;/font&gt;&lt;/strong&gt;: &lt;font style=&quot;color:rgba(0, 0, 0, 0.85);&quot;&gt;when settlement is complete and transfer is irrevocable, delivery has been reached.&lt;/font&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; It is the&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;final stage&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;, achieving&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;settlement finality&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;. The central bank adjusts the balances of the relevant&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;commercial banks&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; in their&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;reserve accounts&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;. Only then have funds fully transferred in law. A payment institution’s&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;centralized customer-reserve custody account&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; is adjusted correspondingly, ultimately affecting its custodian bank’s reserve account.&lt;/font&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Typical processing times:&lt;/font&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(0, 0, 0) !important;&quot;&gt;Stage&lt;/font&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(0, 0, 0) !important;&quot;&gt;Time window&lt;/font&gt;&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85) !important;&quot;&gt;Transaction&lt;/font&gt;&lt;/td&gt;
&lt;td&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85) !important;&quot;&gt;24/7 (users may initiate payment instructions at any time)&lt;/font&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85) !important;&quot;&gt;Clearing&lt;/font&gt;&lt;/td&gt;
&lt;td&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85) !important;&quot;&gt;Real-time clearing (D0) or scheduled clearing (such as early morning T+1, chosen by the payment institution)&lt;/font&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85) !important;&quot;&gt;Settlement&lt;/font&gt;&lt;/td&gt;
&lt;td&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85) !important;&quot;&gt;24/7 (small-value / third-party payments) or weekdays 8:30–17:00 (large-value system, traditional mode)&lt;/font&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85) !important;&quot;&gt;Delivery&lt;/font&gt;&lt;/td&gt;
&lt;td&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85) !important;&quot;&gt;Real-time after settlement (account balances update immediately)&lt;/font&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;在线支付的资金流转&quot;&gt;Fund movement in online payments&lt;/h2&gt;&lt;p&gt;Suppose user A transfers 100 yuan from a China Merchants Bank account to user B’s Alipay balance. Each stage is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Transaction:&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; A clicks pay in Alipay, which sends an instruction to China Merchants Bank, and the bank freezes A’s amount.&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; A sees payment succeed and B receives money, but Alipay has only made internal entries; funds have not truly moved.&lt;/font&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Clearing:&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; Alipay packages transactions from a period and sends them to NetsUnion, which calculates how much Alipay should receive from CMB and pay other banks (netting).&lt;/font&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Settlement:&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; NetsUnion turns the calculated net result (for example, Alipay receives a net 100 yuan from CMB) into a formal settlement list and sends it to the PBOC payment system.&lt;/font&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Delivery:&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; The PBOC’s large-value payment system completes the transfer&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;finally and irrevocably&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; on its ledger: it debits 100 yuan from CMB’s reserve account and credits Alipay’s reserve account.&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; Only then has A’s 100 yuan actually left CMB and entered Alipay’s pool.&lt;/font&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;The flowchart makes the full fund movement easier to see:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/610f57ff_nlark_img5.svg&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;在线支付的缺点&quot;&gt;Drawbacks of online payments&lt;/h2&gt;&lt;p&gt;Conventional RMB is essentially cash; online payments merely digitize it. Ordinary people cannot digitize cash and trade online by themselves, so they deposit money in banks and use banks or payment institutions as intermediaries.&lt;/p&gt;
&lt;p&gt;Intermediaries create these drawbacks:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Poor timeliness:&lt;/strong&gt; Although small transfers seem instant to both parties, that only completes the transaction stage; the fund flow has not finished. Large-value transfers still need T+1 (withdrawals commonly arrive T+1). If domestic large-value transfers take T+1, international transfers take even longer; USD-to-PKR may take a week.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;High fees:&lt;/strong&gt; Alipay and WeChat charge withdrawal fees (0.1%). Cross-border transfers can involve more intermediary banks and average around 7% in fees.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Difficult account opening:&lt;/strong&gt; Offline, cash is enough. Online, an account must be opened at a bank or payment institution, with identity registration and review. This is unfriendly to overseas travelers and visitors from elsewhere.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Network dependence:&lt;/strong&gt; Current online payments rely on the Internet. Without it, online transactions are impossible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Institutional risk:&lt;/strong&gt; Online payments rely on banks and payment institutions. If they disappear or fail, users’ funds may change hands.&lt;/p&gt;
&lt;p&gt;The digital renminbi emerged to address these drawbacks.&lt;/p&gt;
&lt;h1 id=&quot;数字人民币的底层逻辑&quot;&gt;The underlying logic of the digital renminbi&lt;/h1&gt;&lt;h2 id=&quot;数字人民币的本质&quot;&gt;Its nature&lt;/h2&gt;&lt;p&gt;Unlike conventional RMB, which is still essentially physical cash, the digital renminbi uses blockchain capabilities and is digital from inception. In theory, its online transactions need no intermediary bank or payment institution.&lt;/p&gt;
&lt;p&gt;For example, Zhang San can transfer 10 yuan of digital RMB directly to Li Si through their electronic wallets, just as handing over 10 yuan in cash. The record is written directly to a blockchain operated by the PBOC; no payment institution or bank participates.&lt;/p&gt;
&lt;p&gt;In one sentence: conventional RMB online transactions, such as online banking and Alipay, separate a first-completed information flow from a later-completed fund flow. Digital-RMB online transactions, like conventional RMB offline transactions, unify both flows and complete them simultaneously.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/2a1aec14_nlark_img6.png&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;区块链与智能合约&quot;&gt;Blockchain and smart contracts&lt;/h3&gt;&lt;p&gt;The digital renminbi is based on blockchain technology. What is a blockchain?&lt;/p&gt;
&lt;p&gt;Think of it as a special linked list. Unlike an ordinary linked list, changing information in any blockchain node requires changing every following node to produce a valid chain again. Because such changes are extremely costly (almost impossible), a blockchain is essentially an immutable database.&lt;/p&gt;
&lt;p&gt;For an article on blockchain, see:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.yuque.com/wangxingxing-f4sey/iur8x3/mekvlarxoaw58uvb&quot;&gt;A 10,000-word introduction to blockchain and Bitcoin · Yuque (in Chinese)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Because it cannot be modified, once user transaction data is stored on a blockchain, we can trust the authenticity of account balances derived from it.&lt;/p&gt;
&lt;p&gt;A smart contract can be understood as a contract running on a blockchain. It is tightly bound to the currency on that blockchain. Code can specify when it automatically transfers money to someone. Once published, it cannot change; when a specified condition is triggered, it transfers money to a specified account.&lt;/p&gt;
&lt;h3 id=&quot;数字人民币和比特币&quot;&gt;Digital RMB and Bitcoin&lt;/h3&gt;&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Based on blockchain and cryptographic algorithms, both digital RMB and Bitcoin are cryptographic digital currencies, but they differ fundamentally.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;The central difference is that&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Bitcoin is completely decentralized&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;: any participant can operate a node, and transaction information is fully public. By contrast,&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;digital RMB uses a centralized architecture&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; whose blockchain nodes are entirely controlled by the PBOC, protecting transaction records and account balances.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;In terms of value,&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Bitcoin fluctuates sharply&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;: from the original 1,250 BTC for a pizza to as much as US$110,000 per coin, its price is highly uncertain.&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Digital RMB is stable&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;: as central-bank digital currency, it is backed by national credit, exchanged at par with paper RMB, and has unlimited legal-tender status.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;As to issuance,&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Bitcoin supply is fixed&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt; at 21 million from inception and is controlled by no institution.&lt;/font&gt;&lt;strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Digital RMB issuance is flexible&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;: like conventional RMB, it is macro-regulated by the PBOC according to economic need.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Thus, despite both using cryptography, digital RMB is legal tender representing national credit; Bitcoin is decentralized crypto currency with more of an investment character. This is their most fundamental difference.&lt;/font&gt;&lt;/p&gt;
&lt;h2 id=&quot;数字人民币的应用&quot;&gt;Digital-RMB applications&lt;/h2&gt;&lt;p&gt;Returning to the original question: what does digital RMB have to do with ordinary people? After much thought, regrettably, not much. Competition from Alipay and similar institutions pushed banks to optimize online payments—small transfers are free and arrive in seconds—so consumers already have an excellent, fee-free experience.&lt;/p&gt;
&lt;p&gt;In special circumstances, however, ordinary users can benefit. Without a network, online payments stop working. An electronic wallet on a physical device can pay digital RMB offline because balance information is local, then synchronize settlement with the PBOC once online. That means tourists in mountainous areas need not carry cash.&lt;/p&gt;
&lt;p&gt;But the country would obviously not invest this effort merely for offline payment. The core is direct transactions between counterparties through the central-bank system, without intermediaries. Together with smart contracts, which can enforce transfers automatically under contractual terms, this creates different application scenarios.&lt;/p&gt;
&lt;h3 id=&quot;跨境场景&quot;&gt;Cross-border use&lt;/h3&gt;&lt;p&gt;Friends who trade US or Hong Kong stocks may know that domestic-to-foreign-bank transfers use SWIFT. Funds need clearing through the relevant central banks for USD, EUR, or RMB and through several intermediary banks. Each bank holds the funds and charges a percentage or fixed clearing fee.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/58c18f2d_nlark_img7.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;International interbank transfers typically take at least T+1 and fees are often around 5%. Excluding risk-control review, digital RMB could arrive in real time and at very low cost because there are no intermediary banks. This would be a major benefit for cross-border trade, one of the three engines of growth.&lt;/p&gt;
&lt;p&gt;The country is also promoting digital RMB in Belt and Road settings to reduce dependence on the US-centered SWIFT system. If countries across Asia and Africa accept it, cross-border trade—especially fund movement—could happen anytime and anywhere.&lt;/p&gt;
&lt;h3 id=&quot;B2B领域&quot;&gt;B2B&lt;/h3&gt;&lt;p&gt;For merchants, consumer payment in digital RMB transfers directly through the PBOC blockchain into the merchant’s digital-RMB wallet. There are no payment institutions or intermediary banks, and thus no “withdrawal,” withdrawal fee, or slow arrival.&lt;/p&gt;
&lt;p&gt;Smart contracts also bind contracts to fund flows automatically, making payment default less likely. For example, suppose Company A and Company B sign a procurement contract: when B receives A’s product, B pays A RMB 1 million. Traditionally, the contract creates legal obligations; if B does not pay, A must use judicial procedures.&lt;/p&gt;
&lt;p&gt;With digital RMB this could differ. A and B can create a coded smart-contract agreement. When a logistics company sends delivery information to the contract, it automatically transfers B’s RMB 1 million to A’s account, preventing non-payment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/c22de5e6_nlark_img8.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Business is complex, however. &lt;u&gt;Not every acceptance criterion can be digitized. If Party A delays acceptance, the system cannot trigger the contract and the digital RMB in A’s account still cannot settle to Party B.&lt;/u&gt; Technology alone cannot solve this.&lt;/p&gt;
&lt;h3 id=&quot;G2B领域&quot;&gt;G2B&lt;/h3&gt;&lt;p&gt;Smart-contract rules can direct government digital-RMB fiscal allocations more precisely, preventing misuse and untraceability of conventional funds. For example, a poverty-alleviation program is intended for fertilizer and seeds. The government creates a subsidy smart contract and funds it. It specifies: 1) only designated farmers may use the subsidy, each with an allowance; 2) it lasts one year and unused subsidy returns automatically to the government; and 3) recipients must be agricultural merchants registered with the government.&lt;/p&gt;
&lt;p&gt;After a farmer buys fertilizer and seed from a designated merchant, they enter the merchant information registered with the government. This notifies the smart contract to use the farmer’s allowance, and it transfers the digital RMB to the merchant wallet.&lt;/p&gt;
&lt;p&gt;If the farmer buys a Mac rather than agricultural goods, the contract has no Apple information, so funds cannot be sent to Apple.&lt;/p&gt;
&lt;p&gt;Smart contracts therefore enable targeted subsidy use. This strengthens government control of subsidy funds and uses taxpayer money more efficiently.&lt;/p&gt;
&lt;h1 id=&quot;数字人民币和稳定币&quot;&gt;Digital RMB and stablecoins&lt;/h1&gt;&lt;p&gt;With the US GENIUS Act enacted in recent months and Hong Kong’s Stablecoins Ordinance formally taking effect, stablecoins have again created a market wave.&lt;/p&gt;
&lt;p&gt;A stablecoin is essentially Bitcoin tied to fiat currency. For an article on Bitcoin, see:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.yuque.com/wangxingxing-f4sey/iur8x3/vhm08q3mw87ocv4p&quot;&gt;Seven images that explain what Bitcoin looks like (in Chinese)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;As crypto assets, stablecoins inherit some key Bitcoin characteristics:&lt;/font&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Anonymity:&lt;/strong&gt; an account is a hash value and all transactions use hashes. Even if transactions are traceable on-chain, the real owner of a hash account cannot be known.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeliness:&lt;/strong&gt; &lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;transfers can complete in minutes, operate 24/7, and break traditional finance’s time and geographic barriers without waiting days for clearing.&lt;/font&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Stablecoins are also pegged one-to-one to fiat money such as USD and HKD: when someone buys one yuan’s worth, one stablecoin is created on-chain. &lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Compliant issuers must hold equivalent fiat reserves to ensure solvency.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;Their relatively stable market value plus decentralization, anonymity, and real-time settlement have made them popular.&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(15, 17, 21);&quot;&gt;Globally, stablecoins have become a new arena of geopolitical finance:&lt;/font&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For the United States, the GENIUS Act regulates stablecoins redeemable one-to-one for dollars, meaning the US has begun recognizing them. Dollar stablecoin volume may grow explosively. Their anonymity allows even countries previously sanctioned by the US, such as Russia, to trade again with other countries using dollar-based stablecoins, further strengthening dollar hegemony.&lt;/li&gt;
&lt;li&gt;For Hong Kong, they are an outlet for China’s fund flows. Mainland trade funds, whether imports or exports, generally pass through Hong Kong. Its recognition of stablecoins further consolidates its role as a port city. HKD stablecoins can greatly increase cross-border trade-fund liquidity and lower costs.&lt;/li&gt;
&lt;li&gt;For mainland China, digital RMB already has many stablecoin functions, such as real-time settlement, and adds programmability and central-bank backing. Mainland China is therefore unlikely to promote stablecoins. Its foreign-exchange controls also do not permit anonymous USD or HKD stablecoins to circulate domestically.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In one sentence, stablecoins are an opportunity—but across the ocean. In mainland China, it is worth watching the digital renminbi more closely.&lt;/p&gt;
</content:encoded></item>
<item><title>Understanding Bitcoin and Blockchain from First Principles</title><link>https://wxxlamp.cn/en/2025/09/07/what-is-bitcion-and-blockchain/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2025/09/07/what-is-bitcion-and-blockchain/</guid><pubDate>Sun, 07 Sep 2025 02:36:00 GMT</pubDate><description>A systematic introduction to the core principles of blockchain and Bitcoin, beginning with centralized and decentralized money and covering blockchain structure, hash pointers, asymmetric cryptography, UTXO, Proof of Work, and the complete lifecycle of a Bitcoin transaction.</description><content:encoded>&lt;p&gt;Written for both technical readers and complete beginners, this article moves from decentralization and blockchain to the principles behind Bitcoin. Through explanations and questions, it aims to help everyone understand the essence and technical foundations of decentralized money.&lt;/p&gt;
&lt;h1 id=&quot;中心化和去中心化&quot;&gt;Centralization and Decentralization&lt;/h1&gt;&lt;p&gt;Conceptually, centralization and decentralization represent two ways of storing data and executing logic. With centralization, data such as asset records is stored in one place or institution, and logic such as transactions is executed independently by a single system. With decentralization, also called a distributed model, data such as asset information is stored across multiple peer nodes, and logic such as transactions is valid only when recognized by all nodes.&lt;/p&gt;
&lt;h2 id=&quot;中心化货币的优劣&quot;&gt;Advantages and Disadvantages of Centralized Money&lt;/h2&gt;&lt;p&gt;Before blockchain appeared, almost every system was centralized. Note that distributed systems did exist, but they were generally controlled by a single organization and could not be extended freely to any organization or individual. A distributed database such as OceanBase is therefore still centralized.&lt;/p&gt;
&lt;p&gt;A typical centralized application is centralized money. Backed by national credit, it is issued by each country’s central bank as a measure of wealth and a medium of exchange, and it has unlimited legal tender status. Centralized money has two advantages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Stability&lt;/strong&gt;. Take the US dollar issued by the Federal Reserve as an example. The Fed regulates its value, making that value relatively stable and preventing &lt;strong&gt;large fluctuations&lt;/strong&gt;. When people transact in dollars, the value of goods remains within a stable range. Centralized money is also backed by national credit: as long as the country remains stable, its currency can circulate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Risk resistance&lt;/strong&gt;. If money is stolen through an accidental or fraudulent transaction, centralized money backed by national credit can help users recover the lost amount through tracing, recovery proceedings, and other means.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Centralization has a major weakness, however: it is vulnerable to a single point of failure. If the central bank at that single point collapses, the centralized money supported by its national credit becomes water without a source. Holders’ money may be reduced directly to zero, and the wealth it represents disappears as well. Centralized money also has the following disadvantages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Single-point control&lt;/strong&gt;. Because money is controlled by a centralized central bank, the money people hold can potentially be &lt;strong&gt;frozen by that central bank&lt;/strong&gt; at any time. After the United States sanctioned Russia, for example, US dollars held by Russian companies in the United States and affiliated countries were frozen indefinitely without justification.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Loss of central-bank credibility&lt;/strong&gt;. A central bank independently decides how much money to issue. If it is incompetent and makes a bad decision that results in &lt;strong&gt;overissuance&lt;/strong&gt;, the ensuing inflation can drastically erode people’s wealth. The now-abandoned Zimbabwean dollar, for example, ceased to function as money because of overissuance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Poor privacy&lt;/strong&gt;. When centralized money is used for transactions, every payment record except small cash transactions is recorded by the central bank. This means payment data that users consider private is visible to centralized institutions. If you buy a steamed bun through Alipay in the morning, for example, the central bank can associate that transaction with your ID whenever it wishes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low cross-border efficiency&lt;/strong&gt;. Centralized money is especially inefficient in cross-border transactions conducted through the SWIFT system. Different countries have different central banks, each issuing its own currency. When centralized currencies issued by different entities are exchanged, settlement passes through multiple multinational banks. More participants make cross-border remittances extremely slow and expensive.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;去中心化货币的机遇与挑战&quot;&gt;Opportunities and Challenges of Decentralized Money&lt;/h2&gt;&lt;p&gt;In countries whose governments enjoy very high public trust, centralized money meets the needs of 99% of people. But there will always be another 1% who are dissatisfied with the status quo and hope to address its flaws through decentralization.&lt;/p&gt;
&lt;p&gt;With decentralized money, issuance and transactions are not dictated by one entity; they are decided collectively by all nodes that use the currency. There is no central bank, yet everyone is a central bank. If issuance and transactions require approval from a majority of community nodes, at least the following problems are addressed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The currency can operate on nodes around the world. The failure of any single node does not affect its basic operation.&lt;/li&gt;
&lt;li&gt;Issuance is no longer controlled by one institution or organization, reducing the risk that a single party will issue money recklessly. The amount issued must be approved by a majority of community nodes, and everyone has the right to participate.&lt;/li&gt;
&lt;li&gt;Transactions are no longer controlled by one institution. Without agreement from a majority of community nodes, no transaction can be rejected or frozen.&lt;/li&gt;
&lt;li&gt;Transactions run on nodes around the world. Domestic and international transactions are fundamentally the same, so cross-border payments can be more efficient than the current SWIFT system. Note that centralized money remains more efficient for domestic payments.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Judging from these problems, decentralized monetary transactions may seem far too “free” for most people. No sovereign state can accept a currency it cannot control through its central bank. An inability to control issuance means that, during a major economic crisis, the state cannot adjust the economy by increasing or reducing the money supply, as happened during the 2008 financial crisis. An inability to control transactions also allows far more illicit and underground transactions to flourish.&lt;/p&gt;
&lt;p&gt;Furthermore, decentralization means that every full node in the community must store all transaction data. As the number of transactions grows, so does the data volume, continually increasing the storage and computing requirements imposed on community members.&lt;/p&gt;
&lt;h1 id=&quot;区块链是什么&quot;&gt;What Is Blockchain?&lt;/h1&gt;&lt;p&gt;Blockchain is one way to implement decentralized money. Combined with cryptographic techniques such as asymmetric encryption, it can implement decentralized cryptocurrency. Several misconceptions need clarification: cryptocurrency is not necessarily decentralized, and decentralized money is not necessarily cryptocurrency. Gold, for example, is a classic decentralized, non-cryptographic currency. With modern digital money, however, a lack of encryption could let A impersonate B. Bitcoin and Trump-themed meme coins, among others, are therefore both cryptocurrencies and decentralized currencies.&lt;/p&gt;
&lt;p&gt;A blockchain consists of individual blocks. Each block has two parts: a block header containing basic information and a block body primarily containing transaction data. There are two kinds of transaction. A coinbase transaction issues currency out of thin air and transfers it to an account. A regular transfer transaction moves money from account A to account B. Every full node in the community maintains the complete blockchain.&lt;/p&gt;
&lt;h2 id=&quot;从宏观上看区块链&quot;&gt;A High-Level View of Blockchain&lt;/h2&gt;&lt;p&gt;Technically, a blockchain is essentially a linked list. In a conventional linked list, the preceding block generally points to the next one, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/715e78c0_btc_img_01.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;What makes a blockchain special is that the next block points back to the previous block. Instead of using a conventional memory-address pointer, it uses a hash pointer.&lt;/p&gt;
&lt;p&gt;A hash pointer is essentially a hash value. Anyone with a computer science background knows that a hash algorithm maps a value X to another value Y through an irreversible process. A common algorithm such as SHA-256 maps an X of any size to a new, fixed-length 256-bit value Y.&lt;/p&gt;
&lt;p&gt;When the next block b points to the preceding block a, block b is essentially recording the hash value of block a, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/ab2a1c74_btc_img_02.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;One advantage is that if the contents of block a change, its hash value recorded in block b also changes. Block b then regards block a as invalid, breaking the chain. To keep the chain intact, a node storing it must alter block a and recalculate the hashes of every subsequent block, because each block’s hash depends on the preceding block. This consumes enormous computing power—for example, under PoW, the node must redo the mining validation. The technical cost is extremely high. More significantly, even after completing the changes, the node must control over 51% of the entire network’s computing power under PoW, or staked assets under PoS, to force other nodes to abandon the original valid chain and accept the altered version. The cost of such a 51% attack far exceeds any benefit from the alteration, making tampering virtually impossible.&lt;/p&gt;
&lt;p&gt;This property of blockchain ensures that every block on the chain is &lt;strong&gt;tamper-resistant&lt;/strong&gt;. That is really all there is to blockchain; nothing about it is inherently complicated. In one sentence, &lt;strong&gt;a blockchain&lt;/strong&gt;&lt;strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85);&quot;&gt; is an immutable database maintained jointly by decentralized nodes&lt;/font&gt;&lt;/strong&gt;&lt;font style=&quot;color:rgba(0, 0, 0, 0.85);&quot;&gt; (this “immutability” is not absolute; in theory, unlimited computing power could alter it, but in practice it is enough to ensure the authenticity and security of on-chain transaction records)&lt;/font&gt;.&lt;/p&gt;
&lt;h2 id=&quot;去中心化货币为什么需要区块链&quot;&gt;Why Does Decentralized Money Need Blockchain?&lt;/h2&gt;&lt;p&gt;Before discussing blockchain further, consider why decentralized money needs it. Since one of blockchain’s major properties is immutability, the question becomes: why must decentralized money be immutable?&lt;/p&gt;
&lt;p&gt;Trust in centralized money comes from the central bank’s backing. People believe that a central bank will honor its debts and that banks will accurately record deposits. Even if banking-system data is altered, the central bank can use its authority to trace and correct it.&lt;br&gt;Decentralized money has no central authority, and every node has equal power. Its “trust” can come only from the technical “authenticity of data.” If transaction records in a block could be altered at will—if A could secretly return a transferred 100 yuan to their own account, for example, or fabricate a transaction that takes B’s money—the entire currency system’s anchor of value would collapse instantly.&lt;/p&gt;
&lt;p&gt;If decentralized transactions are technically immutable, then the data stored in their blocks can be trusted completely. Note that this trust is not the trust placed in a central bank under centralized money, but trust based on technical resistance to alteration.&lt;/p&gt;
&lt;h2 id=&quot;比特币、区块链、以太坊的关系&quot;&gt;The Relationship Among Bitcoin, Blockchain, and Ethereum&lt;/h2&gt;&lt;p&gt;Most people have heard something about Bitcoin, blockchain, and Ethereum, but how exactly do they differ?&lt;/p&gt;
&lt;p&gt;Simply put, Bitcoin and Ethereum are both built on blockchain and both possess the characteristics of decentralized money. Although Ethereum can store and exchange value like a currency, its more important property is support for Smart Contracts.&lt;/p&gt;
&lt;p&gt;Ethereum makes Smart Contract-based Apps distributed. A DApp’s core logic, including transaction rules and asset ownership, can be packaged into blocks through Smart Contracts and then flooding-synced to every Ethereum node worldwide, while frontend resources may still be deployed on centralized machines. As a result, the application no longer exists on a single institution’s server under the absolute control of an App vendor, as it did in the Web2 era. It exists across all node machines, and immutable Smart Contracts prevent even the App vendor from changing rules or manipulating data midway through execution. This is Web3.&lt;/p&gt;
&lt;h1 id=&quot;基于区块链账本的比特币&quot;&gt;Bitcoin Based on a Blockchain Ledger&lt;/h1&gt;&lt;p&gt;Blockchain guarantees that data cannot be tampered with. Building on that property, nodes running Bitcoin must also be capable of issuing currency, supporting valid transactions, and rejecting invalid ones—for example, when A has only 10 yuan but tries to transfer 100 yuan to B.&lt;/p&gt;
&lt;p&gt;There are tens of thousands of Bitcoin nodes worldwide, all with equal authority. This section explores how Bitcoin uses blockchain and other specialized data structures to ensure that transactions and blocks are valid.&lt;/p&gt;
&lt;h2 id=&quot;非对称加密&quot;&gt;Asymmetric Cryptography&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;As mentioned earlier, Bitcoin is a cryptocurrency. We will therefore begin with some of the cryptographic algorithms involved in Bitcoin. We have already briefly discussed hash algorithms, so this section provides a short introduction to asymmetric cryptography.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Each blockchain block records a certain number of transfers. Suppose one transaction in a block transfers 10 BTC from Alice to Bob. From a cryptographic perspective, what encryption work must be done?&lt;/p&gt;
&lt;p&gt;Consider Alice transferring money to Bob through Alipay. Before scanning the code to pay, Alice must enter a password to prove that the current Alipay account is really hers. This involves two steps: first, she sets a password in the centralized Alipay system before making the transfer; second, during the transfer, she enters that password and centralized Alipay verifies it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/27e44fd0_btc_img_03.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Likewise, the initiator of a blockchain transaction must prove that she is Alice. First, Alice should set a password for this transaction. In blockchain, this act is called signing, just as a credit-card receipt may require a signature. When the transaction is about to be packaged into a block, the block performs the second step: verifying the signature’s authenticity.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/23123fe5_btc_img_04.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Because Bitcoin is decentralized, individual nodes cannot store account passwords; if nodes stored those passwords, every account password would be exposed. Asymmetric cryptography makes it possible to sign and verify without revealing a password, thereby authenticating the user.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Asymmetric cryptography uses public and private keys. Readers with computer-related backgrounds likely know that HTTPS is based on asymmetric cryptography. Its simplified process is: 1) the client uses the server’s public key as the key to asymmetrically encrypt content, then transmits it to the server; 2) the server asymmetrically decrypts the content with its own private key.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From the HTTPS process, we can see that the essence of asymmetric cryptography is the one-to-one relationship between a public key, which is disclosed, and a private key, which remains secret. It has two primary scenarios: 1. Encryption: a public key encrypts data, and only the private key can decrypt it, as in HTTPS transmission; 2. Signing: the private key encrypts data such as transaction information to produce a “signature,” and anyone can use the public key to decrypt the signature and verify that the data came from the private-key holder. Together, these scenarios support Bitcoin’s transaction security.&lt;/p&gt;
&lt;h2 id=&quot;比特币账户&quot;&gt;Bitcoin Accounts&lt;/h2&gt;&lt;p&gt;Based on these properties of asymmetric cryptography, when Alice transfers money to Bob, Alice must 1) write the transaction information; 2) sign her transaction with her private key; and 3) include her public-key information so nodes can verify the transaction signature when packaging it into a block.&lt;/p&gt;
&lt;p&gt;We can therefore easily determine how a Bitcoin account is uniquely identified: its account credentials, analogous to a bank-account number and password, are a &lt;strong&gt;public-private key pair conforming to asymmetric cryptography&lt;/strong&gt;. The public key is disclosed for receiving funds and representing the owner’s identity. The private key is kept by the user and used to sign transactions. If someone steals your private key, they can easily transfer the money from your account. Because the system is decentralized, no institution can recover the money even after someone else transfers it away.&lt;/p&gt;
&lt;p&gt;This raises a question: how do we determine the balance of a Bitcoin account?&lt;/p&gt;
&lt;p&gt;In a centralized banking system, a relational database such as Oracle or MySQL can store mappings between accounts and balances. When a user makes a transaction, the centralized system need only run a simple update statement. But in a decentralized system, how can we ensure that every node updates every account affected by transactions in a block? How can we ensure that account data is consistent across nodes?&lt;/p&gt;
&lt;p&gt;This is extremely difficult. We also know that blockchain data cannot be updated once inserted because of its immutable design. An account balance therefore cannot be updated on the Bitcoin blockchain. How can it be represented?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/c5e69815_btc_img_05.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;As shown above, it can be calculated from a transaction ledger. The blockchain records every transaction, including the sender and recipient of BTC. Adding up the transaction records gives the account’s balance. This explains this section’s title: Bitcoin is a cryptocurrency based on a blockchain &lt;strong&gt;ledger&lt;/strong&gt;. Ethereum is account-based and has a more complex MPT design, which is outside this article’s scope.&lt;/p&gt;
&lt;h2 id=&quot;区块链、节点、账户&quot;&gt;Blockchains, Nodes, and Accounts&lt;/h2&gt;&lt;p&gt;The preceding section introduced Bitcoin accounts. Beginners may be confused about whether an account that initiates transactions is equivalent to a node, and whether every node must maintain a blockchain.&lt;/p&gt;
&lt;p&gt;One point must be made clear: accounts and nodes have no necessary relationship. A node is a machine that runs the blockchain. Every node is equivalent and runs the same blockchain. Blocks in the blockchain package information about transactions between accounts. A person without any account can still run a blockchain node, although there is no reward and therefore no reason to do so.&lt;/p&gt;
&lt;p&gt;For example, Alice has an account containing the public key PublicA and private key PrivateA. She owns a machine containing the complete blockchain. That machine is a Bitcoin node.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/9beb844a_btc_img_06.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;There is a problem: as the number of transactions grows, the complete chain requires more storage. If every node stores all information in the chain, every node needs a machine with enormous storage capacity, making Bitcoin prohibitively difficult to use. To allow even a phone to act as a blockchain node and make the system accessible to everyone, the Bitcoin protocol divides nodes into full nodes and lightweight nodes.&lt;/p&gt;
&lt;p&gt;A full node stores all blockchain information and validates every transaction and block. Some full nodes participate in packaging blocks, or mining; these are called “miners.” A lightweight node does not store transaction details, only the header of each blockchain block. It can synchronize valid blocks from full nodes but does not write transactions.&lt;/p&gt;
&lt;h1 id=&quot;比特币的数据结构&quot;&gt;Bitcoin’s Data Structures&lt;/h1&gt;&lt;h2 id=&quot;单个区块的数据结构&quot;&gt;The Structure of a Single Block&lt;/h2&gt;&lt;p&gt;We now know that lightweight nodes store only blockchain block heads, while full nodes store both block heads containing metadata and block bodies containing all transaction information, as shown below. For the exact contents of a recent real block, visit &lt;a href=&quot;https://www.oklink.com/bitcoin/block/914370&quot;&gt;OKLink&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/f67db3e0_btc_img_07.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Several details deserve emphasis. The hash pointer discussed earlier is not a hash of all the data in the preceding block. Suppose block b follows block a. Block b obtains block a’s hash through this process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;When block a is generated, all its transactions are first processed through a Merkle Tree, discussed below, to calculate the final Hash of all transactions, the Merkle root, which is written into its block header.&lt;/li&gt;
&lt;li&gt;Block a then calculates a Hash of its complete header, including the transaction hash and the preceding block’s Hash, producing “block a’s block-header Hash.”&lt;/li&gt;
&lt;li&gt;When block b is generated, “block a’s block-header Hash” is written directly into block b’s header, forming the chain relationship.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;区块中交易的数据结构&quot;&gt;The Data Structure of Transactions in a Block&lt;/h2&gt;&lt;p&gt;Besides blockchain itself, does Bitcoin use other specialized data structures?&lt;/p&gt;
&lt;p&gt;It does. Suppose Alice transfers 10 BTC to Bob. How does Bob know that the money has reached his account? With centralized money, Bob can ask a bank. In Bitcoin’s decentralized setting, if Bob owns a full node, it can traverse all nodes to check whether a transaction says “Alice transferred 10 BTC to Bob.” But what if Bob has a lightweight node, which holds only each block’s head and no transaction information?&lt;/p&gt;
&lt;p&gt;Bob’s lightweight node can only ask an arbitrary full node on the P2P network whether an “Alice transferred 10 BTC to Bob” transaction exists. But because Bitcoin is decentralized, no node is theoretically trustworthy. Suppose Bob’s lightweight node happens to query Alice’s full node and Alice’s node replies that the transaction exists when it actually does not. Bob has effectively been deceived.&lt;/p&gt;
&lt;p&gt;How can this be solved? By using a Merkle Tree to maintain the structure of all transactions.&lt;/p&gt;
&lt;h3 id=&quot;Merkle-Tree&quot;&gt;Merkle Tree&lt;/h3&gt;&lt;p&gt;A Merkle Tree is constructed by hashing transactions in pairs, hashing the resulting hashes in pairs, and continuing recursively until a single root hash, called the Merkle root, remains. All transactions and all hashes generated during this process form a Merkle Tree, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/5f5ac865_btc_img_08.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;The diagram shows that changing a transaction’s hash propagates upward and changes the Merkle root. Because the Merkle root is included in the block header, changing it alters the block header’s hash pointer, which in turn affects every successor block. Accomplishing this is virtually impossible.&lt;/p&gt;
&lt;p&gt;A Merkle Tree has a useful property: given some hash values, namely a Merkle path, one can verify whether a particular transaction exists. In the diagram above, if a lightweight node wants to verify that transaction TX1 exists, the full node need only provide the hash values for TX2, H(2), and H(34). The lightweight node can calculate the Merkle root and compare it with the hash in its own block header. If they match, transaction TX1 really exists.&lt;/p&gt;
&lt;p&gt;In practice, if Bob’s lightweight node wants to verify that transaction TX has been packaged into a block by a full node, the process is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Bob’s lightweight node asks an arbitrary full node on the P2P network to verify transaction TX. Then&lt;/li&gt;
&lt;li&gt;That node returns the Merkle path for transaction TX to Bob. Then&lt;/li&gt;
&lt;li&gt;Bob’s lightweight node independently calculates the “Merkle root” from “TX’s Hash + the Merkle path” and &lt;strong&gt;compares it with the Merkle root stored in its own block header&lt;/strong&gt;. If they match, TX truly exists in the block; if they differ, the path returned by the full node is invalid.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lightweight nodes omit transaction information, but still store every block header in the blockchain, and each header contains a Merkle root. Their validation process essentially compares the correct Merkle root in their own block with the root calculated from the transaction and Merkle path.&lt;/p&gt;
&lt;h2 id=&quot;比特币数据结构概览&quot;&gt;Overview of Bitcoin’s Data Structures&lt;/h2&gt;&lt;p&gt;What exactly does each Bitcoin full node look like? How does it organize its blocks? Where is the Merkle Tree stored?&lt;/p&gt;
&lt;p&gt;The following diagram reveals the data structures inside each full node:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/424a115c_btc_img_09.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;(Note that the block data in every full node’s blockchain is identical.)&lt;/p&gt;
&lt;h1 id=&quot;比特币的共识机制&quot;&gt;Bitcoin’s Consensus Mechanism&lt;/h1&gt;&lt;p&gt;Before analyzing the details of Bitcoin transactions, we must consider how Bitcoin, the original decentralized currency, 1) enables every node to reach consensus and ensures that every node has consistent blockchain data, and 2) prevents malicious nodes from spreading invalid transactions to normal nodes.&lt;/p&gt;
&lt;p&gt;These technical problems are solved using transaction pointers, UTXO, and a multi-node consensus protocol.&lt;/p&gt;
&lt;h2 id=&quot;工作量证明（PoW）&quot;&gt;Proof of Work (PoW)&lt;/h2&gt;&lt;p&gt;Because Bitcoin is decentralized, every node maintains a blockchain. As noted earlier, every node’s blockchain should contain identical blocks and transaction data. Yet every node is independent, so Bitcoin needs &lt;strong&gt;a mechanism through which every node recognizes the block data produced by one node&lt;/strong&gt;. When node A forms its next block, all other nodes must follow that decision, abandon the blocks they were packaging themselves, and use node A’s block as the next block in their own blockchains.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/8e367f8b_btc_img_10.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;When transaction volume is high, node A may package transactions a, b, and c into its block while node B packages b, d, and f into its own. Which of those two blocks should the other nodes follow? In Bitcoin, Proof of Work solves the problem of making every other node recognize a newly packaged block from one node.&lt;/p&gt;
&lt;p&gt;When a Bitcoin node’s software generates a new block, it sets a difficulty value X. To package the block, each node must calculate the hash of its “block header,” which contains the preceding block’s hash, Merkle root, timestamp, difficulty target, nonce, and other data. The resulting hash value Y must meet the specific requirement imposed by difficulty X. Because a hash value is unpredictable, a node can only keep changing the nonce and repeatedly calculating until it obtains a value Y that meets X. Only then is the new block considered valid and eligible to be accepted by other nodes after broadcast. &lt;strong&gt;This process of trying different nonce values until Y satisfies X is called Proof of Work (PoW)&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Every 2,016 blocks, the Bitcoin system dynamically adjusts difficulty X according to the total computing power of all nodes, keeping the average block-packaging time near ten minutes. A transaction therefore takes about ten minutes to be published to the blockchain, while confirmation requires waiting for six subsequent blocks, or roughly an hour. This cannot begin to compare with Alipay’s routinely hundreds of thousands of TPS. The process in which nodes calculate Y to package a block is called mining, and the nodes that do it are called miners.&lt;/p&gt;
&lt;h3 id=&quot;为什么要基于PoW&quot;&gt;Why Use PoW?&lt;/h3&gt;&lt;p&gt;Some may ask why every miner should compete for the right to publish blocks through PoW’s seemingly pointless computation. It simply wastes computing power and electricity; even after the Yarlung Tsangpo dam is completed, it might not withstand consumption on this scale.&lt;/p&gt;
&lt;p&gt;Why not use time? Why not simply follow whoever publishes a block first?&lt;/p&gt;
&lt;p&gt;That does not work, for two reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Each node can be regarded as an independent computer. When relying only on local clocks, times can differ across machines, and a node could even deliberately set its local clock ahead. Different nodes would then disagree about the latest time.&lt;/li&gt;
&lt;li&gt;If blocks were packaged based on time, large numbers of different blocks would be published to the network during the same period, creating many forks. Yet &lt;strong&gt;only the longest chain can be treated as valid&lt;/strong&gt;. Many published blocks would consequently be useless, wasting computing power and harming the Bitcoin community.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/23b7f548_btc_img_11.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;As the blockchain community has developed, Ethereum has introduced a voting mechanism that does not waste computing power, Proof of Stake (PoS). This article focuses on Bitcoin, so PoS will not be discussed further.&lt;/p&gt;
&lt;h3 id=&quot;矿工为什么要挖矿&quot;&gt;Why Do Miners Mine?&lt;/h3&gt;&lt;p&gt;Why would miners waste vast amounts of computing power on mining? Are they simply overflowing with geek spirit and volunteering to package every community transaction into a block? Clearly not. To sustain a thriving community, when miners package transactions into blocks, the system rewards the first miner to calculate a nonce that meets the difficulty requirement. Besides allowing that miner to package and publish the block, it grants the miner a BTC reward.&lt;/p&gt;
&lt;p&gt;Unlike a transfer, this reward has no input; &lt;strong&gt;it generates new Bitcoin “out of thin air”&lt;/strong&gt;. This is what we commonly call a &lt;strong&gt;coinbase transaction&lt;/strong&gt;. Through such transactions, Bitcoin acquires the ability to issue currency.&lt;/p&gt;
&lt;p&gt;Bitcoin distributes a block reward about every 10 minutes, and the reward halves every 4 years, or 210,000 blocks. The current reward is 3.125 BTC per block. At Bitcoin’s current &lt;a href=&quot;https://www.oklink.com/bitcoin&quot;&gt;price&lt;/a&gt; of USD 110,000 per coin, publishing one block earns a miner USD 360,000—more than an ordinary person may earn in a lifetime.&lt;/p&gt;
&lt;h2 id=&quot;交易合法&quot;&gt;Valid Transactions&lt;/h2&gt;&lt;p&gt;We noted earlier that if Alice transfers 10 BTC to Bob, she must sign the transaction with her private key. Other nodes use Alice’s public key to verify it, and the transaction is valid only if verification produces the expected result.&lt;/p&gt;
&lt;p&gt;Because Bitcoin is ledger-based and has no account concept, however, asymmetric cryptography verifies the transaction initiator’s identity but not whether the initiator has sufficient funds. Two situations require attention:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;u&gt;Suppose Alice previously earned a 10 BTC block reward from mining and tries to transfer 20 BTC to Bob. How does a block reject the transaction?&lt;/u&gt;&lt;/li&gt;
&lt;li&gt;&lt;u&gt;Suppose Alice previously earned a 10 BTC block reward from mining, already transferred 10 BTC to Bob, and later attempts to transfer another 10 BTC to Bob. How does a block reject it?&lt;/u&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;交易指针&quot;&gt;Transaction Pointers&lt;/h3&gt;&lt;p&gt;The solution is simple. When publishing a transaction, Bitcoin requires the sender to use a hash pointer to point that transaction to the earlier transaction through which the sender obtained the Bitcoin, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/22a079e5_btc_img_12.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;The node packaging the transaction can then easily use the hash pointer to find the transaction through which Alice obtained her BTC and validate it. “Alice transfers 20 BTC to Bob, but Alice previously received only 10 BTC” is invalid, so validation fails. This solves the first problem.&lt;/p&gt;
&lt;p&gt;The second problem remains. Suppose Alice creates TX2 immediately after TX1 and transfers another 10 BTC to Bob, as shown below. A transaction pointer alone cannot detect this “double-spend transaction.” Nodes therefore need to maintain state recording whether the BTC output from each transaction has already been spent.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/fd9c71b0_btc_img_13.png&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;UTXO&quot;&gt;UTXO&lt;/h3&gt;&lt;p&gt;The data structure that records whether transaction outputs have been spent is called Unspent Transaction Output (UTXO).&lt;/p&gt;
&lt;p&gt;UTXO can be understood as a map keyed by transaction and recipient, whose value records whether another transaction has spent the current transaction’s output. In the diagram above, TX0 gives Alice a 10 BTC block reward, so the UTXO for the block containing TX0 is &lt;code&gt;{&apos;TX0:Alice&apos;: false}&lt;/code&gt;, indicating that TX0’s output has not been spent. Once TX1 is packaged into a block, the UTXO for the block containing TX0 becomes &lt;code&gt;{&apos;TX0:Alice&apos;: true}&lt;/code&gt;, indicating that TX0’s output has been spent. If TX2 later points to TX0, validation finds that TX0’s output has already been spent and classifies TX2 as invalid, so it is not packaged into a block.&lt;/p&gt;
&lt;p&gt;Unlike the immutable blockchain, this UTXO can change. It is not part of the blockchain and does not participate in hash calculations. Nodes &lt;strong&gt;do not&lt;/strong&gt; synchronize their block’s UTXO state to other nodes over the network. Instead, each node locally constructs UTXO whenever it updates its blocks. As new blocks are added, UTXO changes dynamically according to the transactions in the blockchain.&lt;/p&gt;
&lt;p&gt;UTXO exists to help nodes validate transactions more quickly. Without it, a node trying to prevent a “double-spend transaction” would have to traverse every transaction in every block from newest to oldest. With about 910,000 blocks today, even a single traversal would impose considerable computational cost.&lt;/p&gt;
&lt;h2 id=&quot;区块合法&quot;&gt;Valid Blocks&lt;/h2&gt;&lt;p&gt;Transaction pointers and UTXO let a node accurately determine whether transactions are valid while packaging a block. Only valid transactions are packaged. If an abnormal node packages an invalid transaction into its block, the other nodes on the network will not recognize that block.&lt;/p&gt;
&lt;p&gt;But if every transaction in a block is valid, will the other network nodes necessarily accept it? No. Valid transactions are only a necessary condition for a valid block. Validating a block also requires the PoW described earlier.&lt;/p&gt;
&lt;h3 id=&quot;如何验证区块合法&quot;&gt;How to Validate a Block&lt;/h3&gt;&lt;p&gt;After a node publishes a block to the network, other nodes verify its PoW by calculating the hash from the block header’s nonce, pre hash, Merkle root, and other fields and checking whether it meets the mining difficulty. If the Proof of Work is valid, they then validate each transaction using transaction pointers and UTXO. Only when every transaction in the block is valid do the other nodes recognize the block as valid.&lt;/p&gt;
&lt;p&gt;Yet a valid block is not necessarily accepted by the other nodes.&lt;/p&gt;
&lt;p&gt;Consider a node that produces a valid new block A whose transactions are also valid, but whose pre hash points to a block from several years ago, as shown below. Will other nodes accept it?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/72ca1d6d_btc_img_14.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;They will not. A Bitcoin blockchain may have multiple forks, and because transactions can differ across forks, multiple branches cannot all be valid. Bitcoin recognizes only one blockchain as valid: the longest one, meaning the one with the greatest accumulated work. When block A pointing to a very old block is published to the network, other nodes compare the work difficulty of block A’s chain with the accumulated work difficulty of their own local chain. If A’s chain has less work, block A is discarded without mercy. Its transactions do not achieve consensus on the Bitcoin network, and the node that produced A mined for nothing.&lt;/p&gt;
&lt;p&gt;To avoid this, whenever a miner discovers a blockchain longer than its own on the network, it must immediately update the pre hash of the block it is packaging and recalculate the nonce and other parameters to complete PoW again.&lt;/p&gt;
&lt;h3 id=&quot;同时产生合法区块怎么办&quot;&gt;What If Valid Blocks Are Produced Simultaneously?&lt;/h3&gt;&lt;p&gt;Because mining is nondeterministic, two nodes may produce valid blocks with equal work at the same time, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/d01e1eab_btc_img_15.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Other nodes will initially accept both blocks A and B because they contain the same amount of work. Due to network uncertainty, however, some nodes will mine from BLOCK A while others mine from BLOCK B. If a new block is mined after BLOCK A first, the other nodes regard the chain containing BLOCK A as the longest valid chain and discard BLOCK B. Once BLOCK B is discarded, its coinbase transaction awarding currency to the corresponding account also becomes invalid.&lt;/p&gt;
&lt;p&gt;To prevent uncertainty over which node’s transactions to follow when two nodes produce blocks simultaneously, Bitcoin’s real-world payment rule considers transactions in BLOCK A valid only after six additional blocks have been added behind it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/853decfb_btc_img_16.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Thus, &lt;strong&gt;Bitcoin is a consensus game decided by every node in the community, rather than a sovereignty game decided by a central institution&lt;/strong&gt;. That may be the appeal of decentralization.&lt;/p&gt;
&lt;h1 id=&quot;一笔比特币交易的产生过程&quot;&gt;The Lifecycle of a Bitcoin Transaction&lt;/h1&gt;&lt;p&gt;Using the consensus mechanism and Bitcoin data structures described in this article, let us reconstruct how every node in the Bitcoin network changes when “Alice transfers 10 BTC to Bob”:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/9fd2ee78_btc_img_17.svg&quot;&gt;&lt;/p&gt;
</content:encoded></item>
<item><title>A MacBook Pro Setup Guide for Developers</title><link>https://wxxlamp.cn/en/2025/08/15/how-to-use-mac/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2025/08/15/how-to-use-mac/</guid><pubDate>Fri, 15 Aug 2025 04:05:00 GMT</pubDate><description>A detailed guide to the macOS directory structure and development best practices, including common commands, environment setup, runtime version management, and system administration tips.</description><content:encoded>&lt;p&gt;In the blink of an eye, I had been at the company for four years, and it replaced my old machine with a 14-inch MacBook Pro as my new master.&lt;/p&gt;
&lt;p&gt;When I first encountered a Mac four years ago, I installed everything by clicking Next. That occasionally produced baffling environment problems: &lt;code&gt;mvn&lt;/code&gt; could not be found, Python versions differed, and so on. With the old 13-inch MBP officially retiring, this was a good opportunity to understand macOS properly.&lt;/p&gt;
&lt;p&gt;I also did not know where the data from all those installations was stored. I therefore used the opportunity to organize and study basic macOS concepts and operations systematically.&lt;/p&gt;
&lt;p&gt;Readers interested only in practical setup can jump directly to the second part.&lt;/p&gt;
&lt;h1 id=&quot;Mac的目录结构&quot;&gt;The Mac Directory Structure&lt;/h1&gt;&lt;p&gt;Unix-like systems do not require Windows-style disk partitions, so there is no concern that excessive junk on drive C will impair the OS. When downloading on a Mac, we need not decide which disk should hold the file, reducing decision cost. Beginners should nevertheless understand the directory structure roughly.&lt;/p&gt;
&lt;h2 id=&quot;1-1-系统目录&quot;&gt;1.1 System Directories&lt;/h2&gt;&lt;p&gt;Ordinary use rarely touches system directories. Common examples include:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/Applications&lt;/code&gt;, where applications shared by all users are installed. Homebrew installs applications there by default.&lt;/p&gt;
&lt;p&gt;The following table lists common system directories. The first four are most relevant to daily development and use.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Directory&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Main purpose&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Characteristics&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Permissions&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Common use&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;/Applications&lt;/td&gt;
&lt;td&gt;System-wide applications&lt;/td&gt;
&lt;td&gt;Shared by all users&lt;/td&gt;
&lt;td&gt;Administrator required&lt;/td&gt;
&lt;td&gt;Applications for every user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/bin&lt;/td&gt;
&lt;td&gt;Basic system commands&lt;/td&gt;
&lt;td&gt;Core executables&lt;/td&gt;
&lt;td&gt;System protected&lt;/td&gt;
&lt;td&gt;Basic commands such as ls, cp, mv&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/Volumes&lt;/td&gt;
&lt;td&gt;Mount points&lt;/td&gt;
&lt;td&gt;External devices&lt;/td&gt;
&lt;td&gt;System managed&lt;/td&gt;
&lt;td&gt;USB and network-drive access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/opt&lt;/td&gt;
&lt;td&gt;Third-party packages&lt;/td&gt;
&lt;td&gt;Optional software&lt;/td&gt;
&lt;td&gt;Administrator required&lt;/td&gt;
&lt;td&gt;Package managers such as Homebrew&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/System&lt;/td&gt;
&lt;td&gt;Core macOS files&lt;/td&gt;
&lt;td&gt;Protected and read-only&lt;/td&gt;
&lt;td&gt;System protected&lt;/td&gt;
&lt;td&gt;⚠️ Required by the OS; do not modify&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/Library&lt;/td&gt;
&lt;td&gt;System-wide libraries and configuration&lt;/td&gt;
&lt;td&gt;Shared resources&lt;/td&gt;
&lt;td&gt;Administrator required&lt;/td&gt;
&lt;td&gt;Frameworks, plugins, fonts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/Users&lt;/td&gt;
&lt;td&gt;User home directories&lt;/td&gt;
&lt;td&gt;Contains every user’s folder&lt;/td&gt;
&lt;td&gt;System managed&lt;/td&gt;
&lt;td&gt;Root of user-data storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/sbin&lt;/td&gt;
&lt;td&gt;Administration commands&lt;/td&gt;
&lt;td&gt;Administrator utilities&lt;/td&gt;
&lt;td&gt;Root required&lt;/td&gt;
&lt;td&gt;System maintenance and configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/usr&lt;/td&gt;
&lt;td&gt;User programs and libraries&lt;/td&gt;
&lt;td&gt;User-level system programs&lt;/td&gt;
&lt;td&gt;Some actions require administrator&lt;/td&gt;
&lt;td&gt;Programming tools and libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/etc&lt;/td&gt;
&lt;td&gt;System configuration&lt;/td&gt;
&lt;td&gt;Global configuration&lt;/td&gt;
&lt;td&gt;Administrator required&lt;/td&gt;
&lt;td&gt;Services and network settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/var&lt;/td&gt;
&lt;td&gt;Variable data&lt;/td&gt;
&lt;td&gt;Dynamically changing files&lt;/td&gt;
&lt;td&gt;Mixed permissions&lt;/td&gt;
&lt;td&gt;Logs, caches, mail queues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/tmp&lt;/td&gt;
&lt;td&gt;Temporary files&lt;/td&gt;
&lt;td&gt;Cleared after restart&lt;/td&gt;
&lt;td&gt;Writable by all users&lt;/td&gt;
&lt;td&gt;Temporary storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/dev&lt;/td&gt;
&lt;td&gt;Device files&lt;/td&gt;
&lt;td&gt;Hardware interfaces&lt;/td&gt;
&lt;td&gt;System managed&lt;/td&gt;
&lt;td&gt;Hardware-device access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/private&lt;/td&gt;
&lt;td&gt;Private system files&lt;/td&gt;
&lt;td&gt;Internal files&lt;/td&gt;
&lt;td&gt;System protected&lt;/td&gt;
&lt;td&gt;⚠️ Internal use; do not modify&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/home&lt;/td&gt;
&lt;td&gt;User-directory symbolic link&lt;/td&gt;
&lt;td&gt;Points to /Users&lt;/td&gt;
&lt;td&gt;System managed&lt;/td&gt;
&lt;td&gt;Unix compatibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/cores&lt;/td&gt;
&lt;td&gt;Core dumps&lt;/td&gt;
&lt;td&gt;Crash information&lt;/td&gt;
&lt;td&gt;System generated&lt;/td&gt;
&lt;td&gt;Debugging crashed programs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;1-2-用户目录&quot;&gt;1.2 User Directories&lt;/h2&gt;&lt;p&gt;Daily work happens mainly under &lt;code&gt;/Users/$user/&lt;/code&gt;, so most ordinary reads and writes occur there. Like Windows, macOS recommends standard locations for user data:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Directory&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Main purpose&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Characteristics&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Recommended use&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;~/Applications&lt;/td&gt;
&lt;td&gt;Personal applications&lt;/td&gt;
&lt;td&gt;Current user only&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Best location for personal applications&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Documents&lt;/td&gt;
&lt;td&gt;Personal documents&lt;/td&gt;
&lt;td&gt;Main workspace&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Documents, projects, and code&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Downloads&lt;/td&gt;
&lt;td&gt;Downloads&lt;/td&gt;
&lt;td&gt;Browser default&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Default downloaded-file location&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Desktop&lt;/td&gt;
&lt;td&gt;Desktop files&lt;/td&gt;
&lt;td&gt;Visible on desktop&lt;/td&gt;
&lt;td&gt;Shortcuts and temporary files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Library&lt;/td&gt;
&lt;td&gt;User configuration and data&lt;/td&gt;
&lt;td&gt;Application configuration&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Developer configuration files&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Pictures&lt;/td&gt;
&lt;td&gt;Images and photographs&lt;/td&gt;
&lt;td&gt;Media management&lt;/td&gt;
&lt;td&gt;Photos, screenshots, image assets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Movies&lt;/td&gt;
&lt;td&gt;Video files&lt;/td&gt;
&lt;td&gt;Video library&lt;/td&gt;
&lt;td&gt;Personal videos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~/Music&lt;/td&gt;
&lt;td&gt;Audio files&lt;/td&gt;
&lt;td&gt;Music library&lt;/td&gt;
&lt;td&gt;Music and audio&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;1-3-常用配置文件&quot;&gt;1.3 Common Configuration Files&lt;/h2&gt;&lt;p&gt;The user home also contains important configuration files loaded when terminal sessions initialize. Nonsystem commands such as Java, Maven, and Node are commonly added to the environment through these files.&lt;/p&gt;
&lt;p&gt;Before macOS Catalina, Bash used &lt;code&gt;.bash_profile&lt;/code&gt; and &lt;code&gt;.bashrc&lt;/code&gt;. They remain for compatibility, while Catalina and later recommend &lt;code&gt;.zprofile&lt;/code&gt; and &lt;code&gt;.zshrc&lt;/code&gt; for Zsh.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;Terminal.app&lt;/code&gt; opens, the system executes the login profile scripts. The detailed sequence is:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;login → /etc/zshenv → ~/.zshenv → /etc/zprofile → ~/.zprofile → /etc/zshrc → ~/.zshrc → /etc/zlogin → ~/.zlogin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Broadly, &lt;code&gt;.zprofile&lt;/code&gt; runs at login, while &lt;code&gt;.zshrc&lt;/code&gt; runs for each new interactive terminal, including one opened inside VS Code. Put login initialization in &lt;code&gt;.zprofile&lt;/code&gt;, and functions and aliases in &lt;code&gt;.zshrc&lt;/code&gt;. Avoid repeatedly appending &lt;code&gt;PATH&lt;/code&gt; or &lt;code&gt;JAVA_HOME&lt;/code&gt; there in ways that produce a duplicated, extremely long path and slow &lt;code&gt;which&lt;/code&gt;. GUI applications may also read &lt;code&gt;.zprofile&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;File&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Shell&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;When loaded&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Main purpose&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Status&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.zprofile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zsh&lt;/td&gt;
&lt;td&gt;Once at login&lt;/td&gt;
&lt;td&gt;Login initialization and GUI environment&lt;/td&gt;
&lt;td&gt;🟢 Recommended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.zshrc&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zsh&lt;/td&gt;
&lt;td&gt;Every new terminal session&lt;/td&gt;
&lt;td&gt;Interactive Zsh configuration&lt;/td&gt;
&lt;td&gt;🟢 Main configuration file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.bash_profile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bash&lt;/td&gt;
&lt;td&gt;Once at login&lt;/td&gt;
&lt;td&gt;Bash environment configuration&lt;/td&gt;
&lt;td&gt;🟡 Retained for compatibility&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;To apply updated shell configuration, run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;source ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;1-4-隐秘的文件夹&quot;&gt;1.4 Hidden Folders&lt;/h2&gt;&lt;p&gt;The Mac user home contains many configuration folders beginning with &lt;code&gt;.&lt;/code&gt;, generally storing settings for programs such as pyenv, Git, nvm, and VS Code:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/b7b51d7e_mac_folders.png&quot; alt=&quot;Mac hidden-folder structure&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;如何下载常用的工具&quot;&gt;Downloading Common Tools&lt;/h1&gt;&lt;p&gt;As on Windows, Mac software can be installed through the App Store or packages downloaded in a browser. As a Unix-like system, macOS also offers more efficient methods.&lt;/p&gt;
&lt;h2 id=&quot;常用的下载工具&quot;&gt;Common Download Tools&lt;/h2&gt;&lt;p&gt;A development Mac needs many programs without GUIs, for which browser downloads are inconvenient. Homebrew and &lt;code&gt;curl&lt;/code&gt; are common choices. I recommend Homebrew, a Mac package manager that installs, removes, and upgrades software consistently and avoids incomplete later removal. Note that Homebrew is not designed to manage several versions of one tool simultaneously. For Python 2 and Python 3 side by side, use a specialized version manager such as pyenv, discussed below.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Scenario&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Recommended tool&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Reason&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Storage location&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Ordinary downloads&lt;/td&gt;
&lt;td&gt;Safari/Chrome&lt;/td&gt;
&lt;td&gt;Simple, safe, reliable&lt;/td&gt;
&lt;td&gt;~/Downloads/, Chrome application-support and app folders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Batch or automated downloads&lt;/td&gt;
&lt;td&gt;curl&lt;/td&gt;
&lt;td&gt;Scriptable and customizable&lt;/td&gt;
&lt;td&gt;Chosen with &lt;code&gt;-o&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GUI installation&lt;/td&gt;
&lt;td&gt;App Store&lt;/td&gt;
&lt;td&gt;Official integration&lt;/td&gt;
&lt;td&gt;App Store support and ~/Applications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command-line tools&lt;/td&gt;
&lt;td&gt;Homebrew&lt;/td&gt;
&lt;td&gt;Unified package management like apt&lt;/td&gt;
&lt;td&gt;/opt/homebrew/&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;常见的homebrew命令&quot;&gt;Common Homebrew Commands&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 查看Homebrew安装路径
brew --prefix

# 查看某个包的安装位置
brew --prefix &amp;lt;package-name&amp;gt;

# 清理缓存
brew cleanup

# 查看Homebrew占用空间
du -sh $(brew --prefix)

#删除缓存文件
rm -rf &quot;$(brew --cache)&quot;

#重新安装损坏的软件
brew reinstall git

# 查看安装的软件及版本
brew list --versions

#每日维护例程
brew update &amp;amp;&amp;amp; brew upgrade &amp;amp;&amp;amp; brew cleanup

#搜索并安装软件
brew search python
brew info python@3.11
brew install python@3.11

# 服务管理
brew install mysql
brew services start mysql
brew services list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When using Homebrew in mainland China, configure a domestic mirror.&lt;/p&gt;
&lt;h1 id=&quot;常用的Mac-App&quot;&gt;Common Mac Applications&lt;/h1&gt;&lt;p&gt;Here are useful applications and ways to install them.&lt;/p&gt;
&lt;h2 id=&quot;日常&quot;&gt;Daily Use&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Application&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Installation&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;Primary browser&lt;/td&gt;
&lt;td&gt;brew, browser, App Store&lt;/td&gt;
&lt;td&gt;Main browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;Backup browser&lt;/td&gt;
&lt;td&gt;brew, browser, App Store&lt;/td&gt;
&lt;td&gt;A second browser for special cases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sogou Input&lt;/td&gt;
&lt;td&gt;Familiar input method&lt;/td&gt;
&lt;td&gt;brew, browser, App Store&lt;/td&gt;
&lt;td&gt;Better memory than Apple’s, but consider privacy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WeChat&lt;/td&gt;
&lt;td&gt;Communication&lt;/td&gt;
&lt;td&gt;brew, browser, App Store&lt;/td&gt;
&lt;td&gt;Two instances can run on Mac&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maccy&lt;/td&gt;
&lt;td&gt;Clipboard&lt;/td&gt;
&lt;td&gt;brew, GitHub&lt;/td&gt;
&lt;td&gt;Keeps roughly fifty clipboard entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scroll Reverser&lt;/td&gt;
&lt;td&gt;Mouse scrolling&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://scroll-reverser.macupdate.com/&quot;&gt;Browser&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Makes an external mouse scroll like Windows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;效率工具&quot;&gt;Productivity Tools&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Application&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Installation&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;iTerm2&lt;/td&gt;
&lt;td&gt;Terminal&lt;/td&gt;
&lt;td&gt;brew, browser&lt;/td&gt;
&lt;td&gt;More capable than Terminal.app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oh My Zsh&lt;/td&gt;
&lt;td&gt;Zsh customization&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/ohmyzsh/ohmyzsh&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Themes, highlighting, completion, fast directory jumps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IntelliJ IDEA&lt;/td&gt;
&lt;td&gt;Java development&lt;/td&gt;
&lt;td&gt;Browser, JetBrains Toolbox&lt;/td&gt;
&lt;td&gt;JetBrains JVM suite; Rainbow plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PyCharm&lt;/td&gt;
&lt;td&gt;Python development&lt;/td&gt;
&lt;td&gt;Browser, JetBrains Toolbox&lt;/td&gt;
&lt;td&gt;JetBrains Python IDE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebStorm&lt;/td&gt;
&lt;td&gt;JavaScript development&lt;/td&gt;
&lt;td&gt;Browser, JetBrains Toolbox&lt;/td&gt;
&lt;td&gt;JetBrains web IDE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code&lt;/td&gt;
&lt;td&gt;Text editing&lt;/td&gt;
&lt;td&gt;brew, browser&lt;/td&gt;
&lt;td&gt;Python/JS; convenient &lt;code&gt;code xx&lt;/code&gt; command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LightProxy&lt;/td&gt;
&lt;td&gt;Proxy tool&lt;/td&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;td&gt;Development traffic monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/bannedbook/ClashX&quot;&gt;ClashX&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Proxy&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/bannedbook/ClashX&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Network proxy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;Virtualization&lt;/td&gt;
&lt;td&gt;brew&lt;/td&gt;
&lt;td&gt;Both cask and CLI are available&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Oh My Zsh is best used with several plugins:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Switch to the &lt;a href=&quot;https://github.com/spaceship-prompt/spaceship-prompt&quot;&gt;Spaceship theme&lt;/a&gt;; install &lt;a href=&quot;https://github.com/zsh-users/zsh-syntax-highlighting/tree/master&quot;&gt;zsh-syntax-highlighting&lt;/a&gt; to highlight commands; install &lt;a href=&quot;https://github.com/zsh-users/zsh-autosuggestions&quot;&gt;zsh-autosuggestions&lt;/a&gt; for history completion; and install &lt;a href=&quot;https://github.com/wting/autojump&quot;&gt;autojump&lt;/a&gt; for fast directory navigation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;开发环境&quot;&gt;Development Environment&lt;/h2&gt;&lt;h3 id=&quot;git支持多仓库&quot;&gt;Git with Multiple Repositories&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Use SSH tools to generate two private/public key pairs and copy the public keys into the corresponding repository settings.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;ssh-keygen -t rsa -b 4096 -C &quot;wxxlamp@foxmail.com&quot; -f ~/.ssh/id_rsa_person
ssh-keygen -t rsa -b 4096 -C &quot;wxxlamp@work.com&quot; -f ~/.ssh/id_rsa_work
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Configure the keys in GitHub and the GitLab repository, then configure &lt;code&gt;.ssh/config&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# GitHub
Host github.com
    HostName github.com
    User wxx

# 对应工作仓库
Host gitlab.work.com
    HostName gitlab.work.com
    User work
    IdentityFile ~/.ssh/id_rsa_work
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Test connectivity with &lt;code&gt;ssh -T@github.com&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;Java-Maven-Runtime&quot;&gt;Java and Maven Runtimes&lt;/h3&gt;&lt;p&gt;Because Homebrew does not manage multiple versions of one application conveniently, Java, Python, and Node need specialized tools.&lt;/p&gt;
&lt;p&gt;For the JVM ecosystem, SDKMAN can manage Java and Maven versions globally:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 1. 安装 SDKMAN
curl -s &quot;https://get.sdkman.io&quot; | bash
source &quot;$HOME/.sdkman/bin/sdkman-init.sh&quot;

# 2. 查询并安装 Java 21
sdk list java
sdk install java 21.0.4-tem

# 3. 设为默认
sdk default java 21.0.4-tem

# 4. 安装 Maven 最新版
sdk install maven

# 5. 查看结果
sdk current
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Common SDKMAN commands:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example or note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;List candidates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Supported languages and tools&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sdk list&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;List versions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Versions of one candidate&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sdk list java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Install a version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Install OpenJDK 17&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sdk install java 17.0.12&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Install latest stable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Omit the version&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sdk install maven&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use an installed version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Current shell only&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sdk use java 21.0.4-tem&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Set default version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Global default&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sdk default java 21.0.4-tem&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Show current versions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All candidates&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sdk current&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Show one current version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Java only&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sdk current java&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Uninstall a version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Remove local copy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sdk uninstall java 11.0.25&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Show installation path&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Useful in scripts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sdk home java 17.0.12-tem&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;Python-runtime&quot;&gt;Python Runtime&lt;/h3&gt;&lt;p&gt;Python has an overwhelming number of managers. macOS also ships a Python runtime at &lt;code&gt;/usr/bin/python3&lt;/code&gt;, which is sufficient if you are not developing Python projects. Many programs require particular Python versions, however, so third-party tools manage versions and packages.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;&lt;strong&gt;Tool&lt;/strong&gt;&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;&lt;strong&gt;Core purpose&lt;/strong&gt;&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;&lt;strong&gt;Typical use&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;venv&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Lightweight virtual environments&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Dependency isolation only&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Simple projects; built into Python 3.3+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;virtualenv&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Enhanced virtual environments&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Dependency isolation only&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Multiple projects; Python 2/3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;pyenv&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Python version management&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Interpreter versions only&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Several Python versions side by side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;conda&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Environment management beyond Python&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Versions, environments, packages&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Data science and cross-language projects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Put simply, pyenv switches Python versions, virtualenv provides isolated dependency environments, and conda extends environment management to non-Python scientific packages.&lt;/p&gt;
&lt;p&gt;I recommend pyenv for global Python versions and pyenv-virtualenv for virtual environments. Conda can itself be installed as a pyenv-managed environment; switch to it when a project requires non-Python packages.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;These tools implement environments differently.&lt;/p&gt;
&lt;p&gt;Pyenv first ensures that &lt;code&gt;PATH&lt;/code&gt; points to pyenv. Setting a global version writes it to &lt;code&gt;~/.pyenv/version&lt;/code&gt;. Setting a local version applies only inside one folder by creating &lt;code&gt;.python-version&lt;/code&gt; at the project root.&lt;/p&gt;
&lt;p&gt;Activating virtualenv creates a &lt;code&gt;venv&lt;/code&gt; directory that stores the environment’s dependencies. It uses the Python version selected by pyenv rather than copying another interpreter. A new shell must reactivate the environment.&lt;/p&gt;
&lt;p&gt;Activating conda creates an environment directory beneath the conda installation, copies a Python interpreter there, and stores later dependencies in that directory. A new shell must reactivate it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Common commands:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# 确认当前python的路径
which python3

#======= pyenv command
# List all installed Python versions in pyenv
pyenv versions

# Install a specific Python version (if not already installed):
pyenv install 3.11.4

# set the python system or current versions
pyenv global 3.11.4  

cd /path/to/your/project
pyenv local 3.10.8  # Creates a .python-version file in the project

#======= conda command
# 通过pyenv管理conda环境
pyenv install miniconda3-latest

# shell级别切换python版本
pyenv shell miniconda3-latest

# List all conda environments:
conda env list

# Create a new environment with a specific Python version
conda create --name myenv python=3.9 

# Activate an environment 
conda activate myenv
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;# 查看当前pip安装的依赖地址
pip show pip | grep Location
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;Node-runtime&quot;&gt;Node Runtime&lt;/h3&gt;&lt;p&gt;Use nvm to manage Node versions. Installing nvm directly with &lt;code&gt;curl&lt;/code&gt; is recommended.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
nvm install --lts     # 安装最新的 LTS 版本
nvm install 20.12.2   # 安装指定版本
nvm use 20.12.2       # 切换到指定版本
nvm current
nvm list
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item>
<item><title>My 2024: Promotion, a Half Marathon, and Studying Across Hong Kong and Shenzhen</title><link>https://wxxlamp.cn/en/2025/03/07/my-2024/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2025/03/07/my-2024/</guid><pubDate>Fri, 07 Mar 2025 12:19:00 GMT</pubDate><description>My 2024 year-end review: earning a promotion and setting a 1:46 personal best in my first half marathon. After receiving an offer from HKUST, I transferred to Shenzhen and began commuting between Hong Kong and Shenzhen. I earned A grades in all three courses that semester, gained extensive knowledge of finance and cross-border payments, and searched for direction in a completely new environment.</description><content:encoded>&lt;p&gt;I am rather embarrassed that I write my annual review later every year. Time may be like water in a sponge—you can always squeeze some out if you try—but deadlines remain the greatest source of productivity. I did not even begin this year’s review until the end of March 2025.&lt;/p&gt;
&lt;p&gt;The past cannot be changed, but the future can still be pursued. I hope my 2025 review will not be delayed as badly as this one.&lt;/p&gt;
&lt;p&gt;Overall, 2024 felt fresh, fulfilling, and confusing. I entered new environments, and both my way of working and my way of learning changed. I was admitted to HKUST’s MSc program, transferred to a position in Shenzhen, and began commuting between Hong Kong and Shenzhen. Leaving behind my stable relationships and work environment in Hangzhou inevitably made the adjustment somewhat bumpy, yet I also felt eager to try what lay ahead.&lt;/p&gt;
&lt;h3 id=&quot;2024年目标达成情况&quot;&gt;Progress on My 2024 Goals&lt;/h3&gt;&lt;p&gt;In previous years I always reviewed milestones. This year I will use a different approach with two parts: first, reviewing my progress on the goals I set for 2024, and second, listing what I actually did.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Maintain a regular schedule: get up at 8:30 a.m. and go to sleep at midnight. Spend at least one and a half hours studying and writing on weekdays, and at least six hours on weekends, unless something unusual happens.&lt;/strong&gt; ===&amp;gt; 40%. Because I began studying in the second half of the year, I usually got up at 9:30 a.m. and went to bed at 2:30 a.m. Reality was completely different from the plan, so this goal fell apart. I generally did preserve the planned study and writing time on weekdays and weekends, although my actual output also fell somewhat short of expectations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep exercising: six sessions of pull-ups and crunches per week, with two or three sets each time; have at least four visible abdominal muscles by year-end; and run 800 km during the year.&lt;/strong&gt; ===&amp;gt; 70%. I mostly kept up the crunches and pull-ups. I had achieved the four-pack by midyear, although it disappeared again by year-end. I ran 764 km in total, slightly short of the goal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complete a systematic approach for handling hot spots in high-concurrency scenarios; write twelve technical blog posts.&lt;/strong&gt; ===&amp;gt; 0%. I truly had wanted to organize this material because it inevitably comes up in interviews. To be honest, by the time I wrote this article, I had forgotten that I had even set this goal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learn iOS and Vision Pro development&lt;/strong&gt; (tentative). ===&amp;gt; 0%. At the beginning of last year, Vision Pro was incredibly popular, much like DeepSeek is now. A few months later, however, it had almost vanished from view, so this goal quietly came to nothing as well.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gain a basic understanding of China’s financial system.&lt;/strong&gt; ===&amp;gt; 90%. After transferring to the global team, I became responsible for the entire transaction and fund-fulfillment chain. As a result, I passively absorbed a great deal of knowledge about finance, economics, and payments during the second half of 2024.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Read two novels&lt;/strong&gt; (tentative). ===&amp;gt; 100%. I nearly finished &lt;em&gt;Ming Dynasty in 1566&lt;/em&gt;. While reading, I even noticed some familiar historical figures and wanted to build a website showing their relationships. I had too many ideas and too little action, however, so that never happened. I also read Chen Xingjia’s &lt;em&gt;At the Bend of the Jianghu&lt;/em&gt; and &lt;em&gt;The Alleyway Families&lt;/em&gt;. Both were well written and resonated with me. &lt;em&gt;The Alleyway Families&lt;/em&gt; was especially immersive in its portrayal of interpersonal conflict and family tensions, and it offered some approaches that might prove useful in resolving them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learn the foundations of AI and become able to use large models for basic data modeling.&lt;/strong&gt; ===&amp;gt; 10%. LLMs were extremely popular, but perhaps because I was too busy, I only received knowledge about them passively and never studied them actively.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;For my side project, establish a self-media presence based on knowledge creation and personal reflection, reaching 1,000 followers across all platforms this year.&lt;/strong&gt; ===&amp;gt; 50%. I reached a combined total of 500 followers. The basic media matrix is now in place; the next step is to update it consistently and thoughtfully.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As you can see, I completed less than half of my 2024 goals. On one hand, this reflects my poor execution. On the other, circumstances changed so quickly that plans simply could not keep up. In the new year, I therefore need stronger feedback mechanisms so that I can adjust goals and correct course sooner.&lt;/p&gt;
&lt;p&gt;Besides following the map laid out by my goals for 2024, I also did the following during the year:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enrolled at HKUST and became half a university student again.&lt;/li&gt;
&lt;li&gt;Transferred to Shenzhen, where I became responsible for global transaction and fund-fulfillment services for merchants in Europe, Asia, and the Americas.&lt;/li&gt;
&lt;li&gt;Took the senior software engineer examination and missed passing by only a few points.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;2024复盘总结&quot;&gt;Reviewing 2024&lt;/h3&gt;&lt;p&gt;As with last year, I can barely remember the first half of the year. Fortunately, I keep a diary and can revisit what happened. There are too many entries, though, and they are too fragmented. In the new year, besides keeping my diary, I plan to write a monthly review that records each month’s events, lets me reflect and consolidate what I learned, and gives me timely feedback for making corrections.&lt;/p&gt;
&lt;p&gt;Perhaps because I have always worked in southern China, I have gradually learned about southern Lunar New Year customs. Celebrating the New Year in the south differs from where I grew up. Back home, we had neither a New Year’s Eve dinner nor all kinds of holiday trips and activities. For as long as I can remember, the New Year began with winter vacation: buying toy guns, ammunition, and fireworks, then running wild outdoors every day. On New Year’s Eve we would begin putting up Spring Festival couplets, and the first day of the lunar year marked the formal start of the celebration.&lt;/p&gt;
&lt;p&gt;I mainly did two things in January 2024: applied to graduate programs at NTU and NUS, and celebrated the Spring Festival. The main thread of the holiday was much the same as before. I visited relatives to exchange New Year greetings on the first day, then spent days two through five visiting my mother’s and grandmother’s siblings and their families. In previous years I could still rest for a few days on the sixth, seventh, and eighth days. This year was different because of my younger brother. I spent those three days trying every possible connection to move him into a stronger class, but ultimately failed. And with that, January was over.&lt;/p&gt;
&lt;p&gt;After returning to the company in February, there was essentially one major task: writing my review in preparation for the approaching performance season. I was not particularly worried because I knew I would be promoted that year. My responsibilities also expanded. I did some work involving transactions and contracts and gained a basic understanding of B2B cross-border trade.&lt;/p&gt;
&lt;p&gt;In March I received the promotion as expected and also registered for the software qualification examination. Most significantly, I ran the first half marathon of my life and set a personal best of 1 hour and 46 minutes. I also considered how to expand my side project. I decided to begin by posting my IELTS notes on RedNote, but unsurprisingly they made little impact.&lt;/p&gt;
&lt;p&gt;April was mainly a month of internal friction because I used my free time so inefficiently. On a weekend day, I could devote only about three hours wholeheartedly to studying or writing. I could not get up in the morning and did not want to get up in the afternoon; after exercising for a while and looking at my phone, it would already be time to go back to sleep. Alibaba generally pays its year-end bonus in April. When I checked mine, the increase was modest. It seems the Internet industry can indeed no longer be associated with rapid growth.&lt;/p&gt;
&lt;p&gt;I began preparing for the software qualification examination in May, but ultimately fell ten points short and missed the senior software architect certificate. The second major matter was the system migration at work, which had nearly reached its closing stage. I felt that I had put real care into it. The final event determined the turning point of my entire 2024: I received an offer from HKUST. The offer arrived so late that I had originally decided not to go. After careful thought, I realized that I still dreamed of earning a master’s degree, so I decided to resign and study. The decision-making process lasted nearly a month. I asked many people and received all kinds of advice. The whole experience was genuinely painful, and I could accomplish almost nothing useful each day.&lt;/p&gt;
&lt;p&gt;June was largely about telling friends around me that I would soon leave Alibaba to study in Hong Kong. I made all kinds of plans for going there: build a side business, learn English, find a job in Hong Kong, and so on.&lt;/p&gt;
&lt;p&gt;July brought another turn of events. While preparing to enroll, I suddenly realized that I might be able to transfer to Shenzhen. With help from my manager and HR, I successfully rebased to the global sales team there, producing what appeared to be a win-win outcome: I could keep working and attend school at the same time. In any case, avoiding resignation was a good thing, and I began familiarizing myself with the Shenzhen team’s business. I also revived the campus-recruitment edition of my interview study guide and reorganized backend interview questions suitable for new graduates, which felt quite rewarding. Finally, I did something that shaped my entire Hong Kong–Shenzhen commuting plan: I completed the edX courses, allowing me to take three fewer courses at HKUST. That decision later proved to be a lifesaver.&lt;/p&gt;
&lt;p&gt;After continuing July’s work and completing my edX certification, I formally rebased to Shenzhen on August 16 and began what might become a year-long stay there. The second half of August was mainly about learning how the Shenzhen office operated, renting an apartment, and visiting Hong Kong to become familiar with the university. Everything felt new.&lt;/p&gt;
&lt;p&gt;In September I formally began commuting between Hong Kong and Shenzhen. Until the semester ended in November, I had to travel from Shenzhen to Hong Kong three times each week. Between assignments, classes, and a full-time job, I was unbelievably busy and barely wrote anything in my diary. Many interesting firsts happened during this period: my first visit to Hong Kong, my first credit card and Hong Kong bank card, my first Hong Kong identity card, and my first time looking up at residential buildings nearly one hundred stories tall. At school I learned about blockchain, but the instructor did not go very deeply and the material was not oriented toward practical applications. In addition, I found the Indian instructor’s spoken English extremely difficult to understand, so I did not pursue the subject further. Compared with my undergraduate education, I found that universities outside mainland China truly have strengths in education. First, they offer rich educational resources; even 4K displays in the library are free to use. Second, the teaching content is strong: instructors directly use perspectives from research papers as classroom material. Third, administration differs enormously from that in mainland China. Slides, recordings, and assignments are all exchanged through Canvas, and university matters are communicated by email, instead of documents flying around countless QQ groups as they do on the mainland. Because I was frequently in Hong Kong, I also passively absorbed a great deal of financial knowledge, including foreign exchange, investment, and payments. I kept around RMB 20,000 on my Hong Kong card as well. I still have one regret: classes were taught entirely in English and there were many international students, but I was too shy to express myself. My spoken English and listening ability barely improved, and even everyday communication still felt difficult.&lt;/p&gt;
&lt;p&gt;December brought three examinations in succession. I stayed up through several nights, but in the end I earned three A grades, so graduation should not be a problem. In late December I traveled to Beijing for a team-building trip. Traveling without the pressure of examinations was wonderful.&lt;/p&gt;
&lt;h3 id=&quot;2025FLAG&quot;&gt;2025 Goals&lt;/h3&gt;&lt;p&gt;Studying in Hong Kong meant that the course of my entire 2024 did not follow the plan I had expected, but the broad outline of 2025 is still set. I hope I can complete this year’s tasks properly.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;At work, organize the complete business processes and knowledge for cross-border transactions and fund fulfillment, and write one or two documents.&lt;/li&gt;
&lt;li&gt;In development, complete a systematic approach for handling hot spots in high-concurrency scenarios, and write two to four documents.&lt;/li&gt;
&lt;li&gt;Technically, master the foundations from ML through DL, NLP, and LLMs; use Spring AI or LangChain to build a mature product; and write one or two documents.&lt;/li&gt;
&lt;li&gt;In education, earn my Hong Kong master’s degree.&lt;/li&gt;
&lt;li&gt;In my career, strive to advance another level, measured specifically through performance results. With no other matters getting in the way, I need to go all in at work.&lt;/li&gt;
&lt;li&gt;In personal finance, achieve an average annual return of 3%. More specifically, accomplish this by converting part of my funds into US dollars and placing them in fixed-term deposits.&lt;/li&gt;
&lt;li&gt;In mindset, focus on one or two important things and stop spreading my attention. Put concrete plans into action immediately.&lt;/li&gt;
&lt;li&gt;In daily routine, go to sleep at 12:30 a.m. and get up at 8:30 a.m.&lt;/li&gt;
&lt;li&gt;In exercise, run 700 km this year.&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item>
<item><title>Three Years at Alibaba: Personal Reflections Beyond Technology</title><link>https://wxxlamp.cn/en/2025/02/16/ali-3-years-thought/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2025/02/16/ali-3-years-thought/</guid><pubDate>Sun, 16 Feb 2025 12:19:00 GMT</pubDate><description>A non-technical retrospective after three years at Alibaba, sharing genuine reflections from a campus hire on balancing business and technology, project management, workplace mindset, and embracing change.</description><content:encoded>&lt;p&gt;On July 9, 2021, I joined Alibaba as a campus hire and began a journey there that has been both fascinating and meaningful.&lt;/p&gt;
&lt;p&gt;Over these three years, I moved from the Corporate Finance Technology Department to ICBU Technology, with a stint seconded to 1688 Technology in between. Apart from the now-defunct LST business, I seem to have experienced nearly every line of business in the Binjiang campus.&lt;/p&gt;
&lt;p&gt;I went through five managers and changed teams countless times. There were plenty of good and bad moments, and all sorts of frustrating, maddening situations. Fortunately, the experts around me helped me patiently and taught me tirelessly. They let me feel the warmth of Alibaba people, and I gained friendship, growth, and moving memories here.&lt;/p&gt;
&lt;p&gt;There is a saying at Alibaba: “three years to become an adult.” At this point, I would like to offer a small summary. Criticism is welcome~&lt;/p&gt;
&lt;h2 id=&quot;搞业务与做技术&quot;&gt;Working on Business and Doing Technology&lt;/h2&gt;&lt;p&gt;“You build rockets in interviews, then tighten screws after joining.”&lt;/p&gt;
&lt;p&gt;In today’s increasingly competitive environment, interviews have become harder and harder. HR screens résumés first: no non-985 candidates, no candidates without projects. During interviews, most Alibaba interviewers begin with a few standard theory questions, assess your ability through the projects on your résumé, and finish with some high-concurrency questions. After taking that combination punch, candidates really believe their future work will be closely tied to high concurrency and high availability. They imagine taking on tens of millions of concurrent requests and billions of users, thrilled at the thought that transaction after transaction would be completed through code in their hands.&lt;/p&gt;
&lt;p&gt;But after joining, they discover that all the high-performance and high-availability tricks they wielded so easily in interviews have no place to be used. Instead, they get pushed around by PDs and grind through CURD. Every day, they guess at ancient business details buried in mountains of legacy code written years ago. One mistake can earn them a P-level incident, followed by a group chat and a full postmortem. A major blunder can mean an N+1 package and a direct trip back to society.&lt;/p&gt;
&lt;p&gt;Many people join business teams and then find that the code they work with daily—perhaps under pressure from rapid business iteration, perhaps constrained by flaws in a predecessor’s design—has almost no “technical” design to speak of. Adding machines or upgrading configurations at the first sign of trouble is the most common, and perhaps the only, way to deal with so-called high concurrency.&lt;/p&gt;
&lt;p&gt;As for high availability, the middleware platforms are already mature enough that the extreme problems mentioned in interviews are unlikely to occur. Even if they do, they fall outside the scope of business scenarios: the data volume is too small, and offline reconciliation and correction is often the usual solution.&lt;/p&gt;
&lt;p&gt;So after joining a business technology team, I believe most campus hires, after developing business requirements for a while, either start questioning the company and sigh, “Is this all Alibaba is?”, then numb themselves and let requirements push them forward; or suffer a deeper collapse of conviction, begin doubting their original dreams, and eventually either get pushed out or leave for another field.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I belonged to the first group when I had just joined. Watching classmates around me blast through CURD tasks, I could not help feeling lost and questioning the meaning of the three years I had spent studying technology at school. I often wondered: could doing CURD every day really solve problems?&lt;/p&gt;
&lt;p&gt;Pure CRUD certainly cannot solve problems. As I went deeper into the work, I found that things were far less simple than I had imagined. Although it is still create, read, update, and delete, the prerequisite for writing it is a substantial understanding of the business. Only by abstracting and modeling real business through technology can we write highly extensible code. As we take on more and larger requirements, the business knowledge we gain gradually becomes our own moat. Along the way, we write code that is more robust, readable, and extensible. The business knowledge we master, along with the performance and stability targets the business currently needs, all become valuable professional assets.&lt;/p&gt;
&lt;p&gt;The biggest difference between business technology teams and pure technology teams is that they solve different problems. Business teams solve real-world business problems, while pure technology teams solve technical problems. To some extent, computer science students with formal training are more easily influenced by geek culture: they believe that after graduation they will certainly do a great deal of technology-related work. Combined with the hard-core questions asked in interviews, this raises expectations and creates a large sense of disconnect once people start working.&lt;/p&gt;
&lt;p&gt;In a business team, a major taboo for technologists is showing off technical muscle for its own sake. Everyone wants to try new technologies and harder challenges. But if low concurrency can solve the business problem, why invite pain by tackling all the problems of high concurrency? Especially in today’s environment of operating responsibility, technology’s job is to serve the business and solve business problems at the lowest cost.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Still, knowledge is everywhere if you pay attention. Although many hard-core technologies are not useful in day-to-day business scenarios, there are plenty of opportunities to learn after joining Alibaba if you truly enjoy studying technology: read middleware source code, apply middleware design patterns, solve the strange problems you encounter every day (Maven dependency conflicts, class loading), respond to and handle production incidents, and so on. As long as you make time, even if you are tightening screws on a business team, the chance to build a rocket for the business may come if you prepare a little more.&lt;/p&gt;
&lt;h2 id=&quot;草船借箭的勇气&quot;&gt;The Courage to Borrow Arrows with a Straw Boat&lt;/h2&gt;&lt;p&gt;While writing this article, I searched with GPT and found that introverts may account for 60% or more of programmers. This personality trait can certainly help people focus more during development, but it often also makes developers less proactive in communicating with others.&lt;/p&gt;
&lt;p&gt;When many people first join, whether they are learning the business or developing a requirement, they feel embarrassed to ask senior colleagues when they run into problems. Some even mumble that a project is nearly done when a senior asks for progress, only for joint debugging to reveal that their code differs considerably from what the business expected.&lt;/p&gt;
&lt;p&gt;Therefore, for campus hires, asking senior colleagues, managers, and business partners questions at appropriate times is essential to maintaining the pace of growth. Yet people often lack the courage to ask. Based on my own experience and examples around me, the usual reasons are roughly these:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They have never interacted with the person they want to consult and do not know how to start;&lt;/li&gt;
&lt;li&gt;They worry the question is too simple and fear being laughed at or looked down on;&lt;/li&gt;
&lt;li&gt;Everyone is busy, and they worry about disrupting the other person’s work.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Another example left a deep impression on me. A few years ago, a newly joined campus hire in the team next to ours was still working at his desk at 11 p.m. I was also dealing with a few things, and since he had not left, I went over and chatted with him. I learned that he was dealing with a very classic Spring startup issue, so I helped him solve it. Later I found out that he had already been looking at it for almost three hours. If he had found a chance to ask other classmates for help, he could have been in bed launching Genshin Impact at 11 that night.&lt;/p&gt;
&lt;p&gt;It is not only about asking other people. Alibaba has so many technical assets. When you hit a problem, searching Yuque or ATA often gives you a 50% chance of solving it. Learning to use tools is also part of growth.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Unlike ancient times, when a household could nearly complete the entire chain from production to consumption through farming and weaving, modern society is founded on specialization and cooperation. So dealing with other people is unavoidable. Very often, our own work can only be completed by actively or passively relying on others’ help.&lt;/p&gt;
&lt;p&gt;When new colleagues first enter the workplace, they should draw more on the strength of senior colleagues and peers. During the beginner-protection period, people are more tolerant of the things you do not know. So while you are at your most inexperienced, gain as much knowledge as you can.&lt;/p&gt;
&lt;p&gt;When a project gets stuck, draw more on your manager’s strength. Is the business side under a tight deadline? Is a partner uncooperative? Whenever higher-level communication is involved, report to your manager and let them help drive execution from a broader perspective. But before going to your manager, do not merely throw the problem at them; offer possible solutions and an expected timeline. That way, when it comes time to fight for resources or negotiate, your manager will not be so passive.&lt;/p&gt;
&lt;p&gt;For business development, business knowledge is one of the foundations developers must master. When you encounter business problems, draw more on the business side’s strength. Ask them about the background, possible approaches, and reasons behind an issue. Communicating more with the business side not only helps you master more business knowledge, but, from a utilitarian perspective, also lets them get to know you. As everyone becomes more familiar, gaps in later collaboration shrink and work becomes smoother.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Borrowing arrows with a straw boat requires not only courage but also wisdom. Prepare the boat before talking, and pay attention to the time and setting. Expressing gratitude afterwards is indispensable too. Whether it is a simple “thanks,” a coffee, or a meal is up to you~~~&lt;/p&gt;
&lt;h2 id=&quot;预期与项目管理&quot;&gt;Expectations and Project Management&lt;/h2&gt;&lt;p&gt;Whether writing code or managing technical projects as a PM, the core is ensuring that things are delivered on time. There will always be things we do not understand along the way, so balancing unknown pitfalls and fixed delivery dates has been one of the most painful parts of being a technical PM since I joined.&lt;/p&gt;
&lt;p&gt;If more than 30% of a project is unfamiliar to you, I recommend leaving yourself more buffer. There are two reasons. With unknown pitfalls, good luck may let you skip right over them; with bad luck, you may fall into one and be unable to get out for a week. No matter the project’s size, as its owner, we cannot let delivery time fluctuate with luck. That would leave us passive, affect the project’s external communication, and also affect frontline operations.&lt;/p&gt;
&lt;p&gt;Leaving buffer is not only about giving yourself more time to encounter pitfalls; it also gives you more room to discuss schedules with the business side. If you begin by agreeing on a very tight deadline that can barely be met with overtime, the project will very likely be delayed. That will hurt your reputation with partners. Uncle Lu Xun once said: “Chinese people always like reconciliation and compromise. For example, if you say this room is too dark and a skylight must be opened, everyone will surely refuse; but if you advocate tearing off the roof, they will come to compromise and agree to open a window.” So if you estimate a schedule with buffer at the outset, you have plenty of room later to decide whether to tear off the roof or open a skylight.&lt;/p&gt;
&lt;p&gt;But sometimes the schedule is not something small-P employees like us can decide. There are piles of projects that must go live before 9/30 or 3/30; every one is labeled P0 or the Nth growth curve and declared mandatory. There is no room for buffer. What should we do then?&lt;/p&gt;
&lt;p&gt;Past experience tells me that throughout a project of continually discovering and resolving risks, any risk that is hard to solve must be synchronized with your manager. Let your manager understand the project’s progress in time and appropriately manage stakeholders’ expectations. That way, you will not give all the experts a SURPISE at every project milestone.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I remember writing a newcomer post about being a technical PM about half a year after joining. Interested readers are welcome to criticize it: &lt;a href=&quot;https://wxxlamp.cn/en/2022/01/16/tech-pm-first/&quot;&gt;Lessons from My First Technical Project Management Role&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;独乐乐不如众乐乐&quot;&gt;Shared Joy Is Better Than Solitary Joy&lt;/h2&gt;&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;During my three years at Alibaba, I met many outstanding senior colleagues. Most of them gave me very important help during my time at Alibaba. When I asked why they would still make time to answer my questions despite being so busy, their answers were strikingly similar:&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;“Because I was once soaked by the rain myself, I know how painful these tedious things can be.”&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;“Once you understand these conceptual things, you take many fewer detours. There is no need for you to waste time on things with no value.”&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;I was deeply moved. To me, this is the best expression of Alibaba’s senior-junior culture. From my internship through campus recruitment, every senior colleague and manager I worked with did their best to help me land on my feet: teaching me technical principles, showing me how to learn and grow, how to manage projects, and how to control risks. I am grateful for every moment spent with them over these three years. Those days are among the most valuable treasures of my life (one of them, hahaha).&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;So if you have time, help new friends a little more. Before they have grown up, offer less PUA and more help within your ability. It makes you happy and everyone else happy, and little by little the team atmosphere improves and Alibaba’s culture is passed on.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;And when we help them grow, is that not a form of growth for ourselves too?&lt;/font&gt;&lt;/p&gt;
&lt;h2 id=&quot;万物是否皆可卷&quot;&gt;Is Everything Worth Competing Over?&lt;/h2&gt;&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;I joined Alibaba as an intern in 2020 and was one of the lucky undergraduate students who converted to a full-time role through campus recruitment. Internet companies expanded campus hiring heavily in 2020, 2021, and 2022. From today’s perspective, under current hiring standards, there is no way an ordinary undergraduate like me would have been hired. Campus hires in my department now start with master’s degrees and have 985 universities as a baseline. In the competition over academic credentials, I had already lost at the starting line as a developer.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;If I cannot compete on academic credentials, what else can I compete on? Overtime hours? Lines of code? Or logged hours in Aone?&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;I think all of these are process metrics. Competing over them may comfort or ease managers’ anxiety, but it is difficult for them to solve any other problem in a meaningful way, and they do nothing to help our own growth or improvement.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;Top internet companies are reservoirs of talent, and excellent people are everywhere around us: technically capable, business-savvy, and often equipped with their own ways of dealing with people and situations. As newcomers to the workplace, we should work hard on our own skills, learn from the experts around us, improve technically, learn the business, and then put what we learn into practice. We should move from imitation to surpassing it and form a loop of learning, understanding, practice, and improvement.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;After gaining foundational technical and business ability, business developers should not focus only on their own small technical domain. Gradually start competing on business understanding: learn the background, development, and plans of the business you build for, and practice considering problems from a broader view. Alibaba is still tolerant of newcomers making mistakes. Campus hires can experiment quickly, keep growing, wait for opportunities, and take on greater responsibility. I once came across a good talk by Zhang Yiming:&lt;/font&gt;&lt;a href=&quot;https://xueqiu.com/2684655177/154256082&quot;&gt;张一鸣演讲整理 (in Chinese)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;Slow down a little. Give yourself some time to find one or two things you truly want to do in work and in life, then keep doing them. Do not compete over meaningless metrics. Campus hires: begin by competing with yourself.&lt;/font&gt;&lt;/p&gt;
&lt;h2 id=&quot;踩坑了，然后呢&quot;&gt;You Hit a Pitfall—Then What?&lt;/h2&gt;&lt;p&gt;As newcomers, when we first join there are always many things we do not understand. Some people may not even have used the development language commonly used in their department before. This is entirely normal. In the first year after joining, everyone falls into all kinds of pitfalls. I remember hitting many myself:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Batch calls from a DTS task triggered downstream alerts&lt;/li&gt;
&lt;li&gt;An Aochuang deployment went live but did not take effect&lt;/li&gt;
&lt;li&gt;Missing configuration for certain tenants caused an NPE, and so on&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Some even caused P5 incidents. During the postmortems, my manager and senior colleagues helped me summarize the lessons, and I gained a great deal. Many people treat these as dark marks on their work record and do not want to face them. But for campus hires, the opposite is true: as long as the issue is not too serious, they are excellent chances to correct mistakes. As the saying goes, the best memory is worse than the palest ink. Like keeping a notebook of wrong answers in high school, record the pitfalls you have encountered. One day, when you encounter a similar problem for a second time, the scene of the first failure will suddenly come to mind. You can open the record, successfully avoid the pitfall, and keep moving forward.&lt;/p&gt;
&lt;p&gt;People have a high tolerance for campus hires’ mistakes, so new colleagues must use this period to experiment and learn. When I was interning, a senior colleague gave me some suggestions during a code review, but I did not take them seriously and did not correct them. He sternly pointed out the problem with how I responded to comments and patiently taught me: “When someone helps you by pointing out mistakes in a CR, you must respond, discuss, or fix them. It shows that you value the reviewer. If you do not reply, it will be hard to get high-quality comments when you ask the same people for CR in the future.” That advice has served me well. After I formally joined, whenever a senior organized a broad CR and I received high-quality feedback, I would immediately submit a patch to fix it. The earlier a fix is made, the lower the risk of release.&lt;/p&gt;
&lt;p&gt;At the same time, after hitting a pitfall, remember not only to avoid it yourself. If you can turn the lesson into public documentation or a knowledge base to keep others from hitting it, or fix the pitfall along the way, that is even more meritorious. (Of course, how to let others know you fixed it is an art //v//)&lt;/p&gt;
&lt;h2 id=&quot;听得懂、讲明白&quot;&gt;Understand Clearly, Explain Clearly&lt;/h2&gt;&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;As programmers, we deal more with machines in day-to-day development, but communication with people is unavoidable in project management. Information entropy inevitably causes loss during transmission. So in communication, we need to use every possible way to make sure information is conveyed as correctly and completely as possible.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;Communication is a skill that requires long-term practice. Two especially important points are patience and attentiveness. When listening, give your partner enough room to express themselves. Under normal circumstances, do not rush to deny or interrupt them—just as you would not want to be interrupted when speaking. What we need to do is listen carefully, note and digest the points the other person wants to express one by one. Before answering, we can also restate what the other person has just said in a few sentences to reduce the GAP between the two people as much as possible.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;Likewise, when expressing your own views, try to organize them point by point. Like writing an essay, use a general-specific-general structure: state the key point at the beginning and end, with the middle explaining it in organized points. Also be sure to provide context and background. Otherwise, it is easy to confuse the recipient and create an awkward situation in which the two sides are talking past each other. This is especially true when consulting someone about a problem: if you ask only about one specific detail, they may find it hard to grasp the meaning immediately; if you explain the background clearly, communication may be much smoother.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;&lt;font style=&quot;color:rgb(51, 51, 51);&quot;&gt;But after all, every technique is only decoration. When your own influence is strong enough, many things resolve themselves.&lt;/font&gt;&lt;/p&gt;
&lt;h2 id=&quot;拥抱变化&quot;&gt;Embrace Change&lt;/h2&gt;&lt;p&gt;Embracing change is one of the New Six Veins, and no colleague is unfamiliar with it. At the company, embracing change is not merely a slogan; it is vividly reflected in action. Many people answer the organization’s call to “embrace change” every fiscal year, or even every half fiscal year, switching businesses, teams, and managers. Under organizational moves, they meet new people and take on new challenges.&lt;/p&gt;
&lt;p&gt;As a campus hire, I initially did not feel the meaning of the word “change” very deeply. The first time I felt it was five months after I joined, when the whole group was notified that we would move to another BU. Since only the organizational relationship changed and the colleagues and manager I worked with did not, I had only an intuitive understanding of this change. The second time was in 2022, when the manager who had led me since my internship graduated from Alibaba and the entire team fell into a vortex. That time made me think. The third time was my own choice: choosing a different environment in which to continue working.&lt;/p&gt;
&lt;p&gt;These changes left me with several thoughts:&lt;/p&gt;
&lt;p&gt;First, many campus hires may have a misconception when they enter work: they think they must do something closely related to their major, study technology intensely, and write code. But after entering the workplace, I think this student mindset needs to shift slightly. Of course we should keep improving technically, but the core should be to focus on needs and organizational needs, and apply our technology or ability to those needs. The essence of an intermediary making money is matching supply and demand; sometimes making reports or thinking about the business is also growth.&lt;/p&gt;
&lt;p&gt;Second, change has two sides. It means leaving your original comfort zone, but it also means facing a new environment and perhaps greater opportunities. After moving from one organization to another, discomfort and unfamiliarity are challenges we must face while first integrating. Some people may think when facing change, “Why change them instead of me?” Personally, I feel we should not assume too much malice from the organization. Perhaps we have not noticed that being moved from our original place may mean that our development there has already become constrained. So it is better to embrace this change with a more positive attitude and start again in a new field.&lt;/p&gt;
&lt;p&gt;Third, a loss may turn out to be a blessing. Some changes may seem to cost us in the short term, but who can say what will happen in the long run? In only three years, I have seen many examples: changes that initially seemed good but later developed away from what people expected; and changes that began in utter disaster but later opened into a bright future. So when facing change, do your work well with a positive mindset, stay grounded while finding creative opportunities, and wait quietly for flowers to bloom.&lt;/p&gt;
&lt;p&gt;We must acknowledge one fact: a business can keep running without any particular person. Most developers do not have technical ability so exceptional that it leaves everyone else in the dust. At first I naively thought, “This expert understands this technology so well—what will happen if their business changes?” After working longer, I gradually found that only when people change can things change. There is no requirement that cannot be solved by adding code, adding person-days, or doing a refactor. Though the so-called refactor may simply implement the previous feature again—you tell me, does it work or not? Hahaha.&lt;/p&gt;
&lt;p&gt;The ability to learn is very important. When facing change, we must learn to integrate into a new environment quickly. Learn to understand the business quickly, take over unfamiliar code logic quickly, and develop your own methodology. Remember: we will never again have the happy time of a campus hire getting two full months just to familiarize ourselves with the code. The ability to deliver business requirements in unfamiliar code without creating risks, while even maintaining architectural consistency, is the foundational ability for a programmer to embrace change in the environment of our company.&lt;/p&gt;
&lt;p&gt;In change, we must know this: no matter how the people and things around us change, the constant is always ourselves. Improving our abilities and cultivating ourselves through events is the enduring foundation amid change.&lt;/p&gt;
&lt;h2 id=&quot;高P和低P&quot;&gt;High P and Low P&lt;/h2&gt;&lt;p&gt;I have met many P9s at work. Their basic abilities are strong, and communicating with them feels like a spring breeze. But you cannot talk deeply with them for too long: before long, you find that many like to make sweeping pronouncements about fields they do not understand well. One HR person believed that graduate students from Tsinghua and Peking University in technical roles were inferior to master’s graduates from UCL and KCL; another sales leader made many basic factual mistakes while harshly reviewing DeepSeek. So as a campus hire, do not automatically put a flattering filter on high-P people. As the saying goes: “When you are out in the world, your identity is bestowed by the company.” It is hard to say how much real ability they have. At the very least, in the field you know best, they may truly know less than you.&lt;/p&gt;
&lt;p&gt;What I also want to say is that many high-P people today rose during the internet boom years, and their knowledge base and learning ability may no longer keep pace with the times. Many campus hires still want to copy the old path: they arrive and want a promotion, imagining P6 in two years, P7 in two years, P8 in three years, and perhaps P9 after a lucky break. Frankly, the odds are small. No matter how hard you strive, the final result may still differ greatly from your expectations, because promotion is not only about ability; luck matters a great deal too. Here are a few external factors: whether you frequently change teams, whether you meet a good manager, whether there is an opening for promotion, whether the business is growing, whether your age is appropriate, and so on.&lt;/p&gt;
&lt;p&gt;Although a soldier who does not want to become a general is not a good soldier, organizational problems arrive once a company grows large. You may even get caught in the crossfire for no reason because of senior management policies. So sometimes, even if you work very hard, you may not get the result you want. That is entirely normal. I have seen many people with very strong business and technical abilities who did not get promoted because of various factors. As the saying goes, a grain of ash from an era becomes a mountain when it falls on a person. Keep an even mindset, let go of titles, and treat every person at the company for who they are.&lt;/p&gt;
&lt;h2 id=&quot;接雨水和修屋顶&quot;&gt;Catch Rainwater and Repair the Roof&lt;/h2&gt;&lt;p&gt;Although not everything is worth competing over, in today’s intensely competitive environment we often cannot avoid getting drawn into internal competition and numbly doing the same things over and over. The passion and courage we had when first entering work can gradually be worn away by an environment of constantly taking on passive requirements.&lt;/p&gt;
&lt;p&gt;Sometimes, we may agonize every day over colleagues’ negative opinions; lose sleep over a small oversight at work; or become gloomy and shut ourselves off because a project is unfinished. “Hell is other people.” After working for a long time, we often fall into the evaluation system around us, becoming slaves to others’ words instead of masters of ourselves. At such times, seeking outward may be one solution.&lt;/p&gt;
&lt;p&gt;The company’s halo and an individual’s ability are different things. Alongside internal company evaluation standards, try to use external evaluation to position yourself more accurately. In your free time, catch more rainwater and prepare for a rainy day; only then can you stay independent when the downpour comes. Looking back on the great layoffs of 2022 still makes my legs tremble with fear.&lt;/p&gt;
&lt;p&gt;Preparing for a rainy day and repairing the roof while the weather is clear are equally necessary. When there are fewer requirements, relax physically but stay mentally alert. Learn more about competitors’ products and movements, and reflect on your strengths and weaknesses at work. Keep track of industry trends in your field, and feed them back into your business and technology. Build a more comprehensive evaluation system for yourself with a positive attitude, position yourself clearly, and improve accordingly. As an aside, large models are so popular now that every department hopes to use them to ease immediate problems, tell a new story, and paint a grand vision. If we ordinary soldiers do not understand these things and only keep doing curd, we may seem to be in a comfort zone but are actually pushing ourselves into an abyss. When an avalanche occurs, no snowflake is innocent.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The sea is wide enough for fish to leap; the sky is high enough for birds to fly.&lt;/p&gt;
&lt;p&gt;Whatever happens, this is only a job. Becoming depressed because of work is simply not worth it. Cherish every friend around you, try to reconcile with yourself, accept what you cannot do, strive for what you can gain, face the results calmly, and be a friend to life.&lt;/p&gt;
&lt;p&gt;When mountains and rivers seem to leave no road, beyond dark willows and bright flowers lies another village.&lt;/p&gt;
&lt;h2 id=&quot;新的开始&quot;&gt;A New Beginning&lt;/h2&gt;&lt;p&gt;I originally wanted to publish this article on my third anniversary, but a series of events happened in between, and the delay stretched to half a year. Still, despite procrastinating for so long, I am glad it is finally published:)&lt;/p&gt;
&lt;p&gt;It so happens that I will attend the company’s three-year anniversary event on February 17. “What is past cannot be undone; what lies ahead can still be pursued.” With this, I commemorate my third anniversary at Alibaba and welcome new challenges here ^_^&lt;/p&gt;
</content:encoded></item>
<item><title>Building and Testing a Distributed Lock</title><link>https://wxxlamp.cn/en/2024/06/15/distributed-lock-implementation/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2024/06/15/distributed-lock-implementation/</guid><pubDate>Sat, 15 Jun 2024 10:39:00 GMT</pubDate><description>Distributed lock design with retries, renewal, consistent acquisition and release, Redis failure handling, and Java examples showing how to coordinate concurrent unit tests.</description><content:encoded>&lt;h1 id=&quot;制作锁&quot;&gt;Building a Lock&lt;/h1&gt;&lt;p&gt;Points to consider when implementing a distributed lock:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Support retries and renewal, and ensure consistency between lock acquisition and release.&lt;/li&gt;
&lt;li&gt;What should happen if Redis cannot be reached? What should happen if a thread fails to acquire the lock?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;加锁业务逻辑&quot;&gt;Business Logic Around Lock Acquisition&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Include business-required retries and handling for Redis connection failures and unsuccessful lock acquisition.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static &amp;lt;T, R&amp;gt; R runWithLock(Function&amp;lt;T, R&amp;gt; function, T req, Class&amp;lt;? extends AssuranceSyncHandler&amp;lt;T,R&amp;gt;&amp;gt; clazz,
                                       int expiredSec, String... factors) {
    AssuranceSyncHandler&amp;lt;T, R&amp;gt; zedSynchronizedHandler = getSyncHandlerFromClazz(clazz);
    ZedBody zedBody = buildBody(req, expiredSec, factors);
    // 尝试加锁
    boolean lockSuc = tryLock(zedBody, zedSynchronizedHandler);
    // 如果没加锁成功，则按照配置策略进行重试
    if (!lockSuc &amp;amp;&amp;amp; !retryLock(zedBody, zedSynchronizedHandler)) {
        LOGGER.info(&quot;thread can not get lock, retry times is {}&quot;, zedSynchronizedHandler.getRetryTimes());
        // 如果缓存不可用，则直接执行业务逻辑
        if (SmartZedThreadLocal.isSynchronizedLockInvalid()) {
            LOGGER.error(&quot;lock failed because of the system error, so func apply directly&quot;);
            SmartZedThreadLocal.clear(zedBody.getRowKey());
            return function.apply(req);
        }
        // 如果缓存可用，则执行自定义锁策略
        return zedSynchronizedHandler.strategyWhenSync(req);
    }

    R result;
    try {
        result = function.apply(req);
    } catch (Exception ex) {
        LOGGER.error(&quot;AssuranceLockUtils.runWithLock error when apply func&quot;, ex);
        throw ex;
    } finally {
        tryUnLock(zedBody);
    }
    return result;
}

@SuppressWarnings(&quot;unchecked&quot;)
private static &amp;lt;T, R&amp;gt; AssuranceSyncHandler&amp;lt;T, R&amp;gt; getSyncHandlerFromClazz(Class&amp;lt;? extends AssuranceSyncHandler&amp;lt;T,R&amp;gt;&amp;gt; clazz) {
    return Optional.ofNullable(clazz)
            .map(ApplicationContextUtil::getBean)
            // 此处加map是为了把子类转成父类
            .map(e -&amp;gt; (AssuranceSyncHandler&amp;lt;T, R&amp;gt;)e)
            .orElse((AssuranceSyncHandler&amp;lt;T, R&amp;gt;)defaultHandler);
}

private static boolean retryLock(ZedBody zedBody, AssuranceSyncHandler&amp;lt;?,?&amp;gt; handler) {
    int retryTimes = 0;
    boolean lockSuc = false;
    while(!lockSuc &amp;amp;&amp;amp; handler.getRetryTimes() &amp;gt; retryTimes++) {
        lockSuc = tryLock(zedBody, handler);
    }
    return lockSuc;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;内部锁加锁逻辑&quot;&gt;Internal Lock Acquisition&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Control the granularity of the internal lock, and &lt;strong&gt;consider what happens if releasing either of the two locks fails&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;An internal lock may not be necessary. With low contention, each call can acquire the Redis lock anyway, so adding a local lock reduces performance. Consider an internal lock when contention on a single machine is high.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt; private static boolean tryLock(ZedBody zedBody, AssuranceSyncHandler&amp;lt;?,?&amp;gt; zedSynchronizedHandler) {
    boolean ans = false;
    boolean innerTryLock = false;
    try {
        innerTryLock = LocalLockHolder.tryLock(zedBody.getRowKey(), zedSynchronizedHandler.getRetryInterval());
        if (innerTryLock) {
            ans = assuranceSyncService.lock(zedBody);
        }
    } catch (Throwable e) {
        LOGGER.error(&quot;AssuranceLockUtils.runWithLock error&quot;, e);
    } finally {
        // 如果外部锁没加成功，则释放内部锁
        if (!ans &amp;amp;&amp;amp; innerTryLock) {
            LocalLockHolder.unlock(zedBody.getRowKey());
        }
    }
    return ans;
}

private static void tryUnLock(ZedBody zedBody) {
    try {
        LocalLockHolder.unlock(zedBody.getRowKey());
        assuranceSyncService.unLock(zedBody);
    } catch (Throwable e) {
        LOGGER.error(&quot;AssuranceLockUtils.runWithUnLock error&quot;, e);
    }
}

private static class LocalLockHolder {
    private static final Map&amp;lt;String, Lock&amp;gt; LOCK_MAP = new ConcurrentHashMap&amp;lt;&amp;gt;();

    public static boolean tryLock(String key, long timeout) throws InterruptedException {
        Lock lock = LOCK_MAP.computeIfAbsent(key, k -&amp;gt; new ReentrantLock());
        return lock.tryLock(timeout, TimeUnit.MILLISECONDS);
    }

    public static void unlock(String key) {
        Lock lock = LOCK_MAP.get(key);
        if (lock != null) {
            lock.unlock();
        }
        LOCK_MAP.remove(key);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;核心加锁逻辑&quot;&gt;Core Locking Logic&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Include support for reentrant locking.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public Boolean lock(ZedBody zedBody) {
    // 1. 竞争锁
    boolean suc = syncService.set(zedBody.getRowKey(), getValue(), zedBody.getTimeout(), () -&amp;gt; {
        // 如果redis没有连接上，则不加锁
        SmartZedThreadLocal.markSynchronizedLockInvalid(true);
        return null;
    });
    if (suc || isOwnLock(zedBody)) {
        // 增加引用次数
        int count = increaseCount(zedBody.getRowKey());
        LOGGER.info(&quot;lock suc key:{},threadId:{}, count {}&quot;, zedBody.getRowKey(), Thread.currentThread().getId(), count);
        return true;
    }
    return false;
}

@Override
public Boolean unLock(ZedBody zedBody) {
    int lockCount = threadLocal.get().getByKeyWithDefault(zedBody.getRowKey()).count.get();
    LOGGER.info(&quot;un lock start key:{},threadId:{},count:{}&quot;, zedBody.getRowKey(),
            Thread.currentThread().getId(), lockCount);
    // 1. 如果引用计数已经小于0，则失效缓存
    if (decreaseCount(zedBody.getRowKey()) &amp;lt;= 0) {
        return syncService.del(zedBody.getRowKey());
    }
    return true;
}

private int increaseCount(String key) {
    LockCounterHolder counterHolder = threadLocal.get();
    return counterHolder.increaseWithKey(key);
}

private int decreaseCount(String key) {
    LockCounterHolder counterHolder = threadLocal.get();
    return counterHolder.decreaseWithKey(key);
}

private boolean isOwnLock(ZedBody zedBody) {
    String s = syncService.get(zedBody.getRowKey());
    return StringUtils.equals(s, getValue());
}

private String getValue() {
    return LOCAL_HOSTNAME + &quot;:&quot; + Thread.currentThread().getId();
}

private static String getHostname() {
    try {
        return InetAddress.getLocalHost().getHostName();
    } catch (UnknownHostException e) {
        LOGGER.warn(&quot;Failed to get hostname&quot;, e);
        return &quot;[unknown]&quot;;
    }
}

private static class LockCounterHolder {

    private final Map&amp;lt;String, LockCounter&amp;gt; counters = new HashMap&amp;lt;&amp;gt;();

    public LockCounter getByKeyWithDefault(String key) {
        return counters.computeIfAbsent(key, e -&amp;gt; new LockCounter());
    }

    public int increaseWithKey(String key) {
        LockCounter counter = getByKeyWithDefault(key);
        return counter.count.incrementAndGet();
    }

    public int decreaseWithKey(String key) {
        LockCounter counter = counters.get(key);
        // 防止非持有锁的线程释放锁
        if (counter == null) {
            throw new ZedSynchronizedException(&quot;key is not found when release. key=&quot; + key);
        }
        int count = counter.count.decrementAndGet();

        if (count &amp;lt;= 0) {
            //如果count 小于0，删除key，避免内存泄漏
            counters.remove(key);
        }
        return count;
    }
}

private static class LockCounter {

    private final AtomicInteger count;

    public LockCounter() {
        this.count = new AtomicInteger(0);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;Redis连接逻辑&quot;&gt;Redis Connection Logic&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Include retries when a Redis connection cannot be established.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public boolean set(String key, String value, int expireTimeSec) {
    int connectTime = 0;
    String v = null;
    SetParams setParams = new SetParams().nx().ex(expireTimeSec);
    while (Objects.isNull(v)) {
        try (Jedis jedis = jedisPool.getResource()) {
            v = jedis.set(PREFIX + key, value, setParams);
            return StringUtils.equals(SUCCESS, v);
        } catch (Exception e) {
            LOGGER.error(&quot;rdb3 setNx error key:{} value:{}&quot;, key, value, e);
            if (connectTime++ &amp;gt;= RECONNECT_TIMES) {
                LOGGER.error(&quot;rdb3 retry {} times setNx error key:{} value:{}&quot;, RECONNECT_TIMES, key, value, e);
                throw e;
            }
        }
    }
    return StringUtils.equals(SUCCESS, v);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;测试锁&quot;&gt;Testing the Lock&lt;/h1&gt;&lt;p&gt;Given the distributed lock above, how should I write unit tests on one machine to verify its correctness?&lt;/p&gt;
&lt;h2 id=&quot;第一版&quot;&gt;First Version&lt;/h2&gt;&lt;p&gt;My first test looked like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Test
public void testLockSync() throws InterruptedException {
    // 1. 第一个线程抢占到锁，睡眠
    new Thread(() -&amp;gt; {
        String res = AssuranceLockUtils.runWithLock((req) -&amp;gt; {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return &quot;test&quot;;
        }, null, 10, &quot;test&quot;);
        Assert.assertEqual(&quot;test&quot;, res);
    }).start();
    // 2. 第二个线程尝试抢占锁，应该失败
    new Thread(() -&amp;gt; {
        try {
            AssuranceLockUtils.runWithLock((req) -&amp;gt; &quot;test&quot;, null, 10, &quot;test&quot;);
        } catch (Exception exception) {
            Assert.assertTrue(exception instanceOf ZedSynchronizedException);
        }
    }).start();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sharp-eyed readers may immediately spot the problem: the main thread cannot observe the test results. Two things are necessary to fix this. First, pass the worker threads’ results to the main thread. Second, ensure the main thread checks them after the worker threads have finished. Common approaches include &lt;code&gt;CountDownLatch&lt;/code&gt;, &lt;code&gt;BlockingQueue&lt;/code&gt;, and shared memory.&lt;/p&gt;
&lt;h2 id=&quot;第二版&quot;&gt;Second Version&lt;/h2&gt;&lt;p&gt;I therefore tried this second version:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Test
public void testLockSync() throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(2);
    AtomicReference&amp;lt;String&amp;gt; firstThreadAns = new AtomicReference&amp;lt;&amp;gt;();
    AtomicReference&amp;lt;Exception&amp;gt; secondThreadAns = new AtomicReference&amp;lt;&amp;gt;();
    // 1. 第一个线程抢占到锁，睡眠
    new Thread(() -&amp;gt; {
        firstThreadAns.set(AssuranceLockUtils.runWithLock((req) -&amp;gt; {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                latch.countDown();
            }
            return &quot;test&quot;;
        }, null, 10, &quot;test&quot;));
    }).start();
    // 2. 第二个线程尝试抢占锁，应该失败
    new Thread(() -&amp;gt; {
        try {
            AssuranceLockUtils.runWithLock((req) -&amp;gt; &quot;test&quot;, null, 10, &quot;test&quot;);
        } catch (Exception exception) {
            secondThreadAns.set(exception);
        } finally {
            latch.countDown();
        }
    }).start();
    // 3. 主线程check结果
    latch.await();
    Assert.assertEquals(&quot;test&quot;, firstThreadAns.get());
    Assert.assertTrue(secondThreadAns.get() instanceof ZedSynchronizedException);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running this test exposes several failure cases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The assertion on line 31 fails because firstThreadAns.get() == null. &lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/dbcd4d8e_8e094577.png&quot;&gt; The worker calls &lt;code&gt;latch.countDown()&lt;/code&gt; prematurely on line 14. The main thread starts checking before the worker has passed its result back.&lt;/li&gt;
&lt;li&gt;The main thread waits forever, preventing the program from finishing. &lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/f9929e9f_9b8f3bf1.png&quot;&gt; The second thread runs first, so the first thread fails to acquire the lock and throws an exception without calling &lt;code&gt;latch.countDown()&lt;/code&gt;. The main thread remains pending. The solution is straightforward: ensure the first test thread reaches the required point before the second. A &lt;code&gt;Semaphore&lt;/code&gt;, &lt;code&gt;CountDownLatch&lt;/code&gt;, or lock can do this, but note that &lt;code&gt;Thread.join()&lt;/code&gt; is unsuitable here.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;第三版&quot;&gt;Third Version&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Test
public void testLockSync() throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(2);
    Semaphore semaphore = new Semaphore(0);
    AtomicReference&amp;lt;String&amp;gt; firstThreadAns = new AtomicReference&amp;lt;&amp;gt;();
    AtomicReference&amp;lt;Exception&amp;gt; secondThreadAns = new AtomicReference&amp;lt;&amp;gt;();
    // 1. 第一个线程抢占到锁，睡眠
    new Thread(() -&amp;gt; {
        firstThreadAns.set(AssuranceLockUtils.runWithLock((req) -&amp;gt; {
            req.release();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return &quot;test&quot;;
        }, semaphore, 10, &quot;test&quot;));
        latch.countDown();
    }).start();
    // 2. 第二个线程尝试抢占锁，应该失败
    new Thread(() -&amp;gt; {
        try {
            semaphore.acquire();
            AssuranceLockUtils.runWithLock((req) -&amp;gt; &quot;test&quot;, null, 10, &quot;test&quot;);
        } catch (Exception exception) {
            secondThreadAns.set(exception);
        } finally {
            latch.countDown();
        }
    }).start();
    // 3. 主线程check结果
    latch.await();
    Assert.assertEquals(&quot;test&quot;, firstThreadAns.get());
    Assert.assertTrue(secondThreadAns.get() instanceof ZedSynchronizedException);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/6cc2037a_ee446bac.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Perfect.&lt;/p&gt;
</content:encoded></item>
<item><title>Handling Hot Reads and Writes</title><link>https://wxxlamp.cn/en/2023/07/09/how-to-process-hot-data/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2023/07/09/how-to-process-hot-data/</guid><pubDate>Sun, 09 Jul 2023 02:36:00 GMT</pubDate><description>Using flash-sale scenarios as an example, this article discusses solutions for hot reads and hot writes: cache warming, large-key sharding, and multi-level caching for hot reads; inventory sharding, asynchronous Redis persistence, and application-level rate limiting for hot writes, while maintaining cache consistency.</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;We will use a flash-sale scenario throughout: skuId, skuContent, and skuStore.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;查询热点&quot;&gt;Hot Reads&lt;/h1&gt;&lt;p&gt;Hot reads mean that skuContent must be displayed for a skuId. Since the database IO connection pool is fixed, the amount of data the database can handle is limited, so hot data needs to be synchronized to Redis.&lt;/p&gt;
&lt;h2 id=&quot;缓存一致性&quot;&gt;Cache Consistency&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Deletion is preferable to updating, because updating is not an atomic operation.&lt;/li&gt;
&lt;li&gt;If the cache is deleted first, then the database is updated and the cache is written back, inconsistency can occur during the write.&lt;/li&gt;
&lt;li&gt;Therefore, use a delayed double-deletion strategy.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;热点缓存处理&quot;&gt;Handling Hot Caches&lt;/h2&gt;&lt;p&gt;The basic approach is to warm hot data in advance so requests do not hit the database directly. If traffic is so large that even Redis cannot handle it, consider these measures:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Shard large keys. For example, hash skuId into different keys using a consistent-hash algorithm and store them in Redis.&lt;/li&gt;
&lt;li&gt;Use multi-level caches: keep one copy in the browser, CDN, local cache, and Redis.&lt;/li&gt;
&lt;li&gt;Make requests in stages. For example, request the relevant hot data as soon as the user starts loading, so there is no need to request it again when the page is actually entered.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&quot;写入热点&quot;&gt;Hot Writes&lt;/h1&gt;&lt;p&gt;When a skuId becomes hot, database updates become a major problem too. Updating the database directly may produce a very high RT, because under the RC isolation level the row for that skuId is locked. The following approaches can be considered:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Distribute SKUs across different databases and decrement inventory there.&lt;/li&gt;
&lt;li&gt;Decrement inventory in Redis, then send a message through Redis to the database for the decrement.&lt;/li&gt;
&lt;li&gt;Write transaction details directly, then update inventory asynchronously from those details (the update must query the sum of the details).&lt;/li&gt;
&lt;li&gt;Apply rate limiting at the application layer by skuId to prevent requests from reaching the database.&lt;/li&gt;
&lt;li&gt;Create a hot-data database and move hot products into it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note: use optimistic locking to ensure consistency when deducting inventory.&lt;/p&gt;
</content:encoded></item>
<item><title>Handling Message Idempotency in Queue Consumers</title><link>https://wxxlamp.cn/en/2023/05/07/how-to-deal-msg-reconsume/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2023/05/07/how-to-deal-msg-reconsume/</guid><pubDate>Sun, 07 May 2023 02:36:00 GMT</pubDate><description>How to solve idempotency problems caused by repeated message-queue consumption, covering distributed locks, unique indexes, special SQL, and exclusive locks, with an analysis of MySQL locking.</description><content:encoded>&lt;p&gt;Recently, while integrating with another business, I found a problem:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When consuming messages from an upstream business, an unusual upstream situation caused multiple messages to be sent at the same time (status-change messages, with the status changing several times in one moment). Since the consumer had no idempotency protection, it consumed the messages repeatedly. In this case, that resulted in two rows being inserted.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;How do we solve it? It is actually simple: lock the conflicting resource. There are several approaches.&lt;/p&gt;
&lt;h2 id=&quot;方案一：分布式锁&quot;&gt;Approach 1: Distributed lock&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;Add a distributed lock to the current consumption logic by user.&lt;/li&gt;
&lt;li&gt;If it has already been consumed, do not consume it again.&lt;/li&gt;
&lt;li&gt;If it has not been consumed, consume it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The pseudocode is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public void consume(Message msg) {
	String id = msg.getId();
	RedisLock.acquire(id, msg -&amp;gt; {
        Record record = db.query(msg);
        if(record != null) {
            return;
        }
        process(msg);
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;方案二：数据库唯一索引&quot;&gt;Approach 2: Database unique index&lt;/h2&gt;&lt;p&gt;Most current Web services are deployed as clusters, so a lock must be distributed. Although databases may use sharding, for the same shard key a record from one consumption will always land in one physical database and one physical table.&lt;br&gt;We can therefore use a unique index on the single physical database and let the database provide the lock. If insertion fails, simply catch an exception such as &lt;code&gt;Duplicate entry&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;方案三：特殊SQL语句&quot;&gt;Approach 3: Special SQL statements&lt;/h2&gt;&lt;p&gt;These essentially also depend on a unique index.&lt;/p&gt;
&lt;h3 id=&quot;insert-ignore&quot;&gt;insert ignore&lt;/h3&gt;&lt;p&gt;&lt;code&gt;insert ignore&lt;/code&gt; ignores data already present in the database (determined by the primary key or unique index). If no data exists, it inserts a new row; if data exists, it skips the row.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt; insert ignore into sc (name,class,score) values (&apos;张三&apos;,&apos;三年二班&apos;,90)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running the statement produces no error, but the primary key still auto-increments.&lt;/p&gt;
&lt;h3 id=&quot;replace-into&quot;&gt;replace into&lt;/h3&gt;&lt;p&gt;&lt;code&gt;replace into&lt;/code&gt; first attempts to insert data into the table. If the row already exists (determined by the primary key or unique index), it &lt;strong&gt;first deletes the row and then inserts a new one&lt;/strong&gt;; otherwise it inserts a new row directly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;replace into sc (name,class,score) values (&apos;张三&apos;,&apos;三年二班&apos;,90);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;insert-on-duplicate-key-update&quot;&gt;insert on duplicate key update&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;on duplicate key update&lt;/code&gt; is specified at the end of an &lt;code&gt;insert into&lt;/code&gt; statement and inserting the row would create a duplicate value in a UNIQUE index or PRIMARY KEY, the duplicate row is updated. If there is no duplicate, a new row is inserted as with an ordinary &lt;code&gt;insert into&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If a new row is inserted, the affected-row count is 1; if an existing row is updated, it is 2; if the value is unchanged before and after the update, it is 0.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;insert into sc (name,class,score) values (&apos;张三&apos;,&apos;三年二班&apos;,90) on duplicate key update score=100;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;方案四：事务-排他锁&quot;&gt;Approach 4: Transaction + exclusive lock&lt;/h2&gt;&lt;p&gt;InnoDB reads use MVCC, so an ordinary &lt;code&gt;select&lt;/code&gt; does not block. We can use a database transaction to lock during the query. Other queries are then blocked, and we insert if nothing is found. The SQL is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;select * from c where name = &apos;张三&apos; for update;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Approach 4 resembles approach 1, except approach 1 locks at the service level while this approach uses a database lock. The following examples use the RR isolation level.&lt;/p&gt;
&lt;h3 id=&quot;索引和加锁的关系&quot;&gt;The relationship between indexes and locks&lt;/h3&gt;&lt;h4 id=&quot;主键索引&quot;&gt;Primary-key index&lt;/h4&gt;&lt;p&gt;Start a transaction with an indexed &lt;code&gt;where&lt;/code&gt; condition:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;begin;
select * from article where id = 3 for update ;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lock state is:&lt;/p&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/d2b6d8dd_how-to-deal-msg-reconsume-2.png&quot; align=&quot;middle&quot;&gt;

&lt;p&gt;Both the table and row are locked: the table has an intention-exclusive lock, and the row has a standard exclusive lock (only the record, not the gap). Therefore, another transaction attempting an exclusive-lock SQL statement will fail:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;select * from article where id = 3 for update ;
update article set name=&apos;ck&apos; where id = 3;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;PS, the purpose of an intention lock:&lt;br&gt;Without an intention lock, obtaining an exclusive table lock would require traversing every record to check whether any record had an exclusive lock, which would be slow.&lt;br&gt;With an intention lock, an intention-exclusive table lock is added before an exclusive record lock. When obtaining an exclusive table lock, we can simply check whether the table has an intention-exclusive lock. If it does, records in the table already have exclusive locks, so there is no need to traverse them.&lt;br&gt;Therefore, &lt;strong&gt;the purpose of an intention lock is to quickly determine whether any record in a table is locked&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;无索引&quot;&gt;No index&lt;/h4&gt;&lt;p&gt;Start a transaction with the following condition:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;begin;
select * from article where author_id = 1 for update ;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lock state is:&lt;br&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/62261fbf_how-to-deal-msg-reconsume-1.png&quot; align=&quot;middle&quot;&gt;&lt;br&gt;We can see that the table also has an intention-exclusive lock. However, every record receives a fully exclusive lock (locking both the row and the gap), effectively locking the entire table. This is dangerous because other transactions cannot perform locking operations on the table.&lt;/p&gt;
&lt;h3 id=&quot;如果没有命中如何加锁&quot;&gt;Locking when there is no match&lt;/h3&gt;&lt;p&gt;For a case without an index, the locking logic is the same as for a match. Here we look at how locking works when there is an index but no match.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;begin;
select * from article where id = 3 for update ;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The lock state is:&lt;br&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/d7aa5d63_how-to-deal-msg-reconsume-3.png&quot; align=&quot;middle&quot;&gt;&lt;br&gt;The row receives a fully exclusive lock covering the range from 3 to positive infinity. Nothing can be inserted or updated between 3 and positive infinity.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;primary key value(s) of the locked record if LOCK_TYPE=’RECORD’, otherwise NULL. This column contains the value(s) of the primary key column(s) in the locked row, formatted as a valid SQL string (ready to be copied to SQL commands). If there is no primary key then the InnoDB internal unique row ID number is used. If a gap lock is taken for key values or ranges above the largest value in the index, LOCK_DATA reports “supremum pseudo-record”. When the page containing the locked record is not in the buffer pool (in the case that it was paged out to disk while the lock was held), InnoDB does not fetch the page from disk, to avoid unnecessary disk operations. Instead, LOCK_DATA is set to NULL&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;参考&quot;&gt;References&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://xiaolincoding.com/mysql/lock/how_to_lock.html#%E5%94%AF%E4%B8%80%E7%B4%A2%E5%BC%95%E7%AD%89%E5%80%BC%E6%9F%A5%E8%AF%A2&quot;&gt;How MySQL locks (in Chinese)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.51cto.com/u_15905482/5919949&quot;&gt;Detailed MySQL locking process (in Chinese)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.51cto.com/article/744551.html&quot;&gt;What lock does Select for update acquire? (in Chinese)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item>
<item><title>Sperm Donation: The Process and Personal Reflections</title><link>https://wxxlamp.cn/en/2023/05/05/juanjing-review/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2023/05/05/juanjing-review/</guid><pubDate>Fri, 05 May 2023 02:36:00 GMT</pubDate><description>A record of the complete sperm-donation process at Zhejiang’s sperm bank, covering its significance, strict screening standards, procedures, subsidies, declining sperm quality, factors affecting programmers, and scientific suggestions for improvement.</description><content:encoded>&lt;h2 id=&quot;为什么要捐精&quot;&gt;Why Donate Sperm&lt;/h2&gt;&lt;h3 id=&quot;标准回答&quot;&gt;The Standard Answer&lt;/h3&gt;&lt;p&gt;As the environment worsens and people adopt increasingly unhealthy lifestyles, the number of families facing infertility is growing rapidly. &lt;strong&gt;Today, one in every eight couples of childbearing age in China has a fertility problem.&lt;/strong&gt; Some men cannot produce qualified sperm, while others have genetic diseases that prevent them from having children. They need sperm from healthy men to help solve their fertility problems, creating a major demand for sperm donation. These families often suffer greatly because they cannot have children. In that sense, sperm donation is a noble humanitarian act, as honorable as donating blood or bone marrow!&lt;/p&gt;
&lt;h3 id=&quot;真实答案&quot;&gt;The Real Answer&lt;/h3&gt;&lt;p&gt;Pure curiosity; the possibility of storing sperm for future use; and the subsidy.&lt;/p&gt;
&lt;h2 id=&quot;捐精要求&quot;&gt;Requirements&lt;/h2&gt;&lt;p&gt;The requirements are extremely strict. The basic requirements are:&lt;/p&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/5eea1622_juanjing-review.png&quot; align=&quot;middle&quot;&gt;

&lt;p&gt;But if those were the only requirements, sperm banks in China would not be short of sperm. There are further hidden requirements during screening:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Volume must exceed 2 ml.&lt;/li&gt;
&lt;li&gt;Concentration must exceed 60 million/ml (below 15 million/ml is oligospermia).&lt;/li&gt;
&lt;li&gt;Motility (straight-line movement, generally indicators a+b) must exceed 60% (below 30% is asthenospermia).&lt;/li&gt;
&lt;li&gt;More than 30% must have normal morphology.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;捐精流程&quot;&gt;Procedure&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;Zhejiang Province is used as an example below. Information is available through the WeChat public account Zhejiang Human Sperm Bank.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Make an appointment on the &lt;a href=&quot;http://zjjzk.cn:8081/wjwpro/indexApartment.htm?tjrid=71d0&quot;&gt;website (in Chinese)&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/b0d86f36_juanjing-review-1.png&quot; align=&quot;middle&quot;&gt;

&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Go directly to the second floor of Zhejiang Maternal and Child Health Hospital to donate:&lt;/li&gt;
&lt;/ol&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/80b27adf_juanjing-review-2.png&quot; align=&quot;middle&quot;&gt;

&lt;h2 id=&quot;捐精补助&quot;&gt;Subsidies&lt;/h2&gt;&lt;h3 id=&quot;未成功&quot;&gt;If unsuccessful&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Blood type measurement&lt;/li&gt;
&lt;li&gt;A 50-yuan subsidy&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;成功&quot;&gt;If successful&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Free sperm storage for 30 years&lt;/li&gt;
&lt;li&gt;Free blood tests for HIV, syphilis, and other diseases&lt;/li&gt;
&lt;/ol&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/b8fe347c_juanjing-review-3.png&quot; align=&quot;middle&quot;&gt;

&lt;p&gt;PS: If someone referred you, the referrer also receives 100 yuan.&lt;/p&gt;
&lt;h2 id=&quot;感悟与思考&quot;&gt;Reflections&lt;/h2&gt;&lt;p&gt;One third of 2023 had already passed. More than 1,200 people had applied to donate at the Zhejiang sperm bank, but only 25% qualified. As far as I know, qualified programmers are even rarer.&lt;/p&gt;
&lt;p&gt;Men’s sperm quality in China really is declining. A &lt;a href=&quot;http://www.hzswysyy.com/index/article/index/element/497&quot;&gt;recent report (in Chinese)&lt;/a&gt; found a sharp decline: the average number of sperm per milliliter fell from around 130 million in 2005 to 67 million in 2015. The average percentage of progressively motile sperm fell from 56.5% to 51.6%. The report notes that sperm-bank donors are generally healthier than the general population, so these figures may still exceed the public average. Sperm concentration and motility are directly related to the probability of conception; fewer than 20 million sperm per milliliter is oligospermia, and progressive motility below 32% is asthenospermia. If the trend continues, the outlook is worrying.&lt;/p&gt;
&lt;p&gt;Apart from objective factors such as radiation, the main influences I know of are staying up late, stress, prolonged sitting, drinking, and smoking. What a miserable list—these factors seem tailor-made for programmers.&lt;/p&gt;
&lt;p&gt;To maintain good sperm quality:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Do moderate aerobic exercise.&lt;/strong&gt; Scientists say at least three 30-minute aerobic sessions per week can increase sperm count, although the improvement is temporary and returns to its original state within a month after stopping. Excessively intense exercise can also damage sperm quality. Source: Reproduction.&lt;br&gt;&lt;strong&gt;Sleep seven to eight hours every night.&lt;/strong&gt; Compared with eight hours, men sleeping under six or over nine hours have a 42% lower chance of conception in any given month. Source: Boston University School of Public Health.&lt;br&gt;&lt;strong&gt;Eat healthily.&lt;/strong&gt; Eat foods rich in vitamins D and C, zinc, selenium, and folate, such as nuts, tomatoes, oranges, eggs, fish, poultry, milk, leafy vegetables, beans, blueberries, pomegranates, and dark chocolate.&lt;br&gt;&lt;strong&gt;Drink fewer soft and energy drinks.&lt;/strong&gt; Men who drink at least one soft drink daily are 33% less likely to successfully conceive with their partners. Source: Epidemiology.&lt;br&gt;&lt;strong&gt;Do not keep your phone in your trouser pocket.&lt;/strong&gt; Compared with a control group, men who often do so had 8% lower sperm survival and progressive motility. Source: Environment International.&lt;br&gt;&lt;strong&gt;Wear boxer briefs.&lt;/strong&gt; Harvard research found that men who regularly wear boxer briefs have higher sperm concentration and total sperm counts than men who do not. Avoiding overly tight trousers follows the same principle.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finally, according to the study &lt;a href=&quot;https://www.ncbi.nlm.nih.gov/pmc/articles/PMC9385505/&quot;&gt;Effects of Seasonal Factors on Semen Quality among Sperm Donors in Beijing&lt;/a&gt;, spring is the best season for sperm donation.&lt;/p&gt;
</content:encoded></item>
<item><title>Troubleshooting a MetaQ Consumer Backlog</title><link>https://wxxlamp.cn/en/2023/04/25/metaq-consumer-heap-up/</link><guid isPermaLink="true">https://wxxlamp.cn/en/2023/04/25/metaq-consumer-heap-up/</guid><pubDate>Tue, 25 Apr 2023 13:36:00 GMT</pubDate><description>A consumer-backlog investigation triggered by migrating from Spring Cloud Stream to native MetaQ configuration. Source analysis shows that one ConsumerGroup can map to only one MQConsumerInner; using separate ConsumerGroup IDs resolves it.</description><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Note: MetaQ is an internal Alibaba product whose open-source version is called RocketMQ.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;问题现象&quot;&gt;Symptoms&lt;/h2&gt;&lt;p&gt;For special reasons, one application in the business I owned needed its messaging configuration changed from Spring Cloud Stream to native MetaQ. The configurations before and after the change were:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-properties&quot;&gt;spring.cloud.stream.bindings.consumerA.group=CID_CONSUMER_A
spring.cloud.stream.bindings.consumerA.contentType=text/plain
spring.cloud.stream.bindings.consumerA.destination=CONSUMER_A_TOPIC
spring.cloud.stream.metaq.bindings.consumerA.consumer.tags=CONSUMER_A_TOPIC_TAG

spring.cloud.stream.bindings.consumerB.group=CID_CONSUMER_A
spring.cloud.stream.bindings.consumerB.contentType=text/plain
spring.cloud.stream.bindings.consumerB.destination=CONSUMER_B_TOPIC
spring.cloud.stream.metaq.bindings.consumerB.consumer.tags=CONSUMER_B_TOPIC_TAG

spring.cloud.stream.bindings.consumerC.group=CID_CONSUMER_A
spring.cloud.stream.bindings.consumerC.contentType=text/plain
spring.cloud.stream.bindings.consumerC.destination=CONSUMER_C_TOPIC
spring.cloud.stream.metaq.bindings.consumerC.consumer.tags=CONSUMER_C_TOPIC_TAG
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-properties&quot;&gt;spring.metaq.consumers[0].consumer-group=CID_CONSUMER_A
spring.metaq.consumers[0].topic=CONSUMER_A_TOPIC
spring.metaq.consumers[0].sub-expression=CONSUMER_A_TOPIC_TAG
spring.metaq.consumers[0].message-listener-ref=consumerAListener

spring.cloud.stream.bindings.consumerB.group=CID_CONSUMER_A
spring.cloud.stream.bindings.consumerB.contentType=text/plain
spring.cloud.stream.bindings.consumerB.destination=CONSUMER_B_TOPIC
spring.cloud.stream.metaq.bindings.consumerB.consumer.tags=CONSUMER_B_TOPIC_TAG

spring.cloud.stream.bindings.consumerC.group=CID_CONSUMER_A
spring.cloud.stream.bindings.consumerC.contentType=text/plain
spring.cloud.stream.bindings.consumerC.destination=CONSUMER_C_TOPIC
spring.cloud.stream.metaq.bindings.consumerC.consumer.tags=CONSUMER_C_TOPIC_TAG
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When half the machines had been deployed and we began observing the canary rollout, messages started accumulating:&lt;/p&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/a563e111_metaq-consumer-heap-up-1.png&quot; align=&quot;middle&quot;&gt;

&lt;h2 id=&quot;问题原因&quot;&gt;Cause&lt;/h2&gt;&lt;h3 id=&quot;消息订阅关系不一致&quot;&gt;Inconsistent Message Subscriptions&lt;/h3&gt;&lt;p&gt;Based on past experience, I suspected that machines in the consumer group had inconsistent subscription relationships. Some subscribed to A and others to B, so MetaQ could not determine whether to consume the messages and left them accumulating on the broker. The MetaQ console confirmed that inconsistent subscriptions had caused the backlog.&lt;/p&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/19657061_metaq-consumer-heap-up-2.png&quot; align=&quot;middle&quot;&gt;

&lt;p&gt;The subscription on the deployed machine was:&lt;/p&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/e1890480_metaq-consumer-heap-up-3.png&quot; align=&quot;middle&quot;&gt;

&lt;p&gt;The undeployed machine had the following subscriptions, clearly more than the deployed machine:&lt;/p&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/50585be8_metaq-consumer-heap-up-4.png&quot; align=&quot;middle&quot;&gt;

&lt;h3 id=&quot;Spring-Cloud-Stream-和-MetaQ-Native&quot;&gt;Spring Cloud Stream and Native MetaQ&lt;/h3&gt;&lt;p&gt;This raised another question: why did the subscriptions for the same &lt;code&gt;ConsumerId&lt;/code&gt; change after replacing Spring Cloud Stream with native MetaQ?&lt;/p&gt;
&lt;p&gt;More simply, when MetaQ and Spring Cloud Stream used the same &lt;code&gt;ConsumerId&lt;/code&gt;, why did MetaQ’s subscriptions overwrite Spring Cloud Stream’s subscriptions?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: one &lt;code&gt;consumerId&lt;/code&gt; can subscribe to multiple topics.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The only way to answer this was to inspect the startup source code of Spring Cloud Stream and MetaQ.&lt;/p&gt;
&lt;h4 id=&quot;MetaQ&quot;&gt;MetaQ&lt;/h4&gt;&lt;p&gt;The MetaQ client’s class diagram is shown below:&lt;/p&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/ead0ccb5_metaq-consumer-heap-up-5.png&quot; align=&quot;middle&quot;&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MQConsumerInner&lt;/code&gt; records how the current &lt;code&gt;consumerGroup&lt;/code&gt; interacts with the server and maps topics to tags. Its default implementation is &lt;code&gt;DefaultMQPushConsumerImpl&lt;/code&gt;, with a one-to-one relationship to &lt;code&gt;consumerGroup&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MQClientInstance&lt;/code&gt; centrally manages reusable objects such as network connections. A map maintains the relationship between &lt;code&gt;ConsumerGroupId&lt;/code&gt; and &lt;code&gt;MQConsumerInner&lt;/code&gt;. Put simply, one &lt;code&gt;ConsumerGroup&lt;/code&gt; can correspond to only one &lt;code&gt;MQConsumerInner&lt;/code&gt;, as shown below:&lt;/li&gt;
&lt;/ul&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/bb273e43_metaq-consumer-heap-up-6.png&quot; align=&quot;middle&quot;&gt;

&lt;h4 id=&quot;Spring-Cloud-Stream&quot;&gt;Spring Cloud Stream&lt;/h4&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/3c942beb_metaq-consumer-heap-up-7.png&quot; align=&quot;middle&quot;&gt;

&lt;p&gt;Spring Cloud Stream is a glue layer between Spring and middleware. When it starts, it also registers a &lt;code&gt;ConsumerGroup&lt;/code&gt;, as shown below:&lt;/p&gt;
&lt;img src=&quot;https://cdn.jsdelivr.net/gh/wxxlamp/blog-img-repo@main/images/c4d6e249_metaq-consumer-heap-up-8.png&quot; align=&quot;middle&quot;&gt;

&lt;h3 id=&quot;问题根因&quot;&gt;Root Cause&lt;/h3&gt;&lt;p&gt;At this point, the cause is clear. During startup, Spring Cloud Stream creates its own &lt;code&gt;MetaPushConsumer&lt;/code&gt;, which is effectively a new &lt;code&gt;MQConsumerInner&lt;/code&gt;. One &lt;code&gt;ConsumerGroup&lt;/code&gt; therefore has two &lt;code&gt;MQConsumerInner&lt;/code&gt; instances, violating MetaQ’s required one-to-one mapping. By default, MetaQ replaces the old mapping with the new one. The native MetaQ consumer consequently replaces Spring Cloud Stream’s consumer.&lt;/p&gt;
&lt;p&gt;That is why, for &lt;code&gt;ConsumerA&lt;/code&gt; on deployed machines, only the native MetaQ subscription remained.&lt;/p&gt;
&lt;h2 id=&quot;解决思路&quot;&gt;Solution&lt;/h2&gt;&lt;p&gt;Change the consumer ID:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-properties&quot;&gt;spring.metaq.consumers[0].consumer-group=CID_CONSUMER_A
spring.metaq.consumers[0].topic=CONSUMER_A_TOPIC
spring.metaq.consumers[0].sub-expression=CONSUMER_A_TOPIC_TAG
spring.metaq.consumers[0].message-listener-ref=consumerAListener

spring.cloud.stream.bindings.consumerB.group=CID_CONSUMER_B
spring.cloud.stream.bindings.consumerB.contentType=text/plain
spring.cloud.stream.bindings.consumerB.destination=CONSUMER_B_TOPIC
spring.cloud.stream.metaq.bindings.consumerB.consumer.tags=CONSUMER_B_TOPIC_TAG

spring.cloud.stream.bindings.consumerC.group=CID_CONSUMER_B
spring.cloud.stream.bindings.consumerC.contentType=text/plain
spring.cloud.stream.bindings.consumerC.destination=CONSUMER_C_TOPIC
spring.cloud.stream.metaq.bindings.consumerC.consumer.tags=CONSUMER_C_TOPIC_TAG
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;思考和总结&quot;&gt;Reflections and Summary&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;The cause was not complicated, but many people might stop at the first layer—an inconsistent subscription relationship caused the backlog. We still need the curiosity to investigate more deeply.&lt;/li&gt;
&lt;li&gt;Avoid maintaining two sets of configuration in production wherever possible, because doing so adds cognitive overhead.&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item>
</channel></rss>