MiMo Code Goes Open Source, but Xiaomi’s Real Ambition Is MiMo SoloEngine

In the early hours of June 11, Xiaomi officially released and open-sourced MiMo Code. Released under the MIT license, it comes with a free built-in MiMo-V2.5 model and scored 62% on SWE-Bench Pro—5 percentage points higher than Claude Code. The media went wild. Developers were ecstatic.

But if you only noticed MiMo Code, you missed the most critical move in Xiaomi’s grand strategy.

MiMo Code is built for developers. Xiaomi’s real ambition is MiMo SoloEngine—a low-code platform that enables every industry to build Agentic AI systems.

1. How Powerful Is MiMo Code?

Let’s start with MiMo Code, as it is indeed Xiaomi’s heavyweight weapon in the Agent space.

Persistent Memory System—A triple mechanism comprising project memory, session checkpoints, and task progress. The main Agent focuses on execution while an independent SubAgent handles all documentation. Context window filling up? The SubAgent automatically compresses a clean briefing, allowing the main Agent to continue seamlessly. Even after hundreds of long-running conversation rounds, no critical information is lost.

Compose Mode—Press the Tab key to switch modes. Provide a simple idea, and the system automatically completes the entire workflow: design → planning → coding → testing → review. Industrial-grade code delivery.

/dream Command—Triggered automatically every 7 days, an independent Agent reads historical sessions and existing memory files, performing merging, deduplication, path validation, and compression to converge scattered memories into a compact current state. MiMo Code doesn’t start from scratch each time—it grows continuously with accumulated project understanding.

Voice Input—Powered by MiMo-V2.5-ASR speech recognition, supporting verbal modification instructions and operational commands without ever touching the keyboard.

On SWE-Bench Pro, MiMo Code achieved 62% (Claude Code scored 57%). On Terminal Bench 2, it reached 73% (Claude Code scored 68%). Under identical model conditions, the Agent framework alone accounts for this 5-percentage-point difference.

The developers’ problem is solved. But what about everyone else?

2. The Gap Between 87% and 10%

Amazon Web Services revealed a striking statistic at the AIGC2026 Summit: 87% of enterprises claim to have deployed AI at scale, yet only 10% have genuinely derived value from it.

The root cause isn’t weak models—MiMo-V2.5-Pro already ranks first globally among open-source models on the Agent Index. Nor is it insufficient compute—after Xiaomi’s 99% price reduction, API costs have dropped to electricity-bill-level pricing.

The real issue is this: the ability to build Agents remains locked in the hands of developers.

Currently, there are only two paths to building AI Agents. The first is low-code Workflow platforms like Dify and n8n. Visual canvas, drag-and-drop nodes, rapid assembly. But their core logic relies on “preset paths”—controlling flows with if/else conditions without supporting true autonomous decision-making. When encountering scenarios outside the presets, the workflow stalls.

The second path is code-based development frameworks like LangChain and CrewAI. They support true Agentic AI but require Python programming skills. A lawyer won’t use LangChain. An accountant can’t configure a ReAct Agent. A marketing specialist doesn’t write Python.

Low-code platforms are easy to adopt but don’t support genuine autonomous decision-making. Code frameworks enable autonomous decision-making but are only accessible to developers.

MiMo Code fills the gap for developers. MiMo SoloEngine fills the gap for every other industry.

3. SoloEngine: The True Agent Platform for Everyone

SoloEngine is the first low-code Agentic AI development platform. Its mission is clear: enable people in every industry to build their own Agentic AI systems, just as developers use MiMo Code for Vibe Coding.

How does it achieve this?

SoloEngine encapsulates the ReAct architecture, Tool calling, MCP protocol, Skills, and SubAgents entirely in the backend. Users don’t need to understand these technical terms—just open a browser, drag Agents onto the canvas, connect their collaboration relationships, configure the required tools, and click Run. The backend automatically compiles everything into a dedicated Agentic AI system.

This system is just as powerful as MiMo Code. Each Agent runs a ReAct loop (Think → Act → Observe → Repeat), making real-time judgments based on current conditions. When encountering unexpected situations, it adjusts its strategy autonomously. When discovering better approaches, it proactively switches paths. The entire process requires no if/else statements and no preset paths.

Progressive Disclosure—Tools, Skills, and MCP protocols load on demand, reducing token consumption by over 85%. Unified Adaptation Layer—covering all major models including OpenAI, Anthropic, Ollama, MiMo, DeepSeek, Tongyi Qianwen (Alibaba), and Zhipu. One-Click Packaging—assembled Agent teams can be packaged into complete products for self-deployment or distribution.

Open-sourced under Apache 2.0, completely free.

4. The OPC Era: SoloEngine’s True Battlefield

Nationwide, One-Person Companies (OPCs) have surpassed 16 million, accounting for 27.4% of all enterprises. 2026 has been dubbed the “Year of OPC,” with over 20 cities introducing dedicated OPC support policies.

The core demand of these “one-person companies” is to replace traditional teams with AI Agents, achieving “one-person armies.” But LangChain requires programming skills, and Dify’s Workflows don’t support genuine autonomous decision-making.

SoloEngine was built precisely for this era.

A cross-border e-commerce operator builds an Agent team with SoloEngine: a Product Analysis Agent (automatically analyzing competitor data), a Copywriting Agent (generating product descriptions), and a Customer Service Agent (automatically handling customer inquiries). Three Agents collaborating—running 6 online stores single-handedly is no longer a myth.

A lawyer builds a legal affairs Agent team with SoloEngine: a Case File Analysis Agent (automatically extracting key information from case files), a Legal Research Agent (automatically matching relevant regulations), and a Document Generation Agent (automatically drafting legal document templates). The lawyer only needs to review and sign.

An independent developer builds a full-stack development Agent team with SoloEngine: a Requirements Analysis Agent, an Architecture Design Agent, a Code Implementation Agent, and a Testing Agent. One person accomplishing what once required an entire team.

SoloEngine transforms Vibe Coding into Vibe Everything.

5. Xiaomi’s True Ambition

Xiaomi’s pace of expansion in AI has exceeded most expectations.

In April 2025, MiMo-7B was open-sourced. In March 2026, MiMo-V2-Pro launched, and the miclaw mobile Agent began closed beta the same month. April saw the MiMo-V2.5 series open-sourced and the Agent ecosystem platform enter public beta. In May, MiMo-V2.5-Pro claimed the top spot globally among open-source models, with API prices slashed by 99%. June brought MiMo Code’s release and open-sourcing, alongside SoloEngine’s simultaneous open-source launch.

From a 7-billion-parameter model to a trillion-parameter flagship, from follower to frontrunner—Xiaomi achieved this in less than a year.

But no matter how powerful large models are, they are essentially just a “brain.” What Xiaomi aims to do next is equip this brain with “hands and feet.”

MiMo Code provides hands and feet for developers. SoloEngine provides hands and feet for everyone.

MiMo provides the “brain” for Agents, while SoloEngine provides the “building platform” for Agents. Together, they elevate Xiaomi’s strategy from “building models” to “building platforms” and “building services.”

Xiaomi’s ecosystem advantages are further amplified through SoloEngine: MiMo’s model capabilities, 99%-reduced API costs, 1 billion IoT devices, the Agent ecosystem platform, and the miclaw mobile Agent—these resources are interconnected by SoloEngine, forming an ecosystem moat that other platforms cannot easily replicate.

MiMo Code is open source, and developers are celebrating. But Xiaomi’s true ambition is to give every industry its own “Claude Code.”

This ambition has a name: MiMo SoloEngine.

Open Source · Apache 2.0 · github.com/Sh4r1ock/SoloEngine

Building a SaaS engine in public: an affiliate program that isn’t one hardcoded scheme

For most of this series I have been shipping the parts of a SaaS that you can see: auth, multi-tenancy, an admin console, billing. The affiliate program is the part I deliberately left for last, because it is the one most likely to get built as a special case and regretted later. This post is about finishing it without that regret.

LaraFoundry is a reusable Laravel SaaS core I am extracting in public from a CRM that already runs in production, one module at a time. The core is free and stays free for everything except money. The day a business wants to charge its own customers, that is the paid billing add-on. An affiliate program is squarely on the paid side: it is a way to grow paid revenue, so it ships inside that add-on, on top of the billing engine I wrote about a few posts back.

Here is what “done” means concretely. A partner identity with a unique referral code. A capture link that attributes a new tenant to the partner who sent them. Commission that accrues when a referred tenant pays, on rules you choose. A clawback when that payment is refunded or fails. A super-admin payout console with per-currency totals and a CSV export. And a self-serve dashboard where a partner sees their own referrals and balance. All of it under 314 Pest tests.

Two decisions shaped every line, and they are the whole point of the post.

Decision one: an engine, not a scheme

The easy way to build an affiliate program is to encode the one you want. Twenty percent, recurring, paid on every invoice. It works, you ship in a day, and you have just fenced every host that uses your core into your commission policy. A reusable core cannot do that. So instead of a scheme I built an engine, configured along three axes that do not know about each other.

'affiliate' => [
    'enabled'     => true,
    'eligibility' => 'auto',          // auto | self_serve | admin
    'commission'  => [
        'trigger'       => 'recurring', // first_payment | recurring | lifetime
        'window_months' => 12,
        'basis'         => 'per_plan',  // per_plan | percent | fixed
    ],
],

Eligibility answers who becomes a partner. auto mints a code for every user so anyone can share a link. self_serve lets users opt in but holds them pending an admin approval. admin means partners are invited only. Trigger answers which payments pay out. first_payment is a one-time bounty per referral. recurring keeps paying for a window, twelve months by default, then stops. lifetime never stops. Basis answers how the amount is figured. per_plan reads a commission amount from the plan dictionary, percent takes a share of the net payment, fixed is a flat fee. A per-partner override can raise or lower the percent rate for a specific affiliate.

Because the three axes are orthogonal, any combination is valid and the engine does not branch into a tangle of special cases. The trigger decides whether a given payment is eligible at all; the basis, independently, decides the amount once it is. The default is the sensible one (a code for everyone, recurring for a year, priced per plan) but it is a default, not a law.

Decision two: zero host code

The part I am happiest with is that turning a partner program on does not add a single line of PHP to the host application. It rides two events that already existed before the affiliate program did.

// Free core, public: fired whenever a tenant is created.
event(new CompanyCreated($company, $owner));

// Paid add-on, fired whenever a company payment is processed.
event(new CompanyPaymentProcessed($payment));

CompanyCreated is part of the free core. It fires on every signup regardless of billing or affiliates, because the core needs it for its own bookkeeping. CompanyPaymentProcessed is the billing add-on’s own event, fired by the gateway webhook listener when a charge settles. Neither was invented for the affiliate program. The affiliate program simply subscribes to them.

Event::listen(CompanyCreated::class, AttributeReferral::class);
Event::listen(CompanyPaymentProcessed::class, AccrueAffiliateCommission::class);

AttributeReferral reads the referral cookie set by the capture link and locks the new company to a partner. AccrueAffiliateCommission runs the trigger and basis logic and writes a commission row. Both listeners, and the engine behind them, are the proprietary part of the add-on, so I am describing them rather than dumping them. The shape is what matters here: the add-on registers these listeners from its own service provider, gated behind the affiliate.enabled flag. A host turns the feature on in config, publishes the two Inertia pages, and is finished. There is no controller to write, no event to fire by hand, no model to touch. The seam was already in the core; the add-on plugs into it.

Attribution is lock-once, and not an oracle

A referral link is /r/{code}. Hitting it drops an encrypted cookie and redirects. The redirect is uniform: it goes to the same place whether the code is real or not, so the endpoint never becomes an oracle you can poke to enumerate valid codes. When that visitor later creates a company, AttributeReferral fires.

Attribution locks once. The first partner to refer a company keeps it; a later link cannot steal an existing tenant, because the referral row is keyed unique on the referred company. Self-referral is blocked, so a partner cannot sign up under their own code. And the partner gets exactly one bounty per referral under the first_payment trigger, tracked so an out-of-order webhook cannot pay it twice. That last one was a real bug an adversarial review pass caught before it shipped: the idempotency key was per payment, not per referral, which under a replayed or out-of-order webhook could have accrued two first-payment bounties. The fix was to make “first payment” mean “this referral has no live commission yet,” and a regression test now guards it.

Money stays honest

Commissions are stored per currency, in integer minor units, with no converter anywhere. This is the same rule the rest of the billing engine follows. A partner who refers customers paying in euros, zlotys and dollars accrues three separate balances, and the console reports three separate totals. There is no FX rounding turning real money into an approximation of a dollar figure.

Payout is manual on purpose, and I want to be clear that this is a feature, not a gap. The add-on has no money-out payment provider, and it does not pretend to have one. What it gives the operator is the truth: a report of who is owed what, per currency, with a state machine they drive by hand. A commission starts pending when it accrues. The operator approves a batch (pending to approved), then marks them paid once the money has actually left, attaching a payout reference (approved to paid). A commission can be voided manually for a refund the automatic clawback could not catch. Speaking of which: when a payment is refunded or fails, the matching commission for that same invoice is clawed back automatically, unless it has already been paid out, in which case it is left alone for a human to reconcile.

The console exports the current filtered report as a CSV, and that CSV neutralises spreadsheet formula injection: any cell whose text starts with one of the dangerous characters is prefixed so a spreadsheet treats it as literal text, not a formula. A payout reference an operator typed should never execute in Excel.

The two surfaces

There are two UI surfaces, both Inertia and Vue pages the host publishes, the same delivery pattern the rest of the core’s frontend uses, so the add-on does not drag in a second build pipeline.

The super-admin gets the payout console: the commission report with per-currency totals by status, the bulk approve / mark-paid / void actions, and the CSV export. The partner gets a self-serve dashboard scoped strictly to their own user id: their code, their referrals, and their balance by currency and status. The dashboard never leaks another partner’s numbers, which the tests assert directly.

The proof, because every release claims one

314 Pest tests in the add-on, Pint clean, and a two-agent adversarial review (one for security, one for correctness) before this could be called done. The security pass came back with no high findings: the partner dashboard scope is airtight, the console is gated three ways (route, policy, form request), the payout state machine is safe against id tampering, and attribution is self-referral-blocked and oracle-free. The correctness pass found the out-of-order double-bounty I described above, plus a quieter one: a percent rate provided through an environment variable arrives as a string, and the coercion had been rejecting numeric strings and silently falling to zero percent, which would have paid nobody. Both are fixed with regression tests. A payout you cannot trust is worse than no program at all, and the failure modes here are the quiet kind.

The honesty section

Every post in this series gets one. This is the new code, not the proven-by-years code. The core underneath the affiliate program comes out of a CRM that runs in production, but the affiliate engine itself is greenfield, written for this milestone, and proven by tests rather than by time. Its accrual rides on CompanyPaymentProcessed, which is driven by the billing gateways I have built and tested but not yet run against a live merchant account end to end. So I am not going to tell you commissions have been earned and paid in production, because they have not. They have been earned and paid across 314 tests.

And it is a lean engine, not a full affiliate platform. There is no automated money-out, no fraud scoring, no tiered partner ladders. Those are deliberate non-goals for a first version. What is here is the spine: attribution, a configurable accrual engine, clawback, a payout console and a partner dashboard, drawn so a host can grow it without forking it.

The thread, again

Every phase in this series lands on the same shape. The interesting engineering is fine, but the real lesson is always a default that was right for one app and wrong for a reusable one. For the affiliate program it was the commission scheme itself. The CRM I extracted from would only ever have needed one policy, because it serves one business, and hardcoding twenty-percent-recurring would have been completely reasonable there. In a reusable core it would have quietly become everyone’s policy. The work was not building referral tracking. It was refusing to bake in one company’s idea of what an affiliate program owes, and wiring the whole thing so a host turns it on without touching their own code.

Follow along

  • Code, versioned and Pest-tested before each tag: github.com/dmitryisaenko/larafoundry
  • The project and roadmap: larafoundry.com

Micro Context Switching

NB, These are the mental rambling of an aged software engineer and AI-sceptic.

Writing computer code is a little like writing a well considered letter/document. In both cases we plan what we want to say, we have to consider that text in the micro (each statement/sentence) and the macro (the document/application as a whole.) But AI interference, I suspect in both cases, disrupts the thought process.

We used to say ‘software developers are not paid to write code, they are paid to think and solve problems’. This remains true even in these days of AI, perhaps even more so. But typing in code is itself part of the thought process and therefore the problem solving. Some might say the code is the ultimate expression of the software design process.

However, AI tools embedded in the Integrated Development Environments (IDEs), whilst some might find them useful, can be a serious distraction and disrupt the thought and coding process.

Before, or after disabling the AI tools, the process was easier. After studying the brief (task description) and considering candidate solutions, you start reading the code looking for the touch points. Identifying what code needs to be removed or changed and building a mental map of the code needed to effect an appropriate solution. You might not set out with a single solution in mind, expecting the existing code to inform your judgement.

This follows the observation “plans seldom survive first contact with the enemy.”

As an aside, this is where I find having a comprehensive collection of unit tests really helps. Agreed, as well as making the change, unless it is just a refactor, it is likely to demand a revision of the unit tests, and require more effort. However, you will be glad when a breaking change is trapped by a failing unit test and you realise the change is not a straightforward patch or even a minor change but something that will impact code outside of the unit. A you discovered it before adversely impacting others.

Without AI “assistance” the process is, read the code, understand the code in the context of your developing mental model and the solution you are applying, make the necessary changes, repeat. A unidirectional feedback loop that delivers a dopamine hit.

With AI, I find that flow is disrupted. Read the code, understand it, start making the change and bang! Your thought process is distracted by a suggestion from the AI you did not ask for. Some might say, you did ask for it by typing in the code, I disagree. The feedback loop has now become bidirectional.
It is like working with a bad pair-programmer who keeps taking the keyboard away from you and enters code you don’t recognise.
You have to regain composure, read the suggested code, which to be fair might be as good and sometimes better than what you were going to write, evaluate the suggestion aside your mental model before accepting or rejecting it.

“Now where was I, and so much for me expected dopamine hit.”

The mental disruption is worse than a colleague tapping you on the shoulder for a coffee break, which can actually be beneficial when struggling with a problem, or a notification pinging up on the screen informing you of a new email or pending software update.

The AI is relentless and insistent that it knows your thoughts better than you. It might be right but surely it is my right to maintain and apply those thoughts in my own way. After all, that is what I am being paid for isn’t it?

Please let me use my brain before it starts to atrophy or rot from reviewing AI-generated code.

有人在拆 Transformer:Memory Caching 與 CTM 各拆走了一半

這篇要談的兩篇研究——Google 的 Memory Caching(RNNs with Growing Memory)和 Sakana AI 的 Continuous Thought Machine(CTM)——常被包裝成「Transformer 殺手」。不是。它們是兩篇研究論文,不是產品,也不是要取代 Transformer。把它們放在一起讀,真正的故事只有一句:

Transformer 的 self-attention記憶(在上下文裡 recall)和計算(思考發生在 forward pass)綁在同一個機制裡,代價是 O(L²)。這兩篇各拆走一半。

Memory Caching 拆記憶那一半,CTM 拆計算那一半。理解了這個軸,後面所有細節都會歸位。

一個先講清楚的規矩:本文只採用原論文能支持的宣稱。二手文章裡那些「在 SWE-bench / GPQA 上如何如何」的數字,凡是回不到原論文的,一律不寫。這兩篇論文本身都沒有報告 SWE-bench 結果——把二手整理的 agent 數字寫成論文結論,是這個題目最常見的造假。

一、成本牆:融在一起的代價

先講為什麼有人想拆。

self-attention 可以理解成一種可微分的關聯記憶:每個 query 去比對所有 key,加權讀取 value。這讓模型很會在上下文裡做 recall,也讓 in-context learning 成立。但序列長度是 L 時,完整 self-attention 的時間與空間成本是 O(L²)。相關理論工作也指出,這個二次成本不只是實作不夠好,而有更深的計算複雜度限制(見 On the Computational Complexity of Self-Attention)。

推理時 KV cache 緩解了自回歸生成重複計算歷史 token 的問題,但沒有免費午餐:KV cache 本身吃大量顯存,每生成一個 token 仍要與整段上下文互動。當上下文從 8K 推到 128K、1M,瓶頸通常從 FLOPs 轉向記憶體容量、記憶體頻寬、服務成本

這裡要區分清楚一件事,因為後面會反覆用到:「發布」≠「可用」≠「可商用」。長上下文視窗能跑,跟它在你的延遲與成本預算內能跑,是兩回事。成本牆主要卡在「可商用」這一層——而這兩篇論文,目前都還停在「論文能跑」的更前面一層。

把這個機制拆開看,它其實同時做了兩件事:記住很多、可以讀取很多(記憶),以及運算就發生在這一次前向傳播裡(計算)。Transformer 把這兩件事用一個機制、一個 O(L²) 的價格綁在一起。接下來的兩篇論文,分別質疑其中一半。

二、Memory Caching:拆「記憶」那一半

這篇出自 Ali Behrouz 等人(Google),也就是做 Titans 的同一個團隊(arXiv:2602.24281,2026 年 2 月)。先記住這個團隊背景,到第四節會用上。

傳統 recurrent model 的核心問題是固定記憶。RNN、線性注意力、某些 state-space 或 recurrent memory 變體,把過去壓縮進一個固定大小的 hidden state。這帶來 O(L) 的效率,卻造成長序列下的資訊擠壓:越往後,早期資訊越容易被覆蓋、模糊、遺忘。

Memory Caching 的想法很直接:不要只留當前 hidden state。把序列切成多個 segment,每個 segment 結束時的 memory state 當作 checkpoint 存下來(cache)。後續 token 不只查詢「當前線上記憶」,也能查詢過去 segment 的 cached hidden states。換句話說,RNN 不再只有一本不斷被覆寫的筆記本,而是定期留下壓縮快照。

論文摘要把這個方法的定位講得很清楚:它提供一個介於兩端之間的可調折衷——RNN 的固定記憶(O(L))和 Transformer 的成長記憶(O(L²))之間。

這裡可以建立一個直覺(以下是我從機制推導的直覺,不是論文引用的複雜度結果):假設每段長度 s、整段長度 L,需要查詢的 cached memory 約 L/s 個。若每個 token 都查所有 checkpoint,成本可粗略視為 O(L × L/s) = O(L²/s)。把 s 想成一個旋鈕:s 越大、越接近普通 RNN 的 O(L);s 越小、checkpoint 越密、越往光譜的另一端靠。它不是魔法般消除成本,而是給你一個刻度:用多少記憶,換多少 recall。(嚴格說 s=1 並不等於 attention——那只是光譜的極端,不是同一個東西,這點不要過度宣稱。)

論文提出四種使用 cached memory 的方法,命名都來自論文本體(Introduction 的「Novel Aggregation Strategies」與各節標題,例如 §3.2 就叫 MEMORY SOUP):(Gated) Residual Memory——用殘差連接加上 context-aware gating 聚合多個記憶狀態;Memory Soup——借自 weight souping,平均多個 cached memory module 的參數(對非線性記憶才有區別);Sparse Selective Caching (SSC)——用類似 MoE router 的方式只選最相關的 top-k cached memory 參與讀取,控制超長上下文成本。摘要只用了簡短說法「gated aggregation and sparse selective mechanisms」,完整命名在正文,要查以論文本體為準。

落地視角:Memory Caching 沒有消除成本,它把成本變成可調的。要判斷它能不能進真實 workflow,該問的不是「它比 RNN 強多少」,而是 retrieval fan-out 多大、cached memory 的記憶體頻寬代價多少、跟單純加大 KV cache 比省在哪。論文本身沒回答這些工程問題——這是「論文能跑」和「可商用」之間還沒跨過的距離。

從技術信仰看,這篇務實:它不否定 Transformer 的成長記憶有價值,反而承認它有價值,然後問——能不能用壓縮的記憶 checkpoint 拿到一部分好處,而不付全額 O(L²)。

三、CTM:拆「計算」那一半

CTM 出自 Sakana AI(東京,Darlow、Regan、Risi 等人,arXiv:2505.05522,NeurIPS 2025 Spotlight)。值得一提:共同作者裡有 Llion Jones——Attention Is All You Need 的原作者之一、Sakana 共同創辦人。當年提出 Transformer 的人,現在在拆它,這件事本身就有意思。它的問題意識和 Memory Caching 完全不同:它不太管長上下文 recall,它質疑的是現代神經網路對「時間」與「計算」的抽象方式。

先解名,因為名字本身就是論點。Continuous Thought Machine——「思考」是一個沿著內部時間連續展開的過程,而不是一次前向傳播吐一個答案。和 Memory Caching 的字面命名不同,CTM 的名字是個主張:思考有長度。

三個機制(全部對照論文本體確認過):

1. Internal ticks(內部時間軸,與序列長度 decoupled)。 論文原文:“The CTM uses an internal dimension t∈{1,…,T}, decoupled from data dimensions.” 模型沿一條自己生成的時間軸 t ∈ {1,…,T} 展開,這條軸和輸入序列無關。即使輸入是一張靜態圖片,CTM 也能在內部跑 50 個 tick,不斷更新神經活動、重新注意輸入、修正輸出。這就是「計算」這一半被從序列長度上拆下來的關鍵。

2. Neuron-level models(NLM,神經元級的時間處理)。 標準網路裡,一個 neuron 多半只是一次 activation:輸入進來、過非線性、吐一個值。CTM 給每個 neuron 一個自己的小型 MLP g_θd,處理它自身的 pre-activation history。神經元不再是靜態函數,而是有局部時間歷史的微型處理器。

3. Synchronization as latent representation(用同步當表示)。 這是最反直覺、也最核心的一點。CTM 不直接拿某一刻的 hidden state 當表示,而是追蹤不同 neuron 的活動歷史,計算 neuron pairs 之間的同步:S_t = Z_t · (Z_t)ᵀ(Z_t 是到第 t 個 tick 為止的神經元活動歷史矩陣;同步用的神經元對在初始化時隨機取若干對,例如 32 對)。這個 synchronization 再被投影成 attention query(action synchronization)和輸出 logits(output synchronization)。換句話說,模型真正拿來決策的,不是單一時間切片,而是神經活動在時間上的協調模式

Adaptive compute。 CTM 在每個 tick 都產出 yt,並算 certainty = 1 − normalized entropy。推理時可以設一個門檻(例如 0.8),certainty 夠高就提前停。難的 instance 多想幾個 tick,簡單的早停。計算量隨輸入難度變化——這就是「計算這一半」變成可調旋鈕的具體樣子。

順帶分清楚:CTM 和 chain-of-thought 不是同一回事

你可能會想到 chain-of-thought(CoT)。值得先把兩者分開——它們不在同一層。

CoT 是提示技巧,跑在普通 Transformer 上:你讓模型把「Step 1… Step 2…」寫成輸出 token,思考過程就是那串文字。想多想一點,就是多寫 token——成本仍綁在序列長度上,仍走 O(L²) 那條路。

CTM 是架構,不是提示。它的「思考」不產生任何 token:模型沿內部時間軸展開神經活動,可以對一張靜態圖片跑 50 個 tick,輸出零個中間 token。一句話分辨:CoT 用 token 思考,CTM 用內部時間思考。 這個差別正是本文的主軸——CoT 是在 Transformer 既有的機制裡爭取更多推理(所以付一樣的 token 帳單),CTM 則把推理從 token 軸上整個拿開。

四、同一個問題的兩半

現在把兩篇放回一起。它們不是「對決」,也不是兩個競爭的賭注——它們在拆同一個東西的不同部位。

Transformer 的 self-attention 同時扛了記憶計算,付 O(L²)。

  • Memory Caching記憶軸:讓 recall 便宜、可增長,不走完整的二次成本。它的成敗好衡量——Needle-in-a-Haystack、LongBench、in-context retrieval 這類任務。
  • CTM計算軸:讓內部計算時間和序列長度脫鉤,用神經動態與同步當核心。它關心的是「同一個輸入能不能投入不同長度的內部思考」,更接近推理、規劃、模擬。

這也是為什麼第二節要你記住 Behrouz 是 Titans 團隊:Memory Caching 是「外部/顯式記憶」這條線的延伸思路——記憶是一個可以加掛、可調成本的層。CTM 走的是另一個方向——計算不是一次性的前向傳播,而是一段可以拉長的內部過程。一個在問「記憶怎麼便宜」,一個在問「計算怎麼動態」。

所以它們互補,不互斥。把它們擺成「誰取代誰」會錯過重點——重點是 Transformer 把兩件事綁死了,而現在有人開始分別鬆綁。

五、Scaling law 會被改寫嗎?

傳統 scaling law 關注三個變數:model size、data size、training compute。Kaplan 等人的工作強化了「規模帶來可預測進步」的信念;Chinchilla 進一步指出固定訓練算力下,參數量與訓練 token 數要更平衡地擴張。

這兩篇不會推翻這些 scaling law。但它們各自提示一個新變數正在變重要——以下是推論,不是論文宣稱:

  • Memory Caching 指向 memory capacity / retrieval cost。 模型不只要大,還要能用合理成本保存與檢索長期資訊。未來的 scaling 帳,可能不能只看參數和 token,還要看記憶容量、壓縮率、retrieval fan-out、記憶頻寬。
  • CTM 指向 test-time compute / internal dynamics。 模型不只在訓練時花算力,也在推理時分配內部思考步數。若難題需要更多 tick、簡單題可早停,那 scaling 就不只是「訓練更大的模型」,還包括「測試時怎麼有效花算力」。

這兩個推論都錨在前面講過的機制上——O(L²/s) 那個旋鈕、tick 數那個旋鈕——不是憑感覺喊未來。能不能成立,要看後續有沒有人在真實規模上把這兩個旋鈕跑出可預測的曲線。目前沒有。

六、實驗數據與現實局限

這節最重要,因為它決定了前面所有東西該打幾折。再說一次:這是兩篇研究論文,不是產品。

CTM 的驗證任務(對照論文本體):2D maze(39×39,並可重複套用泛化到 99×99)、ImageNet-1K(搭配 ResNet-152 特徵抽取器、50 個 tick 下 72.47% top-1,論文自己也說不是衝著 accuracy 來的)、parity(64-bit 累積 XOR)、CIFAR-10/100、sorting、Q&A MNIST、RL(CartPole、Acrobot、MiniGrid)。注意那個 ImageNet 數字是 CTM 接在強 CNN backbone 上的結果,不是端到端的獨立分類器——把它讀成「CTM 自己拿到 72%」會高估。論文明講不是要刷 SOTA“preliminary and not intended to beat state-of-the-art … a limitation of this paper is its relatively limited depth of comparison since we favored breadth.” 自陳限制也很清楚:internal sequence 讓訓練時間拉長,NLM 增加參數量。換句話說,它買到的「內部思考」是用訓練成本和參數量換的——這正是「可商用」層該追問的代價。還有一筆推理側的帳:certainty 早停是 data-dependent 的,難的 instance 會一路跑到滿 T 個 tick,per-instance 延遲不固定,會讓延遲預算和 batched serving 變難——adaptive compute 的彈性不是免費的。

Memory Caching 的有效證據主要在語言建模、長上下文理解、in-context recall。論文摘要的措辭很誠實:在 recall 密集的任務上,Transformer 仍取得最佳準確率,MC 變體做到的是「競爭性表現、縮小與 Transformer 的差距、勝過 SOTA recurrent model」。注意這個層次——它不是宣稱打贏 Transformer,是宣稱在 recurrent 這條線裡把差距縮到值得一試。

兩篇都該謹慎解讀的共同點:截至可見的原論文資料,都沒有正式報告 SWE-bench / SWE-bench Verified / SWE-bench Pro 結果。如果你在某篇二手文章看到這些架構「在 agent 工具調用上如何如何」的數字,而那數字回不到原論文——它就不該被當成論文結論。這不是吹毛求疵,這是「發布 ≠ 可用 ≠ 可商用」的最後一道防線。

七、重新組裝

如果你接受第四節那個框架——Transformer 把記憶和計算綁在一起,這兩篇各拆一半——那麼下一步是什麼,幾乎是邏輯上的必然,而不是許願:拆開之後,把它們重新組裝。

未來更可能出現的不是某個單一架構勝出,而是混合架構:Transformer 保留強大的通用建模能力當基座;一個 Memory-Caching-like 的層提供長期、低成本、可選擇性讀取的記憶;一個 CTM-like 的核心提供內部推理時間與 adaptive compute。記憶軸便宜化、計算軸動態化,各司其職。對需要長期互動的 agent 或 world model,這個分工特別合理——昂貴的 attention 不該扛所有歷史,內部推理也不該被序列長度綁死。

需要標明:這一節是推論,不是任何一篇論文的宣稱。 沒有人證明這個組裝會成立。但如果你問「為什麼會有人同時做這兩個方向」,答案不是巧合——是因為它們在拆同一個東西。

結語

Transformer 不會立刻退場。它的軟硬體生態、訓練 recipe、開源工具鏈、產業部署都太成熟,短期內仍是主流基座。

但架構競爭的焦點正在改變。下一階段的進步,不會只靠堆參數和拉長上下文。記憶怎麼便宜、計算怎麼動態——這兩件被 self-attention 綁在一起、現在被分別鬆綁的事,會變成新的核心問題。

Memory Caching 和 CTM 的共同訊號不是「Transformer 要被取代了」。是更安靜的一句:有人開始拆它了。Transformer 的統治還沒結束,但它的孤獨時代正在結束。

參考來源

  • Memory Caching: RNNs with Growing Memory — Behrouz, Li, Deng, Zhong, Razaviyayn, Mirrokni (Google). arXiv:2602.24281 — https://arxiv.org/abs/2602.24281
  • Continuous Thought Machines — Darlow, Regan, Risi, Seely, Llion Jones (Sakana AI). arXiv:2505.05522 — https://arxiv.org/abs/2505.05522
  • Continuous Thought Machines — NeurIPS 2025 (Spotlight), OpenReview — https://openreview.net/forum?id=y0wDflmpLk
  • Continuous Thought Machines — Sakana AI 官方互動 demo/blog(同一研究) — https://pub.sakana.ai/ctm/
  • Attention Is All You Need — https://arxiv.org/abs/1706.03762
  • Scaling Laws for Neural Language Models(Kaplan et al.)— https://arxiv.org/abs/2001.08361
  • Training Compute-Optimal Large Language Models(Chinchilla)— https://arxiv.org/abs/2203.15556
  • On the Computational Complexity of Self-Attention — https://arxiv.org/abs/2209.04881

Modern C++ Support in CLion: What’s New

Modern C++ makes advanced high-performance techniques more accessible, with features like compile-time computation, zero-overhead abstractions, and expressive template code. But as your codebase grows, your ability to use these techniques productively depends heavily on how well your tooling understands them. Without proper language engine support, modern C++ features can lead to false positives, broken navigation, and missing completion, rather than productivity gains.

Supporting these techniques is a core part of what CLion’s language engine is built for – and it keeps getting better. In this blog post, we’ll look at the most interesting improvements introduced in recent releases. To get all the updates mentioned here, you need CLion 2026.1 or later with the CLion Nova engine enabled.

DOWNLOAD CLION

C++26 features

C++26 is a large release with dozens of new features, including ones that extend compile-time capabilities and simplify metaprogramming. CLion now supports all C++26 features except reflection, which we plan to implement in the upcoming 2026.2 release (CPP-48365). Here are some examples:

#embed preprocessor directive: This feature allows you to embed the contents of binary files – such as images, icons, or encoded text – directly into your source code at compile time as byte arrays.

constexpr unsigned char logo[] = {
#embed "logo.png"
};

constexpr std::size_t logo_size = sizeof(logo);

Before C++26, this typically required external tools or custom scripts to convert binary files into C arrays, which you then had to manually sync with the source files – #embed removes that step.

Pack indexing: You can now access individual elements of a parameter pack directly by the element’s index using the pack...[index] format, for example, values...[0], values...[2], and so on.

template <std::size_t I, typename... Ts>
constexpr auto element_at(Ts... args) {
   // Use pack indexing to access an element
   return args...[I];
}

Previously, extracting a specific element required recursive template specializations or helper utilities. Now it’s as easy as indexing into an array.

Variadic friends: This feature lets you grant friendship to all classes in a template parameter pack. This is particularly useful in patterns where a group of closely related types – such as nodes in a data structure – need access to each other’s private class members.

template <class... Ts> class X {
   int data = 42;
   friend Ts...;
};

struct A {
   int get(X<A> x) { return x.data; }
};

struct B {
   int get(X<A, B> x) { return x.data; }
};

Previously, each friendship had to be declared individually, which became cumbersome as the number of types grew.

Updates to constexpr support

Compile-time code in C++ is powerful, but debugging it often requires dealing with cryptic compiler errors and little context. CLion’s latest releases bring features and improvements that make constexpr code significantly easier to debug and validate.

Constexpr Debugger

CLion 2025.3 introduced the Constexpr Debugger – the first in-IDE debugger to let you step through compile-time evaluations of constexpr and consteval code. From the debugger, you can inspect locals, navigate the call stack, and even step backward through each evaluation stage.

It’s especially helpful when constant evaluation fails. Instead of deciphering compiler errors, you can run the evaluation until failure and let CLion stop exactly at the point where things went wrong – with the full context in view.

Early detection of constexpr evaluation failures

The IDE now also provides an inspection that detects constexpr evaluation failures, for example, non-constexpr function calls or logic errors. The corresponding popup presents an evaluation trace to help you identify and fix the problems more easily.

You can also use the Constexpr Debugger to investigate the cause of a problem in more detail by clicking Run evaluation until failure in the popup. The Constexpr Debugger will run and stop at the point of the failed evaluation. You can then use Step Into, Step Over, Step Backward, and other actions to debug the code. Learn more in the documentation.

Other updates to constexpr evaluation

We extended CLion’s constexpr evaluation capabilities to cover a wider range of C++ constructs and features, including:

  • switch statements
  • if statements with initializers
  • Structured bindings
  • Trivial default initialization
  • C++20’s defaulted operator==

The following example shows CLion’s warning when evaluation of operator== fails:

The constexpr evaluator still doesn’t support some constructs. You can track our progress in the related YouTrack issues and upvote the ones that matter most to you.

New GCC and Clang extensions

We’ve improved the compatibility with compiler-specific extensions by adding support for the following:

Nested functions: CLion now supports the GCC extension for defining functions inside other functions in C code, which is useful when working with legacy codebases or embedded projects that rely on this pattern.

int main(void) {
   int outer_var = 10;

   // Define the nested function
   void nested_function() {
       printf("Inside nested functionn");
       printf("Accessing outer variable: %dn", outer_var);
   }

   // Call the nested function
   nested_function();
   return 0;
}

_Nullable and _Nonnull qualifiers: Clang’s pointer nullability qualifiers indicate whether a pointer can be null or not. The parser now recognizes these qualifiers, so code that is shared with Apple platforms or that uses Clang-specific APIs no longer produces false-positive warnings or incorrect inspections.

Conditionals with omitted operands: The x ?: y shorthand, where the middle operand is omitted, is especially useful in GNU C codebases, providing a fallback value without repeating the condition.

Designated initializer range syntax: This GCC extension lets you initialize a range of array elements to the same value in a single expression, like int arr[100] = { [0 ... 49] = 0, [50 ... 99] = 1 };. This syntax is handy in embedded and system development for initializing hardware register maps, lookup tables, or memory-mapped I/O buffers that require arrays to have predictable default values.

New code assistance and refactoring features

CLion’s code assistance and refactoring capabilities have received several improvements designed to reduce manual cleanup and repetitive work. These include smarter module imports, new inspections that catch common mistakes early, refactoring support in inactive code blocks, and automatic definition sorting.

Auto-import for C++20 modules auto-import

When you use an exported symbol but the corresponding import declaration is missing, CLion suggests inserting it automatically with a shortcut (Alt + Enter on Windows and Linux or ⌥↩ on macOS).

Note that currently, auto-import for C++20 modules works for symbols exported directly from primary module interface units and module partitions.

New inspections

We’ve added several new built-in inspections that help you keep your code cleaner and easier to read. Here are some examples:

  • CLion now automatically detects duplicate forward declarations, highlights them in gray, and offers a quick-fix to remove them, so you don’t have to hunt for them manually. You can also do this with a shortcut (Alt+Enter on Windows and Linux or ⌥↩ on macOS).
  • If your project targets C++20 or later and contains the typename keyword in contexts where it’s no longer required, CLion identifies these cases and offers a quick-fix to remove them.
  • The IDE now detects instances where C++20 designated initializers are in the wrong order relative to their declarations within the struct. CLion highlights the problem directly in the editor, so you can see it immediately rather than after compilation.
  • Another new inspection warns you when a function has a different access level than the virtual function it overrides in the base class. For example, a public virtual function in the base class can be overridden as private in the derived class. CLion helps you catch such access mismatches, which are often unintended but allowed by compilers.

Improved code assistance and refactorings in inactive code

Code analysis, code completion, and local refactorings are now fully available inside inactive preprocessor blocks, so you can edit platform-specific code without switching your build configuration.

Automatic definition sorting

CLion can now automatically sort function definitions in a source file to match the declaration order in the corresponding header file. For a one-time fix, use the Sort definitions by the order of declaration context action to reorder definitions for a single function, a whole file, or the entire project. For continuous monitoring, enable the corresponding style option in Settings | Editor | Code Style | C/C++ | Syntax Style. CLion will then flag any mismatches with an inspection, and you can fix them automatically with a single click.

The ability to edit naming rules

CLion now provides a new Edit Naming Rule dialog as part of the work to improve support for C/C++ naming rules. To get to this dialog, navigate to Settings | Editor | Code Style | C/C++ | Naming, then select a rule and click Edit, or just double-click it.

Each naming rule allows one or more styles, and for each style, you can configure the base naming pattern, the prefix and suffix, and more.

Conclusion

The improvements covered in this post are powered by CLion Nova, CLion’s custom C++ engine. Many thanks to the C++ Core team for developing it!

We’ll keep highlighting the most interesting language engine updates in upcoming release posts – more to come.

DOWNLOAD CLION

Static Code Analysis and the Rules of Zero, Three, and Five

The Rule of Zero, Three, and Five, sometimes written 0/3/5, is a set of C++ guidelines about resource ownership and the special member functions. They exist because the alternatives are double-frees, dangling pointers, and silently broken copies. In this post we’ll work through the rules by tripping over each of those bugs in turn, then look at how to enforce them automatically with static analysis. Our colleague Anna will take us through her thoughts on this.

Anna Zhukova

Anna Zhukova is a Software Developer in Qodana and brings a powerful mix of backend expertise, cross-platform skills, and a genuine passion for static analysis to the fold. She’s currently working on building and maintaining Qodana for C++ and advancing our static analysis tools to help developers ship better, more secure code faster.

Table of Contents

What are the Rules of Zero, Three, and Five in C++?

Let’s imagine a common scenario that most non-trivial codebases have to solve: You are writing a class and that class owns a resource. A resource in this context is anything that needs to be cleaned up when no longer needed: a file handle will need closing, a pointer will need freeing, and so on. At first glance, this seems like an easy job for the destructor:

// This is BAD CODE and SHOULD NOT BE COPIED
struct TreeVertex {
    vector<TreeVertex*> children;
    ...
	
    ~TreeVertex() {
        for (auto& vertex : children) {
            delete vertex;
        }
    }
};

Seems simple enough. Although, note that there is nothing preventing us from copying an instance of `TreeVertex`. What happens then?

Rules of Zero, Three and Five

Uh oh.

Your program blew up because when you copied a TreeVertex, you implicitly made both the original and the copy believe they own pointers to the (singular) set of children and are free to delete them if needed. At destruction time, the first object to be destroyed will delete the children, and the second object will cause the program to segfault because it tried to free memory that was already freed. This is known as a “Double Free” bug – a classic amongst us memory managers.

Q: Why is nothing preventing us from copying an instance of TreeVertex?

A: To copy a class, you need to have a copy constructor. We never defined one, but luckily for us (ha!) the compiler will generate one if no user-defined copy constructors exist.

Anyway, a savvy reader that has watched a CppCon keynote or two will immediately notice a code smell: we are managing a lifetime of a bunch of pointers by hand, calling low-level functions like delete. Surely all life problems will go away if we use, say, a unique_ptr?

class TreeVertex {
    vector<unique_ptr<TreeVertex>> children;
public:
    ...
    
    // ~TreeVertex()  // Not even necessary anymore!
};

The savvy reader was technically correct – this is better, in the sense that the Earth no longer blows up. However, now your class can’t be copied at all, because unique_ptr solves the problem of two owners by deleting its copy constructor altogether.

We are talking about it like it’s a bad thing, but in real life this might actually be what you want. Writing a fancy copy constructor that duplicates a resource isn’t always the right decision. Say you have an OS window as your resource – would it make sense to implicitly open new windows when you accidentally passed an object by value?

If you have decided that your tree vertices are non-copyable, and that you will let unique_ptr manage your memory for you, then congratulations! You’ve just implemented the Rule of Zero: If you can avoid defining default operations, do.

Let’s say we do want to copy our vertices though. Also, let’s say that we would like to have an internal access function for our lower-level API such that we cannot use unique pointers.

class TreeVertex {
    vector<TreeVertex*> children;
public:
    TreeVertex** children_data() { return children.data(); }
	
    TreeVertex(TreeVertex const& other) {
        children.reserve(other.children.size());
        for (auto const& vertex : other.children) {
            children.push_back(new TreeVertex(*vertex));
        }
    }

    ~TreeVertex() {
        for (auto const& vertex : children) {
            delete vertex;
        }
    }
};

The copy constructor now works okay, but this wouldn’t be C++ if there wasn’t another footgun hidden inside the initial footgun. Something that may not be obvious at first glance is that these two lines call two different functions:

TreeVertex my_vertex = other;
my_vertex = other;

The first one is indeed a copy constructor which we just meticulously defined. The second one calls a copy assignment operator – which luckily for us (ha!) the compiler generated in a similar way to the copy constructor:

// This is BAD ~~compiler-generated~~ CODE and SHOULD NOT BE COPIED
TreeVertex& operator=(TreeVertex const& other) {
    children = other.children;
    return *this;
}

Notice how just by defining a destructor and following the string of bugs, we had to define three functions: the destructor, the copy constructor, and the copy assignment operator. Finally we have arrived at…

The Rule of Three

If you define a copy constructor, copy assignment, or destructor, you should define them all.

A seemingly correct copy assignment operator looks like this:

// This is BAD CODE and SHOULD NOT BE COPIED
TreeVertex& operator=(TreeVertex const& other) {
    // Clear existing children
    for (auto const& vertex : children) {
        delete vertex;
    }
    children.clear();

    // Copy other's children
    children.reserve(other.children.size());
    for (auto const& vertex : other.children) {
        children.push_back(new TreeVertex(*vertex));
    }
    return *this;
}

Let’s forget for a moment how many times we duplicated code in this simple class (we’ll come back to that). If you want to be mean to your junior devs, ask them to find a bug here.

The bug comes out from the fundamental difference between a constructor and an assignment operator – the this object existed before you called the function. This means that you can write something like this:

my_vertex = my_vertex;

In other words, both this and other are the same thing. And instead of doing nothing, our copy assignment operator just obliterated all the children.

Luckily, some smart people came up with a solution to both the self-assignment problem and the code duplication problem. It’s called the copy-and-swap idiom.

A swap function is a special kind of function in C++ that “swaps” the contents of two objects. There is no compiler magic going on here, we just all collectively agreed to use this name. The signature looks like this:

void swap(T& a, T& b);

A default template std::swap does the generic swapping using a temporary variable, but if we know how our class works we can actually do better. Let’s define our own swap as a free friend function:

class TreeVertex {
    ...
public:
    friend void swap(TreeVertex& a, TreeVertex& b) noexcept {
        using std::swap;
        swap(a.children, b.children);
    }
};

First, we added std::swap to the list of functions for overload resolution (this is not necessary in our example, but a good general rule of thumb). Then we swap our children, letting the compiler find the most appropriate overload of swap, which turns out to be an efficient specialization created specifically for std::vector.

We can now rewrite our copy assignment operator like so:

TreeVertex& operator=(TreeVertex other) {
    swap(*this, other);
    return *this;
}

Notice that the signature has changed: we now take other by value, delegating the copying work to the compiler. Then we swap our children, and leave other to be destructed, delegating the deleting work to the destructor. No code duplication, and no bugs with self-assignment.

Q: What’s the deal with defining the body of swap inside of the class?

A: This is another idiom called hidden friends: since swap is a free function, it might be considered for overload resolution even when you are swapping things that are completely unrelated to your TreeVertex. Making the function a hidden friend prevents possible accidental implicit conversions, improves compilation times, cleans up compilation errors, and makes your cat love you.

Since defining swap is so useful, the Rule of Three is also sometimes called the Rule of Three and a Half.

We are almost at the end of today’s discussion, but there is one more thing we need to talk about: the move semantics introduced in C++11. If your company has not adopted C++11 yet, you should stop reading this blog and go update your CV.

Unlike a copy, a move operation is allowed to steal the contents from the other object. In fact, the standard says that after a move operation other is left in a valid, but unspecified state, usually only good for destruction. Allowing an object to be moved is a very powerful optimization, so let’s define a move constructor and a move assignment operator using our amazing swap function:

TreeVertex(TreeVertex&& other) noexcept {
    swap(*this, other);
}

TreeVertex& operator=(TreeVertex&& other) noexcept {
    swap(*this, other);
    return *this;
}

Piece of cake, and now you know the modern version of the Rule of Three, the Rule of Five:

If you have a destructor or either copy function (constructor or assignment), you should probably define both copy functions and both move functions.

*also known as rule of Five and a Half because of the swap function.

Q: Is noexcept necessary? What does it achieve?

A: noexcept is an important performance optimization. Standard containers like std::vector aim to have a strong exception guarantee during reallocation, and will discard your throwing move entirely in favor of an expensive-but-recoverable copy.

This is mentioned in the notes for std::move_if_noexcept. Our swap is also marked noexcept. We don’t need it as such for our own code, since what’s important for us is just that it does not actually throw anything. But third-party code which conditionally optimizes based on std::is_nothrow_swappable would care about the correct label, so it’s a good practice.

Q: Would ASan scream at us for a third time if I didn’t define these two functions?

A: Not this time! Unlike the copy constructor and copy assignment operator, the move versions are not going to be generated by default if you have the copy versions of either.

Big thanks to the excellent Stack Overflow post by GManNickG, which was my reference on this topic for the past 10 years.

Static code analysis and the Rules of Zero, Three, and Five in practice

The bugs in this post, double-free, broken self-assignment, a move constructor that quietly throws, survive code review and surface in production. Static analysis catches them at commit time.

Qodana for C++ wraps Clang-Tidy (among other things), and three checks map directly onto what we walked through above:

  • cppcoreguidelines-special-member-functions is the Rule of Five at its source: if you define a destructor, copy constructor, or copy assignment operator, this check requires you to define (or `= default` / `= delete`) the rest. It’s C.21 from the C++ Core Guidelines.
  • bugprone-unhandled-self-assignment catches the exact self-assignment bug from earlier in this post: a user-defined copy assignment operator with no self-check and no copy-and-swap.
  • performance-noexcept-move-constructor flags non-noexcept move constructors and assignment operators, so std::vector reallocation actually uses your move instead of silently falling back to copy.

All three are enabled by default (in the qodana.starter profile), or you can add them to your .clang-tidy to be sure:

Checks: >
  cppcoreguidelines-special-member-functions,
  bugprone-unhandled-self-assignment,
  performance-noexcept-move-constructor

Then just run Qodana — locally with qodana scan, or in CI via GitHub Actions, GitLab, Jenkins, or TeamCity. 

As for the Rule of Zero: there’s no single check that says “delete this destructor and use a unique_ptr instead” — Rule of Zero is more of a design discipline. What Qodana can do is point you toward it: cppcoreguidelines-owning-memory flags raw owning pointers, and the modernize-* family (modernize-make-unique, modernize-make-shared, modernize-avoid-c-arrays) nudges code toward RAII types that remove the need for special members in the first place. Just add them to your .clang-tidy and Qodana will pick them up automatically:

Checks: >
  ...
  modernize-make-unique,
  modernize-make-shared,
  modernize-avoid-c-arrays,
  cppcoreguidelines-owning-memory

Find out more about improving your code quality with Qodana.

P.S. When you should NOT use copy-and-swap

Despite being a safe bet for 99% of use cases, the copy-and-swap idiom has a problem: copy performance. Recall that the copy assignment operator we wrote takes other by value, which means the object and any underlying storage is copied regardless of whether it is actually necessary. A smarter copy assignment could do something like this:

TreeVertex& operator=(TreeVertex const& other) {
    if (this == &other) return *this;  // Self-assignment check

    auto const common_size = std::min(children.size(), other.children.size());

    for (size_t i = 0; i < common_size; ++i) {
        *children[i] = *other.children[i];
    }

    for (size_t i = common_size; i < children.size(); ++i) {
        delete children[i];
    }
    children.resize(common_size);
    children.reserve(other.children.size());
    for (size_t i = common_size; i < other.children.size(); ++i) {
        children.push_back(new TreeVertex(*other.children[i]));
    }

    return *this;
}

It’s certainly more complex but achieves something important: the storage that was already present in the copied-to object can be reused, partially or fully, depending on who has more children. For our example in particular, the gains are as follows:

Given N = children.size() and M = other.children.size():

Textbook copy-and-swap Custom copy function
calls to new M max(0, M-N)
calls to delete N max(0, N-M)

Also note that these gains are recursive, since each child has its own children. Assigning a subtree to itself is completely free with the custom copy function.

Know this caveat but always remember: premature optimization is the root of all evil.


Thank you Anna Zhukova for your contribution to the blog! Anna works on Qodana for C++. Try it today if you haven’t already or view the documentation.

Qodana for C++

#powerbi

How I Built a Treasure-Run Game Where Australia Saves the Sun

This is a submission for the June Solstice Game Jam

What I Built

For this game jam, I built Dawn Dashers, an adventure runner set during the June Solstice, the longest night of the year in Australia.

The idea started with a simple question:

What if the longest night didn’t end?

In Dawn Dashers, a rogue machine called the Turing Engine has stolen the sunrise and scattered seven Sun Fragments across Australia. Players race through deserts, bushland trails, coastal cliffs, and hidden ruins to recover the fragments and bring daylight back.

The game takes inspiration from the June solstice, but with an Australian treasure-hunting twist. Along the way, players dodge obstacles, collect relics, unlock animal abilities, and solve quick logic puzzles inspired by Alan Turing.

One thing I wanted to avoid was making the solstice just part of the story. Instead, it’s part of the gameplay itself. At the start, the world is trapped in darkness. As players recover Sun Fragments, the environment gradually becomes brighter, warmer, and more alive until the final sunrise returns.

The goal was simple:

Build something fun first, while using the June Solstice as the heart of the adventure.

Video Demo

You can also try:

  • Running through the Australian Outback
  • Discovering treasure clues
  • Solving Turing-inspired logic challenges
  • Collecting Sun Fragments
  • Unlocking animal abilities
  • Watching the world transform from the longest night back into daylight

… directly through the deployed game (using Vercel)

dawn-dashers.vercel.app

Code

GitHub Repository

Built with:

  • Unity 6
  • C#
  • Universal Render Pipeline (URP)
  • WebGL
  • GitHub Copilot

How I Built It

I started by focusing on the core gameplay loop:

Run → Dodge → Collect → Discover → Restore Light

Once movement felt fun, I started layering in everything else.

Making the Solstice Matter

The biggest design decision was making the June Solstice an actual game mechanic rather than just background lore.

The game begins during the longest night of the year. Every Sun Fragment recovered pushes the world closer to dawn.

As players progress:

  • Darkness slowly recedes
  • New areas become visible
  • Lighting becomes warmer
  • The world feels more hopeful

By the end of the game, the player has literally restored the sunrise.

Building an Australian Adventure

Rather than using generic fantasy locations, I wanted the game to feel distinctly Australian.

The adventure takes players through:

  • Outback ruins
  • Bushland trails
  • A quirky roadside servo
  • Coastal lighthouse cliffs
  • Aurora-lit Tasmania

Each region introduces new obstacles and visual themes while keeping the action moving.

A Nod to Alan Turing

Since June is also Alan Turing’s birth month, I wanted to include a tribute without turning the game into a history lesson.

The game’s antagonist, the Turing Engine, believes that endless night is the most logical state of existence.

Players encounter quick puzzles based on:

  • Pattern recognition
  • Binary switches
  • Signal routing
  • Light circuits

The puzzles are short and designed to support the action rather than interrupt it.

Art Direction

One of the biggest pivots during development was moving away from a bright arcade aesthetic and leaning into a treasure-hunting adventure style.

The world uses:

  • Warm desert colours
  • Ancient ruins
  • Dust particles
  • Golden sunlight effects
  • Treasure-map inspired UI

This direction helped tie together the themes of the solstice, exploration, and treasure hunt.

Technology

The game was built using:

  • Unity 6
  • C#
  • URP
  • WebGL deployment
  • GitHub Copilot for development acceleration

The focus throughout was on keeping the project lightweight, responsive, and playable directly in the browser.

Prize Category

Best Ode to Alan Turing

Dawn Dashers is my tribute to Alan Turing’s legacy.

Rather than focusing on historical events, I wanted to celebrate the ideas that made his work so influential: logic, patterns, problem-solving, and computation.

The Turing Engine serves as the main antagonist, and players overcome its challenges through quick puzzles inspired by computational thinking.

Best Google AI Usage

I’m also submitting for Best Google AI Usage.

I’ll be honest: I’m much more comfortable building systems and writing code than designing beautiful interfaces.

One of the challenges during this project was figuring out how to make Dawn Dashers feel cohesive visually. I had a rough idea of the atmosphere I wanted—an Australian treasure-hunting adventure, but translating that into colours, layouts, typography, and UI design isn’t my strongest skill.

That’s where Google Stitch helped.

I used Stitch to explore rapidly:

  • UI layouts and screen structure
  • Menu and HUD designs
  • Colour palettes
  • Typography combinations
  • Visual hierarchy
  • Overall art direction

What impressed me most was how easy it was to iterate. Instead of spending hours moving elements around in a design tool, I could describe the feeling I wanted and quickly generate multiple directions to evaluate.

For example, I started with a fairly generic arcade-style look, but through a few iterations in Stitch, I landed on a much stronger visual identity built around:

  • Warm desert colours
  • Treasure-map inspired UI
  • Gold accents
  • Weathered parchment styling
  • Adventure-game aesthetics

Those explorations directly influenced the final design language of the game.

I also used Gemini extensively during the design phase.

One challenge was incorporating Alan Turing-inspired puzzles without making the game feel like a classroom exercise. I wanted puzzles based on logic, patterns, and computation, but they also needed to feel like they belonged in an Australian treasure-hunting adventure.

Gemini helped me brainstorm and refine puzzle concepts such as:

  • Pattern recognition challenges
  • Binary switch puzzles
  • Signal routing mechanics
  • Treasure clues tied to Australian locations and wildlife
  • Turing-inspired logic challenges that could be solved quickly during gameplay

For example, I used Gemini to take abstract puzzle ideas and re-theme them into Australian contexts, turning generic logic puzzles into clues involving lighthouses, Outback landmarks, wildlife, and hidden Sun Fragments.

The combination of Stitch and Gemini helped bridge two areas where I typically spend a lot of time: design exploration and content creation. Instead of starting from a blank page, I could rapidly iterate on ideas, evaluate options, and focus more of my time on building and polishing the game itself.

The AI tools didn’t replace the creative decisions—they helped me reach better ones faster.

As a solo developer, having a tool that could help bridge the gap between “I know what I want it to feel like” and “I know how to design it” was incredibly valuable. It let me spend more time building gameplay while still ending up with a much more polished visual experience.

Google Stitch didn’t replace the design decisions—it helped me discover and refine them much faster.

Happy Dashing !!!

Thanks for checking out Dawn Dashers 🌅

It was a lot of fun combining the June Solstice, Australian landscapes, treasure hunting, arcade runners, and a small nod to Alan Turing into one adventure.

2026. Week 23: a UI task that stopped being small

I Thought This Would Be a Local UI Task

This week, I thought I was solving a fairly narrow task: how to show group settings more neatly in the new checklist editor. The question looked local enough: how to read a group’s state right in the item row, and how to provide a convenient entry point for editing.

New editor
The Morning chaos row is active now, and it is easy to see that the group of indicators on the right takes a significant part of the row’s space.

At first, this looked like a normal UX improvement. I needed to find a form of presentation that would not force the user to open the detail panel too often, while also not overloading the item row.

The First Version Turned Out Too Noisy

My first move was toward a richer inline representation. I wanted to show a set of signals directly in the group row, so that its state could be read quickly.

It became clear quite fast that this was the wrong direction. This UI did not make reading easier; it added noise. Instead of a more “talkative” interface, I had to move in the opposite direction: remove extra elements and compress the state representation.

In the end, the solution narrowed down to one summary pill in the group row and one shared popover for editing. That became the main UX lesson of this part of the week: in a dense editor UI, extra signals stop helping very easily.

Then a Deeper Problem Surfaced

The story did not end there. While I was working on the interface, it became clear that the problem was not only about how it looked, but also about how it worked at all.

During the work, an ambiguity in the semantics of visibility surfaced unexpectedly. Initially, I made it so that if a field was invisible, conditions like “if another item has such-and-such value” could make it visible. But the new interface showed this approach poorly, to the point that even I, the author, could not quickly understand what was going on when looking at the editor.

After several experiments, I realized that the problem was not in the editor UX but in the semantics. In the end, I had to invert it: effective_visible = is_visible && conditions_pass. In other words, an item that is invisible by default cannot be made visible by any conditions. But if a visible item also has conditions, then the item can become invisible.

A typical breaking change, and good that it happened early.

After That, the Most Honest Part of the Work Began

Once the rule became clearer, the work was not finished. After all the edits, a more unpleasant but also more honest phase began: I had to verify that the system really behaved the way it was now described.

This is where the E2E part began. It looked much less elegant than the idea of the solution itself. There were failures like Expected: "hidden" Received: "visible", there were timeouts around the drawer and popover, and there were situations where everything already looked fine locally, but the tests answered: no, the behavior is still not fully assembled.

In the end, E2E became the real finish line of this task. Not the moment when the interface already looked convincing, but the moment when the target scenarios started to converge in a verifiable form.

In Parallel, Another Shift Was Taking Shape

There was another line of work this week as well — backend cleanup. I do not want to expand on it here: it will get a separate text.

Against that background, and also against the background of publishing LLM-Assisted Deploy: You Save Typing, Not Thinking, another thought started to come together more strongly for me. A meaningful part of the work should not disappear into local edits, commits, and one-off sessions. It should be brought to the state of a public artifact: a text, a case, a note — something from which one can later read not only the result, but also the line of thought, the constraints, and the way of verification.

What I Take Away From This Week

In short, this was a week in which a small UI task refused to stay small.

First, it ran into the need to simplify my own solution. Then it brought out behavior that had not been fully thought through or clearly stated. Then it demanded that this behavior be proved and shown separately through tests.

That is probably why the week feels coherent. Its center was not that I simply made one more product improvement, but a more general movement: from a local change to an explicit contract, from an explicit contract to verification, and then further to proving and showing the work so that it would not dissolve inside the code.

Introducing TokenCap — Context Engineering for Modern Codebases

One of the biggest challenges in AI-assisted development isn’t choosing the right model.

It’s providing the right context.

As codebases grow, important information gets scattered across files, commits, architecture decisions, documentation, and debugging history. Developers spend valuable time manually gathering context before they can effectively use AI tools.

That’s why I built TokenCap.

TokenCap helps developers generate structured, AI-ready project context from their codebase, making it easier for AI assistants to understand projects without consuming unnecessary tokens.

Current Features

  1. – Project Knowledge Graph
    Visualizes relationships between files, components, services,
    routes, and dependencies.
  2. – Context Memory
    Captures project decisions, architecture notes, constraints, and
    development history.
  3. – Change Intelligence (tokencap diff)
    Transforms raw git diffs into impact analysis, risk assessment
    testing recommendations, and AI review prompts.
  4. – Context Packing
    Prioritizes and compresses relevant project information into
    token-budgeted context packs optimized for AI workflows.

Why TokenCap?
Reduce context-switching overhead
Improve AI response quality
Save tokens and costs
Understand large codebases faster
Generate structured project knowledge automatically

I’m actively working on the next generation of TokenCap, including an Obsidian-style interactive graph and advanced context management capabilities.

Would love feedback from developers building with AI.

Website: https://tokencap.vansharora.app/