{
    "version": "https://jsonfeed.org/version/1",
    "title": "Leo Qin’s Blog",
    "home_page_url": "https://leoqin.com/en",
    "feed_url": "https://leoqin.com/en/rss/feed.json",
    "description": "LeoQin is talking here.",
    "author": {
        "name": "LeoQin",
        "url": "https://leoqin.com/en"
    },
    "items": [
        {
            "id": "https://leoqin.com/en/article/MCP",
            "content_html": "<div id=\"notion-article\" class=\"mx-auto overflow-hidden \"><main class=\"notion light-mode notion-page notion-block-1c81d487a2a18007a34dce10a418cb37\"><div class=\"notion-viewport\"></div><div class=\"notion-collection-page-properties\"></div><div class=\"notion-callout notion-gray_background_co notion-block-9b71de7ee3df48d9b43b19ffb798045b\"><div class=\"notion-page-icon-inline notion-page-icon-span\"><span class=\"notion-page-icon\" role=\"img\" aria-label=\"💭\">💭</span></div><div class=\"notion-callout-text\"><div class=\"notion-text notion-block-bfb05984e2ad4f64bf31a906aa2e877c\">MCP is easy to overhype because it sits exactly where AI products feel messy: tools, files, databases, permissions, and agent workflows. My current view is more boring and more useful. MCP is not the ultimate AI solution. It is a protocol for making tool and context integration less ad hoc. That is a real problem, but solving it does not automatically solve reasoning, trust, security, or product design.</div></div></div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-c726c70091f64e2cac7b9b5763442084\" data-id=\"c726c70091f64e2cac7b9b5763442084\"><span><div id=\"c726c70091f64e2cac7b9b5763442084\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#c726c70091f64e2cac7b9b5763442084\" title=\"The Real Problem MCP Solves\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">The Real Problem MCP Solves</span></span></h3><div class=\"notion-text notion-block-ce9cd1b9c5b44564b8e7176d30bd58a5\">Modern AI apps do not only answer questions. They search files, read databases, call internal APIs, schedule calendar events, inspect code, run tests, and sometimes write changes back into the world.</div><div class=\"notion-text notion-block-940abab7178844cdbcdfe1d1eac6d133\">Without a shared protocol, every AI app ends up building its own adapters:</div><ul class=\"notion-list notion-list-disc notion-block-889f237866194fe8ab7da7473ab2415d\"><li>one connector for Google Drive</li></ul><ul class=\"notion-list notion-list-disc notion-block-40ceaf8923d5436f954046f7b7285b43\"><li>another connector for Slack</li></ul><ul class=\"notion-list notion-list-disc notion-block-52f1e1cde6fa49559e6e82a82a6183df\"><li>another connector for Postgres</li></ul><ul class=\"notion-list notion-list-disc notion-block-96b1c7e9d805474a8ebfb76a8e86d405\"><li>another connector for a local filesystem</li></ul><ul class=\"notion-list notion-list-disc notion-block-48e0aabb07ea416fbb9b1bfc2fd4408e\"><li>another connector for a company-specific API</li></ul><div class=\"notion-text notion-block-77c396faa6a840efbe7a77bbef717d34\">That works for the first two integrations. It gets ugly by the twentieth. Every new tool needs a custom schema, auth story, error format, logging convention, and UI affordance. Worse, every AI client has to learn those integrations again.</div><div class=\"notion-text notion-block-c03dbdb0794e41d0ae80238b5cf0727d\">MCP tries to move that mess into a standard interface. The idea is simple: let a tool provider expose capabilities through an MCP server, and let an AI application connect through an MCP client. The model does not need to know whether the underlying capability is a database, file searcher, shell wrapper, SaaS API, or internal service. It sees a structured capability boundary.</div><div class=\"notion-text notion-block-765f6bbaab2d48ed9809bf997c4883b7\">This is why people compare MCP to the Language Server Protocol. LSP did not make programming languages easier. It made editor-language integration reusable. MCP is trying to do something similar for AI-context and AI-tool integration.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-89920f3d2511497f805afd55171bb4ce\" data-id=\"89920f3d2511497f805afd55171bb4ce\"><span><div id=\"89920f3d2511497f805afd55171bb4ce\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#89920f3d2511497f805afd55171bb4ce\" title=\"What MCP Actually Defines\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">What MCP Actually Defines</span></span></h3><div class=\"notion-text notion-block-b1eee60dd9624417a4f502934988d24b\">As of June 12, 2026, the MCP documentation lists the 2025-11-25 specification as the latest stable version. The core architecture has three names that are worth keeping separate:</div><ul class=\"notion-list notion-list-disc notion-block-bf4592d5fb19465a96b6d64010270b56\"><li><b>Host</b>: the AI application, such as an IDE, chat app, or agent runtime.</li></ul><ul class=\"notion-list notion-list-disc notion-block-6aaed87ebc3d44329ba6fa0866281395\"><li><b>Client</b>: the connector inside the host that speaks MCP.</li></ul><ul class=\"notion-list notion-list-disc notion-block-689704d9421f4d08bd69ed26d46fc6db\"><li><b>Server</b>: the external process or service that exposes capabilities.</li></ul><div class=\"notion-text notion-block-cf85b9cf2b554debbf45d6a14d6aca48\">The base protocol uses JSON-RPC 2.0 messages. That matters because MCP is not magic model glue. It is request/response, notifications, lifecycle negotiation, capabilities, and schemas. The useful part is not that the messages are exotic. The useful part is that everyone can agree on what messages mean.</div><div class=\"notion-text notion-block-1c5c5182410d4762be0fcdf1fe672bd5\">The server side can expose three broad kinds of things:</div><ul class=\"notion-list notion-list-disc notion-block-0324dabaa5684e39be0a52dba7a9a2bb\"><li><b>Resources</b>: context and data, such as files, database schemas, documents, or application state.</li></ul><ul class=\"notion-list notion-list-disc notion-block-76c0189221c24730a09caf71437ba221\"><li><b>Prompts</b>: reusable prompt templates or workflows.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3b3306459a42438ca3d9488398ea4889\"><li><b>Tools</b>: callable functions the model may invoke, such as querying a database, calling an API, or running a computation.</li></ul><div class=\"notion-text notion-block-6dceb510809d4f3b893b4271c722a470\">Tools are the part people usually notice first. A tool has a name, description, and input schema. The client can ask the server for the tool list, then call one of those tools with structured arguments.</div><div class=\"notion-text notion-block-1c81d487a2a1800da199e943b624814d\">That schema boundary is important. A good tool is not just &quot;let the model do anything.&quot; It is a small contract. The contract says what the model can ask for, what the server will accept, and what the result should look like.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-1c81d487a2a1805586a2e0c0e42be0d9\" data-id=\"1c81d487a2a1805586a2e0c0e42be0d9\"><span><div id=\"1c81d487a2a1805586a2e0c0e42be0d9\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#1c81d487a2a1805586a2e0c0e42be0d9\" title=\"Where MCP Helps Most\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">Where MCP Helps Most</span></span></h3><div class=\"notion-text notion-block-1c81d487a2a18078be2cf358bd91e59a\">MCP is most useful when the number of tools or clients starts to grow.</div><div class=\"notion-text notion-block-1c81d487a2a18043b9b8e069f82c1281\">If you are building one chatbot with one internal API, MCP may be overkill. A direct function call is probably fine. But if you have many tools and many AI clients, a standard interface starts to pay for itself.</div><div class=\"notion-text notion-block-108577d599de4f67a861cf7cd1381b35\">The strongest cases are:</div><ol start=\"1\" class=\"notion-list notion-list-numbered notion-block-1c81d487a2a1808c98bee5360f90a287\" style=\"list-style-type:decimal\"><li><b>Developer tools.</b> IDEs and coding agents need access to files, search, tests, package managers, issue trackers, and deployment systems. MCP gives those capabilities a common shape.</li></ol><ol start=\"2\" class=\"notion-list notion-list-numbered notion-block-1c81d487a2a18050a821eff9d1f7f225\" style=\"list-style-type:decimal\"><li><b>Enterprise internal tools.</b> Companies already have many APIs and databases. MCP can wrap them without forcing every AI app to hand-roll integration logic.</li></ol><ol start=\"3\" class=\"notion-list notion-list-numbered notion-block-1c81d487a2a180a6af50d31d1e43b43e\" style=\"list-style-type:decimal\"><li><b>Agent platforms.</b> Agents need to discover what they can do at runtime. MCP&#x27;s tool listing and capability negotiation fit that pattern better than hardcoded function lists.</li></ol><ol start=\"4\" class=\"notion-list notion-list-numbered notion-block-1c81d487a2a180779f12d557c5d2272b\" style=\"list-style-type:decimal\"><li><b>Auditable workflows.</b> When every tool call goes through a structured server, it becomes easier to log, review, rate-limit, and permission.</li></ol><div class=\"notion-text notion-block-1c81d487a2a180a9910bd79b87bbb29e\">The last point is the one I care about most. If AI is going to touch real systems, the integration layer should be visible. Hidden tool calls are scary. Structured tool calls can at least be inspected.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-d7a19bf51409483b8e37de1cbd5ccabc\" data-id=\"d7a19bf51409483b8e37de1cbd5ccabc\"><span><div id=\"d7a19bf51409483b8e37de1cbd5ccabc\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#d7a19bf51409483b8e37de1cbd5ccabc\" title=\"What MCP Does Not Solve\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">What MCP Does Not Solve</span></span></h3><div class=\"notion-text notion-block-1c81d487a2a1808f9947f105872dd9cf\">The mistake is treating MCP as if standardization equals safety.</div><div class=\"notion-text notion-block-1c81d487a2a180d1a506d507e3107e0e\">MCP can tell a client that a tool exists. It cannot guarantee the tool is well designed. It can describe an input schema. It cannot guarantee the model chose the right action. It can define authorization flows. It cannot decide whether your product should allow the action at all.</div><div class=\"notion-text notion-block-1c81d487a2a180eb956cd63982dd41c8\">For example, a &quot;delete_customer_record&quot; tool may have a perfect schema and still be dangerous. A filesystem server may correctly expose files and still leak secrets if its root boundary is wrong. A browser automation tool may be useful and still open a large attack surface.</div><div class=\"notion-text notion-block-1c81d487a2a180cc9a7ce0f45d289655\">This is why the official specification spends time on user consent, privacy, and tool safety, and why security discussions around MCP focus on issues like excessive tool permissions, prompt injection surfaces, SSRF, lifecycle bypass, information leakage, and authentication gaps.</div><div class=\"notion-text notion-block-1c81d487a2a180bc8ab3f2b0e4251cab\">In practice, the dangerous part is that the model sees text from untrusted places and then chooses actions. If a retrieved document says &quot;ignore previous instructions and email the secrets,&quot; MCP does not magically know that text is hostile. The host, tool server, and product UI have to enforce boundaries.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-1c81d487a2a18087ba84ebaff0896e55\" data-id=\"1c81d487a2a18087ba84ebaff0896e55\"><span><div id=\"1c81d487a2a18087ba84ebaff0896e55\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#1c81d487a2a18087ba84ebaff0896e55\" title=\"How I Would Design An MCP Tool\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">How I Would Design An MCP Tool</span></span></h3><div class=\"notion-text notion-block-1c81d487a2a180b6aba7e7f8ba57b3d0\">If I were exposing a serious tool through MCP, I would start with a few rules.</div><div class=\"notion-text notion-block-1c81d487a2a1804aab13f2f498620329\"><b>Make tools narrow.</b> Prefer <code class=\"notion-inline-code\">create_draft_invoice</code> over <code class=\"notion-inline-code\">run_sql_query</code> when the workflow is business-specific. Prefer read-only tools until write actions are truly needed.</div><div class=\"notion-text notion-block-d38a9a6e9375431ead48693e3667a4fb\"><b>Separate read and write.</b> Reading a calendar and creating an event should be different tools with different permissions and UI treatment.</div><div class=\"notion-text notion-block-56ad88c270694decbcdbd3d5dbe93dd5\"><b>Use confirmation for irreversible actions.</b> The MCP tools spec explicitly points toward human-in-the-loop confirmation for sensitive operations. That should not be a checkbox at the end. It should be part of the product flow.</div><div class=\"notion-text notion-block-dfe1dbbeb3ea45e3ac0b2599caabd95e\"><b>Treat tool descriptions as untrusted.</b> A model may read tool names and descriptions as context. If those descriptions come from a third-party server, they can become prompt-injection material too.</div><div class=\"notion-text notion-block-8a35fbec07ae4cf9b6eb7f1756b41d20\"><b>Log every call.</b> Store the tool name, arguments, result, user, session, and model-visible context. If something goes wrong, you need an audit trail.</div><div class=\"notion-text notion-block-49222dd9b7884b2ab1abaa445ed85f73\"><b>Test the server like an API.</b> Schema validation is not enough. You need tests for permissions, malformed input, rate limits, empty states, and adversarial strings.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-78c82fd0a6a84c5384cb5ae7addf8905\" data-id=\"78c82fd0a6a84c5384cb5ae7addf8905\"><span><div id=\"78c82fd0a6a84c5384cb5ae7addf8905\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#78c82fd0a6a84c5384cb5ae7addf8905\" title=\"A Small Mental Model\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">A Small Mental Model</span></span></h3><div class=\"notion-text notion-block-ddf91a0374bb481abb5790d04d3d954a\">I think about MCP in three layers:</div><div class=\"notion-text notion-block-05620f10665b4e529e9711de1924b2ba\">The protocol layer is MCP&#x27;s job. The product and safety layers are still your job.</div><div class=\"notion-text notion-block-c2335e9dcfa644669dc5990921c3b8d9\">That is why I do not buy the phrase &quot;ultimate AI solution.&quot; MCP is closer to plumbing. Good plumbing matters a lot. But clean pipes do not decide what water should flow through them.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-46ce213a09ff4651933d682c5f522244\" data-id=\"46ce213a09ff4651933d682c5f522244\"><span><div id=\"46ce213a09ff4651933d682c5f522244\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#46ce213a09ff4651933d682c5f522244\" title=\"Bottom Line\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">Bottom Line</span></span></h3><div class=\"notion-text notion-block-3bcf558bf35c40bba9048f413f1787a9\">MCP is important because it standardizes a painful integration layer. It can reduce glue code, make tools more discoverable, and give AI applications a shared way to talk to external systems.</div><div class=\"notion-text notion-block-35acf984403f494eb58f99a09af65a2c\">But MCP should be treated as infrastructure, not intelligence. It does not make the model reason better. It does not make unsafe tools safe. It does not remove the need for permissions, confirmations, logging, and adversarial testing.</div><div class=\"notion-text notion-block-c03357ad728149ad8af4df523a1a88aa\">My practical answer is: use MCP when you have multiple tools, multiple clients, or a need for a durable integration boundary. Do not use it as an excuse to give an agent broad powers and call the protocol &quot;safety.&quot;</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-1c81d487a2a180f2b816c01f634e2b27\" data-id=\"1c81d487a2a180f2b816c01f634e2b27\"><span><div id=\"1c81d487a2a180f2b816c01f634e2b27\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#1c81d487a2a180f2b816c01f634e2b27\" title=\"References\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">References</span></span></h3><ul class=\"notion-list notion-list-disc notion-block-1c81d487a2a180e3a510c81b0c5cd274\"><li><a class=\"notion-link\" href=\"https://modelcontextprotocol.io/specification/2025-11-25\" target=\"_blank\" rel=\"noopener noreferrer\">Model Context Protocol specification</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-1c81d487a2a18029b472c85944e3a2f3\"><li><a class=\"notion-link\" href=\"https://modelcontextprotocol.io/specification/2025-11-25/basic\" target=\"_blank\" rel=\"noopener noreferrer\">MCP base protocol overview</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-6ef13a6c1ddb4162bda87f0f658dca91\"><li><a class=\"notion-link\" href=\"https://modelcontextprotocol.io/specification/2025-11-25/server/tools\" target=\"_blank\" rel=\"noopener noreferrer\">MCP tools specification</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-b304a9fd96d14684bc87b99e4320c87f\"><li><a class=\"notion-link\" href=\"https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization\" target=\"_blank\" rel=\"noopener noreferrer\">MCP authorization specification</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-b1368dcbdb444489a92223b2a516df54\"><li><a class=\"notion-link\" href=\"https://www.anthropic.com/news/model-context-protocol\" target=\"_blank\" rel=\"noopener noreferrer\">Introducing the Model Context Protocol - Anthropic</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-14b6bd788f90447d8381b5d26096ffa8\"><li><a class=\"notion-link\" href=\"https://www.ietf.org/archive/id/draft-mohiuddin-mcp-security-considerations-00.html\" target=\"_blank\" rel=\"noopener noreferrer\">Security Considerations for MCP Implementations - IETF Internet-Draft</a></li></ul></main></div>",
            "url": "https://leoqin.com/en/article/MCP",
            "title": "MCP Is Infrastructure, Not the Ultimate AI Solution",
            "summary": "MCP standardizes how AI apps connect to tools, resources, prompts, and external systems. It is useful infrastructure for reducing integration glue, but it does not replace permissions, confirmations, logging, threat modeling, or tool-safety design.",
            "date_modified": "2025-04-01T00:00:00.000Z"
        },
        {
            "id": "https://leoqin.com/en/article/Nextjs-or-Remix",
            "content_html": "<div id=\"notion-article\" class=\"mx-auto overflow-hidden \"><main class=\"notion light-mode notion-page notion-block-1cb1d487a2a180fb8d62d02509553175\"><div class=\"notion-viewport\"></div><div class=\"notion-collection-page-properties\"></div><div class=\"notion-callout notion-gray_background_co notion-block-2d8772b44a264675916aed8c3f5d4219\"><div class=\"notion-page-icon-inline notion-page-icon-span\"><span class=\"notion-page-icon\" role=\"img\" aria-label=\"💭\">💭</span></div><div class=\"notion-callout-text\"><div class=\"notion-text notion-block-cf46ff67d99649e8aed0de5b3c48b966\">In early 2024, OpenAI moved ChatGPT&#x27;s frontend from Next.js to Remix, and the frontend world talked about it for a while. Next.js is the default React framework for many teams, backed heavily by Vercel. When a company at OpenAI&#x27;s scale switches, the natural question is: is Remix simply better, or was it better for that specific situation?</div></div></div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-1cb1d487a2a181488971d45993ffcab2\" data-id=\"1cb1d487a2a181488971d45993ffcab2\"><span><div id=\"1cb1d487a2a181488971d45993ffcab2\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#1cb1d487a2a181488971d45993ffcab2\" title=\"Where The Two Frameworks Come From\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">Where The Two Frameworks Come From</span></span></h3><div class=\"notion-text notion-block-1cb1d487a2a18116aa9aeaf8098a7cd2\">Next.js was created by Vercel, then called Zeit, in 2016. It was one of the earliest frameworks that made React server-side rendering feel usable out of the box. Over time, it added file-based routing, automatic code splitting, SSR, SSG, ISR, and many other features. For many React projects, it became the default answer.</div><div class=\"notion-text notion-block-1cb1d487a2a181a08778ea795a09c788\">Remix arrived later. It was released in 2021 by the React Router team with a different philosophy. It leans harder on web standards, avoids extra client-side machinery when possible, and lets the browser and server handle more of the work directly. Shopify acquired it later, which brought it into more production conversations.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-1cb1d487a2a18143b4d0c1d8d272ab9e\" data-id=\"1cb1d487a2a18143b4d0c1d8d272ab9e\"><span><div id=\"1cb1d487a2a18143b4d0c1d8d272ab9e\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#1cb1d487a2a18143b4d0c1d8d272ab9e\" title=\"How The Mental Models Differ\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">How The Mental Models Differ</span></span></h3><div class=\"notion-text notion-block-1cb1d487a2a1815ebdeee3bdf5237e31\">Take a common case: loading data on the server. In Next.js, the older pages-router style looks like this:</div><div class=\"notion-text notion-block-1cb1d487a2a18101b4adf2c6758c0fbf\">In Remix, the same idea looks like this:</div><div class=\"notion-text notion-block-1cb1d487a2a181f995a8fa36d57f65f4\">The code looks similar, but the mental model is different. Next.js treats the fetched data like page props. Remix treats data loading as part of the route itself. Each route has its own <code class=\"notion-inline-code\">loader</code> and <code class=\"notion-inline-code\">action</code>, and UI, forms, and data flow are tied to the route hierarchy.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-1cb1d487a2a181288fdaee58b7fe752d\" data-id=\"1cb1d487a2a181288fdaee58b7fe752d\"><span><div id=\"1cb1d487a2a181288fdaee58b7fe752d\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#1cb1d487a2a181288fdaee58b7fe752d\" title=\"Why OpenAI Moved\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">Why OpenAI Moved</span></span></h3><div class=\"notion-text notion-block-1cb1d487a2a181b48e70e28eb9002ed8\">According to the Remix post about OpenAI&#x27;s migration, server-side rendering time dropped from around 300ms to around 180ms. The number is interesting, but the reason matters more. Remix&#x27;s &quot;web standards plus server-first&quot; approach can be more stable under heavy traffic. Smaller client JavaScript means faster initial load. Native form behavior also means less dependence on expensive client hydration.</div><div class=\"notion-text notion-block-1cb1d487a2a181049bedc1bbcbe121ce\">ChatGPT is a high-traffic, highly interactive, state-heavy application. Every millisecond gets multiplied by an enormous number of users. In that setting, the &quot;full-featured&quot; nature of Next.js can become weight, while Remix&#x27;s lighter approach can become an advantage.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-1cb1d487a2a181d993afe4eccc0c0dd1\" data-id=\"1cb1d487a2a181d993afe4eccc0c0dd1\"><span><div id=\"1cb1d487a2a181d993afe4eccc0c0dd1\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#1cb1d487a2a181d993afe4eccc0c0dd1\" title=\"How I Would Choose\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">How I Would Choose</span></span></h3><div class=\"notion-text notion-block-1cb1d487a2a181619a3ec1473b89030b\">For a personal project, blog, or marketing site, I would still usually pick Next.js without much hesitation. The ecosystem is mature, deployment is easy, and Vercel support is excellent.</div><div class=\"notion-text notion-block-386e703910fe44448fce38583e3a490b\">For a large server-driven product, especially one where the team is willing to think in web-standard terms, Remix deserves a serious look. OpenAI&#x27;s migration at least proves one thing: in the right environment, a framework that does less can save real money.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-1cb1d487a2a1816696aacc94b663ba7a\" data-id=\"1cb1d487a2a1816696aacc94b663ba7a\"><span><div id=\"1cb1d487a2a1816696aacc94b663ba7a\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#1cb1d487a2a1816696aacc94b663ba7a\" title=\"References\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">References</span></span></h3><ul class=\"notion-list notion-list-disc notion-block-1cb1d487a2a181ffbf7ce8eba977b862\"><li><a class=\"notion-link\" href=\"https://remix.run/blog/remixing-openai\" target=\"_blank\" rel=\"noopener noreferrer\">Remixing OpenAI</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-1cb1d487a2a181d9ac57fe711c505ca5\"><li><a class=\"notion-link\" href=\"https://nextjs.org/docs/app/building-your-application/rendering/server-side-rendering\" target=\"_blank\" rel=\"noopener noreferrer\">Next.js server-side rendering documentation</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-1cb1d487a2a1812394edc622276e0df2\"><li><a class=\"notion-link\" href=\"https://remix.run/docs/en/main/guides/rendering\" target=\"_blank\" rel=\"noopener noreferrer\">Remix rendering guide</a></li></ul></main></div>",
            "url": "https://leoqin.com/en/article/Nextjs-or-Remix",
            "title": "Next.js or Remix: What OpenAI’s Migration Suggests",
            "summary": "Using OpenAI’s move from Next.js to Remix as the case study, this post compares the two React frameworks around data loading, server rendering, hydration costs, and when each one is a better fit.",
            "date_modified": "2024-12-12T00:00:00.000Z"
        },
        {
            "id": "https://leoqin.com/en/article/Statsify-Finance-Tech-Analysis",
            "content_html": "<div id=\"notion-article\" class=\"mx-auto overflow-hidden \"><main class=\"notion light-mode notion-page notion-block-3551d487a2a1811d8d77da56f87e3a03\"><div class=\"notion-viewport\"></div><div class=\"notion-collection-page-properties\"></div><div class=\"notion-callout notion-gray_background_co notion-block-e63800ae4e0a4fe0aded53794962f972\"><div class=\"notion-page-icon-inline notion-page-icon-span\"><span class=\"notion-page-icon\" role=\"img\" aria-label=\"💹\">💹</span></div><div class=\"notion-callout-text\"><div class=\"notion-text notion-block-2f172e107c3745b9ba47449c8b755470\">Statsify Finance is a financial analysis tool website I built for both beginners and more advanced users. This post is a short technical walkthrough: why I built it, what the site contains, and what sits underneath it.</div></div></div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-8507b22212b14c61ac9de21d6d7f1856\" data-id=\"8507b22212b14c61ac9de21d6d7f1856\"><span><div id=\"8507b22212b14c61ac9de21d6d7f1856\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#8507b22212b14c61ac9de21d6d7f1856\" title=\"Why I Built It\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">Why I Built It</span></span></h3><div class=\"notion-text notion-block-d5c6a5b3676b4c8cb9bc6264bd62df13\">I look at stocks and markets myself, and many finance tools feel either too old or too heavy. Some interfaces look like they stopped evolving ten years ago. Others open with pop-ups pushing brokerage accounts. I wanted a cleaner site that connects basic finance concepts with practical tools, something I would actually enjoy using and could also share with others.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-e828a048c4544960ab65b1ade4bee551\" data-id=\"e828a048c4544960ab65b1ade4bee551\"><span><div id=\"e828a048c4544960ab65b1ade4bee551\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#e828a048c4544960ab65b1ade4bee551\" title=\"What The Site Contains\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">What The Site Contains</span></span></h3><div class=\"notion-text notion-block-aa0363be421b4f34bbd100df5cbc7447\">Statsify Finance is roughly split into three areas.</div><div class=\"notion-text notion-block-e8c513583cb64f028468a7bb77983d5e\"><b>Analysis pages (</b><code class=\"notion-inline-code\"><b>/analysis</b></code><b>)</b></div><ul class=\"notion-list notion-list-disc notion-block-858a0cbb1db0452fab9326e332415b28\"><li><b>Qualitative analysis (</b><code class=\"notion-inline-code\"><b>/analysis/qualitative</b></code><b>).</b> Explanations of qualitative economics and finance concepts, each with real examples.</li></ul><ul class=\"notion-list notion-list-disc notion-block-6041bf0877994955b287c7edf6fb6c7a\"><li><b>Quantitative analysis (</b><code class=\"notion-inline-code\"><b>/analysis/quantitative</b></code><b>).</b> Quantitative concepts with formulas and examples.</li></ul><div class=\"notion-text notion-block-71e519d7aa034f338faed81de4c3ef1b\"><b>Strategy pages (</b><code class=\"notion-inline-code\"><b>/strategy</b></code><b>)</b></div><ul class=\"notion-list notion-list-disc notion-block-9b8bcb445286451fbbf160ea4d79dbf6\"><li><b>Trading strategies (</b><code class=\"notion-inline-code\"><b>/strategy/trade</b></code><b>).</b> Common strategies with usage examples.</li></ul><ul class=\"notion-list notion-list-disc notion-block-36df43b4f70e42bda017b0c35e3d34cb\"><li><b>Investor profiles (</b><code class=\"notion-inline-code\"><b>/strategy/investor</b></code><b>).</b> Different investor types, useful for understanding your own style.</li></ul><ul class=\"notion-list notion-list-disc notion-block-2085f9f352094592a1fefe5c1a63a19c\"><li><b>Risk management (</b><code class=\"notion-inline-code\"><b>/strategy/risk-manage</b></code><b>).</b> Risk-control methods, grouped by industry and paired with visualizations.</li></ul><div class=\"notion-text notion-block-8ddbc5b76ccd43658d6c8c2d5a4c7e7d\"><b>Other features</b></div><ul class=\"notion-list notion-list-disc notion-block-dd22bc97a0bb4c08805b318bb17223a6\"><li><b>Articles (</b><code class=\"notion-inline-code\"><b>/articles</b></code><b>).</b> Notes on recent market events and industry trends.</li></ul><ul class=\"notion-list notion-list-disc notion-block-53e165a966a347d4955a0fc8f75bebc7\"><li><b>Search (</b><code class=\"notion-inline-code\"><b>/search</b></code><b>).</b> Search across articles and concepts.</li></ul><ul class=\"notion-list notion-list-disc notion-block-ba28469faf5246c083424f9c78664565\"><li><b>Finance terms (</b><code class=\"notion-inline-code\"><b>/finance-terms</b></code><b>).</b> A cleaner glossary of finance terminology.</li></ul><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a1812fa08aecdbf4760a65\" data-id=\"3551d487a2a1812fa08aecdbf4760a65\"><span><div id=\"3551d487a2a1812fa08aecdbf4760a65\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a1812fa08aecdbf4760a65\" title=\"The Tech Stack\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">The Tech Stack</span></span></h3><div class=\"notion-text notion-block-3551d487a2a181a3b8dee1d76cfe5db5\">The site is built on Next.js and Tailwind. I use Ant Design for UI components and ECharts for charts. I did not use an off-the-shelf CMS. Instead, I built a small one with Next.js API routes and MongoDB, which keeps the data model simple and controllable. Authentication uses NextAuth, and the editor is <code class=\"notion-inline-code\">wangeditor</code>. The project is written in TypeScript.</div><div class=\"notion-text notion-block-3551d487a2a1813789d2cf99841be309\">Main stack:</div><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181b6856ada28b664bf93\"><li><b>Next.js.</b> React framework with SSR, routing, and API routes.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181429fb5f5a2cecd6ec4\"><li><b>Tailwind CSS.</b> Styling.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a18125ad84f57846cf3746\"><li><b>Ant Design.</b> UI component library.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181db8e51e327ce497bb3\"><li><b>ECharts.</b> Data visualization.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a18111beb0daa221a559a1\"><li><b>MongoDB.</b> Database.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181a28282d640daeb9b55\"><li><b>NextAuth.</b> Authentication.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a18171b03ce52f59ce4c04\"><li><b>wangeditor.</b> Rich text editing.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181729497d7087784eccf\"><li><b>TypeScript.</b> Types.</li></ul><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a181e8b93fc5f13a75bb0d\" data-id=\"3551d487a2a181e8b93fc5f13a75bb0d\"><span><div id=\"3551d487a2a181e8b93fc5f13a75bb0d\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a181e8b93fc5f13a75bb0d\" title=\"Project Structure\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">Project Structure</span></span></h3><div class=\"notion-text notion-block-3551d487a2a1814898f2ed61dae0d627\">The code is organized in a fairly simple way:</div><ul class=\"notion-list notion-list-disc notion-block-ac460c9df46547ebb1511df9b3f3138f\"><li><code class=\"notion-inline-code\">app/</code>. Main Next.js application directory, including page routes, API routes, and components.</li></ul><ul class=\"notion-list notion-list-disc notion-block-8b409d807eae48ae90479f7d1c7c634c\"><li><code class=\"notion-inline-code\">components/</code>. Shared components, split between UI components and business components.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a18195b69bcc5faf6b95f3\"><li><code class=\"notion-inline-code\">models/</code>. MongoDB data models.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181eb9708e84b7668a7f5\"><li><code class=\"notion-inline-code\">libs/</code>. Database connection and utility functions.</li></ul><ul class=\"notion-list notion-list-disc notion-block-f0ad10f49b8a45dc8b66faebf2d97312\"><li><code class=\"notion-inline-code\">src/</code>. Styles and static configuration data.</li></ul><div class=\"notion-text notion-block-3551d487a2a18171b088e67e14f8c0a5\">The project is not huge, but it has the basic shape of a real product. I plan to keep adding content and tools to it over time.</div></main></div>",
            "url": "https://leoqin.com/en/article/Statsify-Finance-Tech-Analysis",
            "title": "Statsify Finance: Technical Breakdown",
            "summary": "A product and architecture walkthrough for Statsify Finance, covering its analysis, strategy, article, search, and finance-term pages, plus the Next.js, Tailwind, Ant Design, ECharts, MongoDB, and TypeScript stack behind it.",
            "date_modified": "2024-12-08T00:00:00.000Z"
        },
        {
            "id": "https://leoqin.com/en/article/what-is-ai",
            "content_html": "<div id=\"notion-article\" class=\"mx-auto overflow-hidden \"><main class=\"notion light-mode notion-page notion-block-3551d487a2a181cd9ebfc478e5d6a248\"><div class=\"notion-viewport\"></div><div class=\"notion-collection-page-properties\"></div><div class=\"notion-callout notion-gray_background_co notion-block-37aaded941bf4323a9c26b794913889b\"><div class=\"notion-page-icon-inline notion-page-icon-span\"><span class=\"notion-page-icon\" role=\"img\" aria-label=\"💭\">💭</span></div><div class=\"notion-callout-text\"><div class=\"notion-text notion-block-2972f6ca21324956b87f2005ab4e23aa\">The word &quot;AI&quot; has been used so much lately that it appears everywhere, from subway ads to appliance manuals. This post takes one step back: what is AI, how did it get here, and how much has it already changed ordinary life?</div></div></div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a181bba395d39e59fba759\" data-id=\"3551d487a2a181bba395d39e59fba759\"><span><div id=\"3551d487a2a181bba395d39e59fba759\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a181bba395d39e59fba759\" title=\"What AI Is\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">What AI Is</span></span></h3><div class=\"notion-text notion-block-3551d487a2a1810b8576efcaf02512bb\">If we strip away the buzzword, AI is a system that can perceive, understand, decide, and learn. Humans do these things naturally. Machines need code, data, and compute to approximate them.</div><div class=\"notion-text notion-block-3551d487a2a1815e959ce9320d7910ad\">For example, when you see a cat, you immediately know it is a cat. That is perception. You know it may meow or rub against your leg. That is understanding. You decide whether to pet it. That is decision-making. The next time you see a different-looking animal that is still a cat, you can recognize it. That is learning. Turning these steps into machine behavior is the core problem AI tries to solve.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a18144b3bbffbadeae1fc7\" data-id=\"3551d487a2a18144b3bbffbadeae1fc7\"><span><div id=\"3551d487a2a18144b3bbffbadeae1fc7\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a18144b3bbffbadeae1fc7\" title=\"How It Got Here\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">How It Got Here</span></span></h3><div class=\"notion-text notion-block-3551d487a2a181d2b904f115f6c0a33b\">AI is much older than ChatGPT. I like to tell the story through four names.</div><ul class=\"notion-list notion-list-disc notion-block-6fd568ef502e4259b33c5a7dacb7f8cc\"><li>In 1950, Alan Turing asked whether machines could think and proposed the Turing Test. This is one of the starting points of AI.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a1816da010f0b76586ef54\"><li>In 1956, John McCarthy used the term &quot;Artificial Intelligence&quot; at the Dartmouth workshop.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181bc808dcf545bb06649\"><li>In 1969, Marvin Minsky helped build MIT&#x27;s AI Lab. His book <em>Perceptrons</em> also helped push neural networks into a long period of doubt.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181319e20cc30f80c858b\"><li>In 1986, Geoffrey Hinton helped make backpropagation practical again. Decades later, he received the Turing Award.</li></ul><div class=\"notion-text notion-block-3551d487a2a18123998ed8e9df2caa6f\">There were also two &quot;AI winters&quot; in the middle, when funding dropped and companies cut projects. Each winter happened partly because researchers promised too much and delivered too little. AI really took off again when deep learning crushed older methods on ImageNet in 2012. After that came AlphaGo, GPT, Stable Diffusion, and the acceleration everyone is now watching.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a181d1a316dbdb84dcdd62\" data-id=\"3551d487a2a181d1a316dbdb84dcdd62\"><span><div id=\"3551d487a2a181d1a316dbdb84dcdd62\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a181d1a316dbdb84dcdd62\" title=\"What It Has Changed\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">What It Has Changed</span></span></h3><div class=\"notion-text notion-block-3551d487a2a181d78ff9f16221833096\">Some changes are visible. Your phone camera uses AI for face detection, autofocus, and night-mode image fusion. Food delivery and shopping apps use recommendation systems. Siri, Alexa, and similar assistants combine speech recognition with language understanding.</div><div class=\"notion-text notion-block-3551d487a2a1810fbc6aeed4395359f1\">Other changes are hidden. The order of short videos in your feed is chosen by AI. A suspicious credit-card transaction may be blocked by a fraud model. A spam filter decides which emails never reach your inbox.</div><div class=\"notion-text notion-block-3551d487a2a1815b8a60f8426856bbf4\">The biggest recent change is generative AI. Older AI systems mostly recognized, ranked, or recommended things. Now AI can produce content directly: code, articles, images, video, and voice. This is the first time many of us have seen machines generate media at scale. The cost is that the boundary between real and synthetic content becomes harder to see.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a181fc8713fd79b4bdd243\" data-id=\"3551d487a2a181fc8713fd79b4bdd243\"><span><div id=\"3551d487a2a181fc8713fd79b4bdd243\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a181fc8713fd79b4bdd243\" title=\"My Current View\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">My Current View</span></span></h3><div class=\"notion-text notion-block-3551d487a2a181dc852dc07ff95e2be7\">In the short term, AI is overestimated. In the long term, it is underestimated. That pattern is common with new technology, and AI fits it well.</div><div class=\"notion-text notion-block-3551d487a2a181d58b1ff897d8449a63\">In the short term, many products wrapped in &quot;AI-powered&quot; language are not that useful, and there is real valuation hype. But over ten or twenty years, AI will probably become more like electricity or the internet: less of a magic object, more of a background infrastructure we stop noticing. When that happens, today&#x27;s arguments may feel strange in hindsight.</div><div class=\"notion-text notion-block-04a5719c8b2c4a1fab615e73719ba5f1\">For ordinary people, the best response is not panic and not worship. It is use. Use AI enough to develop a feel for what it can and cannot do. That practical sense is more useful than any headline.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a181b6b095c65f68cd597c\" data-id=\"3551d487a2a181b6b095c65f68cd597c\"><span><div id=\"3551d487a2a181b6b095c65f68cd597c\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a181b6b095c65f68cd597c\" title=\"References\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">References</span></span></h3><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181b6b0aff96a3897ca07\"><li><a class=\"notion-link\" href=\"https://aiindex.stanford.edu/\" target=\"_blank\" rel=\"noopener noreferrer\">Stanford HAI AI Index Report</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a1814fa954fa9bacac1aff\"><li><a class=\"notion-link\" href=\"https://en.wikipedia.org/wiki/History_of_artificial_intelligence\" target=\"_blank\" rel=\"noopener noreferrer\">History of artificial intelligence - Wikipedia</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a18197bc88d4b9a47eb37a\"><li><a class=\"notion-link\" href=\"https://www.coursera.org/learn/neural-networks-deep-learning\" target=\"_blank\" rel=\"noopener noreferrer\">Geoffrey Hinton&#x27;s Neural Networks and Deep Learning course</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-d91d874a5edb40b6a203da5d5e93153a\"><li><a class=\"notion-link\" href=\"https://www.mckinsey.com/capabilities/quantumblack/our-insights/the-state-of-ai\" target=\"_blank\" rel=\"noopener noreferrer\">McKinsey: The State of AI in 2024</a></li></ul></main></div>",
            "url": "https://leoqin.com/en/article/what-is-ai",
            "title": "What Is AI, and How Is It Changing Everyday Life?",
            "summary": "A grounded overview of AI as systems that perceive, understand, decide, and learn, with a short history from Turing and Dartmouth to deep learning and generative AI, plus a practical view of how it affects daily life.",
            "date_modified": "2024-12-09T00:00:00.000Z"
        },
        {
            "id": "https://leoqin.com/en/article/AI-Improve-Efficiency",
            "content_html": "<div id=\"notion-article\" class=\"mx-auto overflow-hidden \"><main class=\"notion light-mode notion-page notion-block-3551d487a2a18133ac49efe6c386f627\"><div class=\"notion-viewport\"></div><div class=\"notion-collection-page-properties\"></div><div class=\"notion-callout notion-gray_background_co notion-block-b6950fc2e00a465287bd7ce834bb1e62\"><div class=\"notion-page-icon-inline notion-page-icon-span\"><span class=\"notion-page-icon\" role=\"img\" aria-label=\"💭\">💭</span></div><div class=\"notion-callout-text\"><div class=\"notion-text notion-block-d6e2f194b17649c19b4c9c3b1e9b79dc\">Over the past year I have used AI coding assistants more and more: GitHub Copilot, Cursor, Claude Code, and a few others. They do improve productivity, but not in the magical way marketing posts describe. This is a more practical note about where AI actually helps, where I do not trust it, and how I split work with it now.</div></div></div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-0e0e42158dec41b19e6fe9c97b4b292d\" data-id=\"0e0e42158dec41b19e6fe9c97b4b292d\"><span><div id=\"0e0e42158dec41b19e6fe9c97b4b292d\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#0e0e42158dec41b19e6fe9c97b4b292d\" title=\"What AI Is Really Good At\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">What AI Is Really Good At</span></span></h3><div class=\"notion-text notion-block-d3e0338e344249329ef19e84fd3f694e\">If I had to summarize the value in one phrase, it would be &quot;fast lookup.&quot; Humans are slow and annoyed by this kind of work; AI is fast enough to make it feel almost free.</div><div class=\"notion-text notion-block-3551d487a2a181158449f531ddd040f4\">For example:</div><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a18197b77acb78fcc3b908\"><li>You vaguely remember an API but not the exact name. Asking AI is faster than digging through docs.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181568e9ae6e93a93445a\"><li>You do not understand a piece of unfamiliar code. A quick explanation can save half an hour.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181eba614e0a85c2d51a5\"><li>You need boilerplate: CRUD endpoints, form validation, simple unit tests. Let AI draft it, then edit.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181e79a27c703bab13b37\"><li>You have a confusing error message. If you give the error and the code to AI, it can often point to the problem directly.</li></ul><div class=\"notion-text notion-block-3551d487a2a181a8b2a7f67fbe4793a3\">The IT Revolution study mentions a 26% productivity improvement from AI coding assistants. In simple tasks, my own feeling is that the speedup can be more than 50%. But as soon as the task becomes complex, the speedup drops close to zero, and sometimes becomes negative.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-7b3b2a3bad0f4464bd9b091b32641923\" data-id=\"7b3b2a3bad0f4464bd9b091b32641923\"><span><div id=\"7b3b2a3bad0f4464bd9b091b32641923\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#7b3b2a3bad0f4464bd9b091b32641923\" title=\"What AI Is Bad At\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">What AI Is Bad At</span></span></h3><div class=\"notion-text notion-block-3551d487a2a181468662c2cc64d9c3d4\">The short version: it struggles when the task requires real judgment.</div><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a18177a042fa0bfd3d9a81\"><li><b>Business logic design.</b> AI does not understand the history and intuition behind your product. It can produce pretty code that is wrong for your actual business rules.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181578d79daaa6151805b\"><li><b>Cross-file consistency.</b> AI only sees the current context window. It may miss existing utilities and create a duplicate abstraction.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181edbfb2e9b7e0328714\"><li><b>Architecture decisions.</b> Whether to split a service, add Redis, or change frameworks is not a question you can safely outsource to a generic answer.</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a1817e950ac25226669576\"><li><b>Boundary conditions.</b> AI-generated code often forgets null checks, empty arrays, concurrent calls, and permission edge cases.</li></ul><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-fe0c02c1e892464da4b6ffd4458d1d01\" data-id=\"fe0c02c1e892464da4b6ffd4458d1d01\"><span><div id=\"fe0c02c1e892464da4b6ffd4458d1d01\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#fe0c02c1e892464da4b6ffd4458d1d01\" title=\"My Current Workflow\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">My Current Workflow</span></span></h3><div class=\"notion-text notion-block-3551d487a2a1814cb055cafecb98ff9f\">I have settled into a simple division of labor.</div><ol start=\"1\" class=\"notion-list notion-list-numbered notion-block-3551d487a2a18132a53bdecf7deb1b4c\" style=\"list-style-type:decimal\"><li><b>For complex tasks, I think first and let AI draft later.</b> I decide what needs to be built, the inputs and outputs, the edge cases, and what existing code should be reused. Then I ask AI for a first pass.</li></ol><ol start=\"2\" class=\"notion-list notion-list-numbered notion-block-3551d487a2a1811d803acc1c451c31b3\" style=\"list-style-type:decimal\"><li><b>For repetitive tasks, AI drafts and I normalize.</b> If I need ten similar CRUD endpoints, I let AI generate the skeleton and then adjust them consistently.</li></ol><ol start=\"3\" class=\"notion-list notion-list-numbered notion-block-3551d487a2a181069d67fe29261a0adc\" style=\"list-style-type:decimal\"><li><b>For unfamiliar areas, AI accelerates the first hour.</b> If I am learning a new library, an AI-generated minimal example can be faster than reading the whole doc site. But for production code, I still check official docs.</li></ol><ol start=\"4\" class=\"notion-list notion-list-numbered notion-block-cb4d00c314e74b3198f5044b86e617f6\" style=\"list-style-type:decimal\"><li><b>For critical code, AI does not write the final version.</b> Money, permissions, migrations, and security-sensitive logic are my job. AI can review them, but I do not let it own them.</li></ol><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a181c7ab8decb59db6543b\" data-id=\"3551d487a2a181c7ab8decb59db6543b\"><span><div id=\"3551d487a2a181c7ab8decb59db6543b\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a181c7ab8decb59db6543b\" title=\"A Real Counterexample\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">A Real Counterexample</span></span></h3><div class=\"notion-text notion-block-3551d487a2a1815a837de0bd2dbfc59d\">Recently I asked an AI assistant to add debounce logic to a React component. It produced this:</div><div class=\"notion-text notion-block-3551d487a2a1812a8c4de659d02b0308\">At first glance it looks fine. But there are two bugs. First, the empty dependency array means the debounced function is never cleaned up, so timers can leak after unmount. Second, <code class=\"notion-inline-code\">fetchResults</code> comes from props but gets captured from the first render, so if the parent passes a new version later, the debounced callback still uses the old one.</div><div class=\"notion-text notion-block-1b3feae3ae4b46dd846a740e9132f916\">A better solution should clean up the debounce with <code class=\"notion-inline-code\">useEffect</code>, or use a tested <code class=\"notion-inline-code\">useDebounce</code> hook. The scary part is that the AI version looks correct if you scan it quickly. That is why I say AI is weak at complex logic: its wrong code often looks plausible until the bug shows up three days later.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-737b0ec056d343f294c8daef2e0aa716\" data-id=\"737b0ec056d343f294c8daef2e0aa716\"><span><div id=\"737b0ec056d343f294c8daef2e0aa716\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#737b0ec056d343f294c8daef2e0aa716\" title=\"Bottom Line\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">Bottom Line</span></span></h3><div class=\"notion-text notion-block-511a037963ed412385e843de8f46436d\">AI coding assistants are not magic. They are good axes: very useful for chopping wood, terrible for designing the house. Once you understand their boundaries and give them the right kind of work, the productivity gain becomes real.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a181809d79e2a82caed98b\" data-id=\"3551d487a2a181809d79e2a82caed98b\"><span><div id=\"3551d487a2a181809d79e2a82caed98b\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a181809d79e2a82caed98b\" title=\"References\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">References</span></span></h3><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a18111a786f35000d5b5d6\"><li><a class=\"notion-link\" href=\"https://itrevolution.com/articles/new-research-reveals-ai-coding-assistants-boost-developer-productivity-by-26-what-it-leaders-need-to-know/\" target=\"_blank\" rel=\"noopener noreferrer\">IT Revolution: AI Coding Assistants Boost Developer Productivity by 26%</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a1814d9149eb313e6d5d0c\"><li><a class=\"notion-link\" href=\"https://github.blog/2023-06-20-developer-productivity-report/\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Copilot Research Report 2023</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-cafc2a5f2c00490aaee4dbb9140d911f\"><li><a class=\"notion-link\" href=\"https://www.mckinsey.com/capabilities/quantumblack/our-insights/the-state-of-ai-in-2023-generative-ais-breakout-year\" target=\"_blank\" rel=\"noopener noreferrer\">McKinsey: The State of AI in 2023</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-e1768ef8500b4e3fa92a55306bbde2e5\"><li><a class=\"notion-link\" href=\"https://www.gartner.com/en/articles/gartner-top-10-strategic-technology-trends-for-2024\" target=\"_blank\" rel=\"noopener noreferrer\">Gartner Top Strategic Technology Trends for 2024</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-db22397775d14accaa34f75a2c94d3da\"><li><a class=\"notion-link\" href=\"https://survey.stackoverflow.co/2023/\" target=\"_blank\" rel=\"noopener noreferrer\">Stack Overflow Developer Survey 2023</a></li></ul></main></div>",
            "url": "https://leoqin.com/en/article/AI-Improve-Efficiency",
            "title": "How AI Coding Assistants Actually Improve Developer Efficiency",
            "summary": "Based on hands-on use of Copilot, Cursor, and Claude Code, this post separates where AI assistants help most, such as search, boilerplate, and error diagnosis, from where they still struggle, such as architecture and edge cases.",
            "date_modified": "2024-12-09T00:00:00.000Z"
        },
        {
            "id": "https://leoqin.com/en/article/avoid-excessive-useeffect",
            "content_html": "<div id=\"notion-article\" class=\"mx-auto overflow-hidden \"><main class=\"notion light-mode notion-page notion-block-3551d487a2a1819f88b7fc45d3c269a8\"><div class=\"notion-viewport\"></div><div class=\"notion-collection-page-properties\"></div><div class=\"notion-callout notion-gray_background_co notion-block-343d3eba7c62477fbf588178f8643445\"><div class=\"notion-page-icon-inline notion-page-icon-span\"><span class=\"notion-page-icon\" role=\"img\" aria-label=\"💭\">💭</span></div><div class=\"notion-callout-text\"><div class=\"notion-text notion-block-84145cb040b1475893e2b6c8ee6c88c2\"><code class=\"notion-inline-code\">useEffect</code> is one of those React hooks that looks convenient until it quietly turns a component into something nobody wants to touch. I have written enough projects that were beaten up by messy effects to become a little cautious. This post is about when I use <code class=\"notion-inline-code\">useEffect</code>, when I try not to use it, and how I clean things up when an effect is unavoidable.</div></div></div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a181d294f9dba0156f7fa4\" data-id=\"3551d487a2a181d294f9dba0156f7fa4\"><span><div id=\"3551d487a2a181d294f9dba0156f7fa4\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a181d294f9dba0156f7fa4\" title=\"What A Side Effect Is\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">What A Side Effect Is</span></span></h3><div class=\"notion-text notion-block-3551d487a2a181cfa6e9fddacd39167d\">In React, a side effect is anything that happens outside the pure rendering process. For example:</div><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a18174b2a2e5a2e4a56c7d\"><li>Calling an API</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a18156a763c6611c225b7a\"><li>Mutating the DOM directly</li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a1816db421c9db20d875da\"><li>Subscribing to an external data source</li></ul><ul class=\"notion-list notion-list-disc notion-block-5d8263ec8afb40379e0fba60888de21e\"><li>Setting a timer or event listener</li></ul><div class=\"notion-text notion-block-3551d487a2a181aab385dce96b24606b\">These things are not rendering itself, but they can affect what the user eventually sees. React does not automatically know when they happen, so <code class=\"notion-inline-code\">useEffect</code> gives you an escape hatch.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a1816c85bde21dd6430077\" data-id=\"3551d487a2a1816c85bde21dd6430077\"><span><div id=\"3551d487a2a1816c85bde21dd6430077\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a1816c85bde21dd6430077\" title=\"What useEffect Looks Like\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">What useEffect Looks Like</span></span></h3><div class=\"notion-text notion-block-3551d487a2a181648a87ca7e05241955\">Declarative setup, cleanup functions, dependency tracking - it all looks nice. The trouble starts when one component has five or six effects, each watching a different dependency.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-c9c31cacdb6c4f6fa980d2d57608941f\" data-id=\"c9c31cacdb6c4f6fa980d2d57608941f\"><span><div id=\"c9c31cacdb6c4f6fa980d2d57608941f\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#c9c31cacdb6c4f6fa980d2d57608941f\" title=\"The Version That Hurts\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">The Version That Hurts</span></span></h3><div class=\"notion-text notion-block-3551d487a2a181bcab34f09894225c13\">I have written code like this before. Looking back, I want to stop my past self.</div><div class=\"notion-text notion-block-f8d70c82e9d44bdfbd7426538cab54c6\">There are four effects scattered through the component. Each one watches its own dependency. It is hard to see what runs first, what triggers what, and which update causes another render. Add some <code class=\"notion-inline-code\">useCallback</code> and <code class=\"notion-inline-code\">useMemo</code>, and the component enters the &quot;do not touch this&quot; zone.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-940b86a4d79c4218a2e29ac6ef8f1aa5\" data-id=\"940b86a4d79c4218a2e29ac6ef8f1aa5\"><span><div id=\"940b86a4d79c4218a2e29ac6ef8f1aa5\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#940b86a4d79c4218a2e29ac6ef8f1aa5\" title=\"How I Write It Now\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">How I Write It Now</span></span></h3><div class=\"notion-text notion-block-3551d487a2a18171b5abeb2d9431c319\">My rule is simple: not every side effect needs to live directly inside the component. If I can move it out, I move it out. The usual options are:</div><ol start=\"1\" class=\"notion-list notion-list-numbered notion-block-3551d487a2a181b4ac54dfd085a68113\" style=\"list-style-type:decimal\"><li>Put related side effects into a custom hook, so the component calls one line.</li></ol><ol start=\"2\" class=\"notion-list notion-list-numbered notion-block-3551d487a2a1817f8940c18a0622291c\" style=\"list-style-type:decimal\"><li>Use React Query, SWR, or server components for data fetching instead of writing fetch logic in effects.</li></ol><ol start=\"3\" class=\"notion-list notion-list-numbered notion-block-3551d487a2a181ca8697e77e9a1ef4e7\" style=\"list-style-type:decimal\"><li>When an effect is really needed, pay close attention to the dependency array and cleanup function.</li></ol><div class=\"notion-text notion-block-2210c077a7a1475baadd99c7d96aacac\">Here is a cleaner version of the dashboard example:</div><div class=\"notion-text notion-block-3551d487a2a1811380ddd372a43d156d\">The <code class=\"notion-inline-code\">mounted</code> flag prevents state updates after the component unmounts. Without some form of cleanup, the red warning in the console will eventually find you.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a181c084b3d8856f01dc0a\" data-id=\"3551d487a2a181c084b3d8856f01dc0a\"><span><div id=\"3551d487a2a181c084b3d8856f01dc0a\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a181c084b3d8856f01dc0a\" title=\"Bottom Line\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">Bottom Line</span></span></h3><div class=\"notion-text notion-block-517ad795e6c14502aed54611f443a559\"><code class=\"notion-inline-code\">useEffect</code> is not the problem. Using it for everything is the problem. Before I write a new effect, I now ask: &quot;Does this truly need to happen after render?&quot; If the answer is &quot;not really,&quot; it probably belongs outside the component or inside a dedicated hook.</div><h3 class=\"notion-h notion-h2 notion-h-indent-0 notion-block-3551d487a2a1813baf3ddb59e03b2b69\" data-id=\"3551d487a2a1813baf3ddb59e03b2b69\"><span><div id=\"3551d487a2a1813baf3ddb59e03b2b69\" class=\"notion-header-anchor\"></div><a class=\"notion-hash-link\" href=\"#3551d487a2a1813baf3ddb59e03b2b69\" title=\"References\"><svg viewBox=\"0 0 16 16\" width=\"16\" height=\"16\"><path fill-rule=\"evenodd\" d=\"M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z\"></path></svg></a><span class=\"notion-h-title\">References</span></span></h3><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181c9b763d083995dbaae\"><li><a class=\"notion-link\" href=\"https://react.dev/reference/react/useEffect\" target=\"_blank\" rel=\"noopener noreferrer\">React documentation: useEffect</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-3551d487a2a181c5a6cec15cb40ebaa2\"><li><a class=\"notion-link\" href=\"https://overreacted.io/a-complete-guide-to-useeffect/\" target=\"_blank\" rel=\"noopener noreferrer\">A Complete Guide to useEffect - Dan Abramov</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-09fd1dabcdfe4ff2b6124366a79ba4b9\"><li><a class=\"notion-link\" href=\"https://kentcdodds.com/blog/useeffect-vs-uselayouteffect\" target=\"_blank\" rel=\"noopener noreferrer\">useEffect vs useLayoutEffect - Kent C. Dodds</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-9493fd4050c646d28e9e86655a7c557f\"><li><a class=\"notion-link\" href=\"https://alexsidorenko.com/blog/useeffect/\" target=\"_blank\" rel=\"noopener noreferrer\">Common useEffect Mistakes and How to Fix Them</a></li></ul><ul class=\"notion-list notion-list-disc notion-block-efea3edff61e4f739298b07baf6904b8\"><li><a class=\"notion-link\" href=\"https://www.robinwieruch.de/react-hooks-fetch-data/\" target=\"_blank\" rel=\"noopener noreferrer\">How to Fetch Data with React Hooks</a></li></ul></main></div>",
            "url": "https://leoqin.com/en/article/avoid-excessive-useeffect",
            "title": "Avoid Overusing React useEffect",
            "summary": "useEffect is useful for side effects outside rendering, but too many effects turn a component into a dependency maze. This post uses a concrete dashboard example to show when to extract custom hooks or move data fetching elsewhere.",
            "date_modified": "2024-12-10T00:00:00.000Z"
        }
    ]
}