# nem035.com Full Context > Personal site of Nemanja Stojanovic (nem035), software engineer, builder, and writer. This file contains the full content of the 5 most recent articles and summaries of all others. For the compact version: https://nem035.com/llms.txt For structured profile data: https://nem035.com/.well-known/brand-facts.json ## About Nemanja Stojanovic (nem035) is a software engineer focused on AI, education technology, and startups. Currently building an AI-first LMS for schools at FlintK12 (https://flintk12.com). Previously at Enki (https://enki.com) where he built an AI coach for tech skills with 2M+ users. ## Recent Articles (Full Content) ### I Want AI to Keep Up With Me *2026-09-09, [thoughts](https://nem035.com/thoughts/i-want-ai-to-keep-up-with-me)* I want AI to help me develop ideas, understand the tradeoffs, and try the result quickly enough to keep thinking and directing the work. I want AI to be extremely intelligent and keep up with how quickly I think. I want to change how a screen works, use it, notice something wrong, and try another version while I still remember exactly what bothered me. I want to work on an image and see a different composition as soon as I can describe it, then follow whatever that makes me think of next. That is the experience I have in mind when I say we still do not have nearly enough AI compute. I want the best work these systems can do, with so little waiting that I can keep exploring without breaking my concentration. A lot of creating works this way. You have some idea of what you want, but making it teaches you things you could not have specified beforehand. An interaction that sounded convenient feels annoying when you use it. Seeing the result helps you figure out what to do next. I want the same experience when thinking through infrastructure or code design. If I am considering moving work into a queue, I want the agent to inspect the current request path and show me what would change, including what happens if a job runs twice. A small diagram and a concrete failure case could help me understand the tradeoff and suggest a better approach. But they need to accurately reflect the system, and they need to arrive while I am still thinking through that decision. I want to understand the answer and follow the next thought without pausing to wait or untangle a mistake. When designing code, I might be unsure where some behavior belongs. Show me how the callers would look under each approach and what happens when the requirements change. Suggest an option I missed. I want help developing the idea well enough to choose a direction, then a working version I can check. Faster creation and better thinking need to happen together. If I have to read pages of explanation or untangle a huge implementation before I can respond, I am still waiting to understand enough to make the next decision. AI can make it harder to stay in flow when using it means repeatedly handing work off and waiting. You ask for a change, switch to something else, and return to a result you have to get your head back into. It may have saved time on the implementation, but you have lost the continuity of working through the problem. If accurate results arrived as quickly as I could use them, I could stay involved, understand what changed, and direct the next step. I think working with AI could feel absorbing again when the feedback is that immediate. The wait also changes what I attempt. If each attempt takes ten minutes, I start deciding whether an idea deserves ten minutes before I have seen it. I leave small annoyances alone. I settle for something that works because checking whether another version would be better feels like too much trouble. Some ideas never get tried because I cannot justify the time it would take to find out whether they are any good. If the result appeared immediately, I would try much more. I could follow a vague suspicion, reject it, and carry on. I would not need to be particularly confident that the experiment was worth doing. By "speed of thought," I mean fast enough that the result arrives while I am still thinking about the change. There will always be things that take time to learn. An instant prototype cannot tell me whether customers will keep using it for six months. But it can let me use the thing I am imagining and decide whether it deserves more work. The intelligence has to be there too. If the system responds immediately but misunderstands the change or breaks something nearby, I still have to stop and repair it. I want it to notice a weak assumption in my approach, explain the consequence clearly, and help me work through it. Once we choose a direction, it should understand the project well enough to implement the change and check that it works. Agent systems already [trade additional time and computation for better task performance](https://www.anthropic.com/engineering/building-effective-agents?utm_source=nem035.com). Fitting that work into an interaction that feels immediate is a much harder target than generating a quick answer. This is also why [access to the whole stack](/thoughts/agents-need-access-to-the-whole-stack) matters. For software, I want the running result in front of me. Reading the code, editing it, building the app, and testing the behavior all contribute to the wait. A faster model only gets me part of the way there if everything around it remains slow. When I am in flow, I still have the alternatives in my head. I remember why I tried this approach and what I wanted to check next. An accurate answer that arrives immediately lets me use all of that. A result that arrives after I have moved on means I have to reconstruct it before I can make a useful decision. I want to spend an afternoon absorbed in a problem with AI helping me work through it. I try something, understand what happened, and have another idea. The work keeps developing while I stay involved in it. Being able to create more quickly would be useful, but being able to keep thinking without repeatedly stopping and starting is what I want most. ### Agents Need Access to the Whole Stack *2026-09-01, [thoughts](https://nem035.com/thoughts/agents-need-access-to-the-whole-stack)* Coding agents become much more productive when they can inspect, operate, and verify the product across development, production, and analytics. Coding agents can already use the same tools we use. An agent working on an iOS app can launch Simulator, navigate to a screen, read device logs, and check its work. On the web it can open the browser, inspect network requests through CDP, and profile a slow page. It can call an API with `curl`, inspect the database with `psql`, and follow the request into logs and traces. We have to give it that access. Repositories and terminals usually come first because they are easy to expose, while the rest of the stack stays scattered across local applications, production dashboards, and separate accounts. The agent can still write code, but it is writing with a partial view of the system. That missing context affects the implementation. A query that works against ten thousand rows may fall apart against one hundred million. The code alone will not tell the agent how large the table is, how the data is distributed, or which indexes exist in production. It should be able to look those things up before choosing an approach. The same goes for traffic, slow dependencies, error patterns, and the analytics events other parts of the business already depend on. Instrumentability is how easily the agent can inspect all of this. A well-instrumented stack lets it answer questions about the real system before it writes code and then check its assumptions afterward. ## Follow the data through the whole stack The interfaces already exist in many cases. `curl`, `psql`, browser tools, simulator commands, and service CLIs are enough to expose a lot of the development loop. MCP servers can connect the agent to live systems such as Sentry, New Relic, PostHog, Mixpanel, or the deployment platform without forcing it through dashboards built for people. Say checkout completion drops after a release. PostHog can narrow the drop to the shipping step. Sentry can connect the new error to a release, and a trace can show that the address fallback is making the same slow call repeatedly. The agent can follow that path into the code, reproduce it locally, write the fix, and run checkout in a real browser to see whether the duplicate requests are gone. After a canary deploy, it can check the same production signal again. Each system adds context to the same investigation. The agent does not need a person to copy an error out of Sentry, explain what a trace says, run a database query, and report whether the funnel recovered. It can move through the data itself and produce the kind of evidence described in [AI-Generated Code Should Come With Evidence](/thoughts/ai-generated-code-should-come-with-evidence). This also makes the first attempt better. If the agent knows the table is large, it can choose an indexed lookup from the beginning. If production traces show that a dependency is unreliable, it can avoid putting that dependency on a synchronous path. Debugging still matters, but fewer obvious assumptions have to fail before the agent reaches a good implementation. ## Keep the agent in the loop after merge I have had great success keeping agents involved after the code is merged. The report below recreates a monitoring workflow I ran in production against real usage. After a fix for empty chat replies shipped, the agent compared failure counts with their pre-deploy baselines every hour. It confirmed that empty replies had dropped 92%, but it also caught generation failures and server errors running at 3.2x and 26.7x their baselines and said they needed investigation. That monitor kept checking for a fixed 72-hour window and then stopped. Other jobs I have run post quiet daily health summaries when nothing is wrong and alert only when a threshold is crossed. Sentry, traces, database state, and product analytics can help the agent understand a task before it starts, verify the work while it is developing, and confirm the result in production. The same access can support a later analysis or postmortem because the agent already has the timeline and the evidence. The agent does not need all of that data loaded into its context. It needs a map of the available systems and permission to query the relevant one when a question comes up. That keeps the integration consistent with [context engineering](/thoughts/how-claude-code-works-part-2#context-engineering): make useful context easy to find instead of making every piece of context permanently present. ## Instrumentability is a top factor Instrumentability should now be one of the top factors in a tech stack decision, alongside product fit, performance, ecosystem quality, and the team's experience. Look at whether the application is easy to build and run from a documented command and whether the agent can inspect the result without asking someone to click through another tool. Local data should be reproducible. Logs and traces should connect back to requests, releases, and source. Production systems should have stable APIs, CLIs, or other machine-readable interfaces. A tool can be pleasant for a person while hiding most of its state behind a dashboard. That hidden state creates manual work every time the agent needs it. A less polished tool with a good API may lead to a much faster development loop once agents are doing a meaningful share of the work. Teams spend a lot of time comparing models and rewriting prompts because those changes are easy to see. Connecting the agent to the browser, database, analytics, and production telemetry looks like plumbing. It often removes more work than another prompt revision because it improves every task that comes afterward. This is the same visibility bias I described in [the attention trap](/thoughts/the-attention-trap). ## Give access safely Live access should be read-only by default. Scoped credentials can let the agent inspect database state, analytics, logs, traces, and deployment metadata without allowing it to change production. The agent can use local data, a simulator, or an isolated environment for most write operations. How far it can go after that depends on the product. On a new product with few customers and an easy rollback path, I am comfortable letting an agent fix, test, and deploy more on its own. On a mature product with real customers and a larger blast radius, I would require approval before deployment or any risky production action. Severity helps with that decision, but so do reversibility, customer impact, sensitive data, dependencies, and the strength of the evidence. The agent can still do most of the work in the mature system. It can detect the problem, reconstruct what happened, reproduce it, prepare the patch, test it, and write the analysis or postmortem. The person steps in near the end to make the decision. Giving agents safe access to the whole stack is one of the best ways to unlock their productivity. They write better code when they understand the system around it, and they can handle more of the work when they can verify the result themselves. ### How Prompt Caching Works, Step by Step *2026-08-17, [thoughts](https://nem035.com/thoughts/how-prompt-caching-works)* A visual walkthrough of prompt caching, from why it is a prefix match to where breakpoints go, what it costs, what silently breaks it, and how to check that it is working. In [How Claude Code Works](/thoughts/how-claude-code-works#layer-3-tool-calling) I showed that an agent re-reads the entire conversation on every turn. A session that adds 30k tokens of new content across eight turns bills for 100k+ input tokens, because the system prompt, tool definitions, and every prior tool result get sent again and again. Prompt caching is what makes that affordable. The provider stores the work it already did on the repeated part of the prompt and charges a fraction of the price to reuse it, which is also why later turns in a long session are not much slower than early ones. It comes with some rules about how a prompt has to be built for the reuse to happen. Every major provider has a version of this. OpenAI [applies it automatically](https://platform.openai.com/docs/guides/prompt-caching) to prompts over 1,024 tokens and discounts the cached portion. Anthropic [makes you mark](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching) where the cacheable region ends. I'll use Anthropic's API for the concrete examples because the explicit markers make the mechanics visible, but the core rule is identical everywhere. This walkthrough builds it up in steps, and every diagram is interactive. --- ## Step 1: Why it's a prefix match Before a model can generate its first output token, it runs over every input token and computes an internal state for each one (in transformer terms, the [attention keys and values](https://huggingface.co/docs/transformers/cache_explanation)). This phase is called **prefill**, and for a long prompt it is most of the input cost and most of the time before the first output token appears. The state for token N depends only on tokens 1 through N. Nothing after it matters. So if two requests share the same first 40,000 tokens, the state for those 40,000 tokens is identical in both, and the second request can load it instead of recomputing it. The moment the two requests differ, everything after the difference is different too, even if the later text happens to match. Prompt caching is a **prefix** match, checked from position zero forward. Try "Put a timestamp at the top". Two requests that are 95% identical share a prefix of zero tokens, because the very first tokens differ. Then try "Same question, different wording", where the shared prefix runs almost to the end. The match is exact at the byte level, so a reordered JSON key or an extra space counts as a difference: The JSON case is the one that bites in real code. Anything you serialize into the prompt (a user profile, tool results, retrieved records) needs a stable key order, or the same data produces different bytes on different requests. Sort keys before serializing and normalize whitespace in one place, and do it at the point where the prompt is assembled rather than hoping every caller remembers. The cached state also belongs to one specific model, since a different model would compute different state from the same tokens. Cache entries are also scoped to your organization; nobody else's prompts can hit your cache and vice versa. --- ## Step 2: What the prefix is in a real request A request to the model is not just your message. As covered in [Layer 2](/thoughts/how-claude-code-works#layer-2-the-assembled-prompt) of the Claude Code walkthrough, it is a system prompt, tool definitions, project context, the conversation so far, and finally the newest message. Whatever nesting the JSON has, the API flattens it into one token sequence in a fixed order: **tools first, then the system prompt, then messages** in order. That order is what "prefix" refers to. Tool definitions are at position zero. The newest message is at the very end. (The order is a property of the API, not of how you write the JSON. On Anthropic's API, `tools` renders before `system` even though most people write `system` first.) So sort the contents of a request by how often they change, most stable first: If the rendered order matches this stability order, caching mostly works on its own. If a per-request value sits at the front, nothing after it can ever be reused. --- ## Step 3: Breakpoints On Anthropic's API you tell the system where a cacheable prefix ends by putting a `cache_control` marker on a content block: ```typescript const response = await client.messages.create({ model: "claude-opus-5", max_tokens: 16000, system: [ { type: "text", text: LARGE_STABLE_SYSTEM_PROMPT, cache_control: { type: "ephemeral" }, }, ], messages: [{ role: "user", content: question }], }); ``` A marker means "cache everything from the start of the request up to and including this block". Since tools render before the system prompt, a marker on the last system block caches the tools too. You can place up to four markers per request. There is also a shortcut: a top-level `cache_control` on the request itself puts a marker on the last cacheable block for you, which is fine when you don't need to control placement. Each marker writes its own entry for the prefix ending at that block, so the last marker sets how much of the request gets cached and the earlier ones are fallback positions: if the next request changes something between marker two and marker three, the API can still match against marker two. And a marker on the newest message caches it for the *next* request, which is the basis of the multi-turn pattern in [Step 6](#step-6-when-it-pays-off). Prefixes shorter than the model's threshold (roughly 1,024 tokens on most current models, as low as 512 on the newest and as high as 4,096 on a few) are silently not cached. No error, just `cache_creation_input_tokens: 0`. This is the first thing to check when a small prompt "isn't caching". ### Why markers at all? If a marker caches everything before it, why not always put one at the very end? For a plain conversation that is the right answer, and it is what the automatic top-level `cache_control` does. Writes cost more than reads, though, and storing a prefix costs the provider something, so the marker is how you say "this part is worth keeping". Placing it yourself matters in three situations: Put a marker where reuse ends, add one where a lookup would otherwise lose the trail, and give each stable region its own lifetime. Everything else can sit at the end. --- ## Step 4: A request that misses, then one that hits Two requests, 40 seconds apart, sharing a 12k-token system prompt with one marker on it. The first has nothing to reuse. The second does. On every request the API checks whether the bytes up to your marker are already stored, reads them if so, and computes and stores them if not, while everything after the marker is computed either way. With several markers, the API checks the *last* one first. If there is no entry for that exact prefix, it walks backward through the earlier markers and roughly the 20 content blocks before the marker, looking for the longest prefix that does exist. This lookback is what lets a marker move forward turn by turn (the next section shows that), and it has a limit: if a single turn adds more than about 20 blocks, say an agent firing many tool calls at once, the next request's marker can fail to find the previous entry. The fix is an intermediate marker inside that window. --- ## Step 5: The life of an entry The default time-to-live of an entry is **5 minutes**, and there is an opt-in **1-hour** TTL. Every time an entry is read, its clock resets at no extra cost. If nothing reads it before the TTL runs out, it expires and the next request has to write it again. Writes and reads are priced differently from plain input: | Operation | Price relative to base input | |---|---:| | Normal input (no caching) | 1x | | Cache write, 5-minute TTL | 1.25x | | Cache write, 1-hour TTL | 2x | | Cache read | 0.1x | Step through a short burst of traffic and watch the entry get written, refreshed, and expire: The 1-hour TTL is for traffic with gaps. If your requests arrive more often than every five minutes, they keep the entry alive on their own and the doubled write price buys you nothing. If you mix TTLs in one request, the 1-hour markers have to come before the 5-minute ones or the API rejects the request. That is the stability order anyway: the frozen system prompt gets the long TTL and the conversation tail gets the short one. --- ## Step 6: When it pays off For the 5-minute TTL, one write plus one read costs 1.25 + 0.1 = 1.35x, against 2x for two uncached requests, so it pays off from the second request. For the 1-hour TTL, you need three requests (2 + 0.1 + 0.1 = 2.2x versus 3x). Beyond that, savings scale with how big the shared prefix is compared to the part that changes. Set requests to 1 and you can see the one case where caching costs money: a prompt that is different from the beginning every time has no reusable prefix, so adding a marker only adds the write premium, and those prompts are better left unmarked. The most important case in practice is the agent loop. Every turn appends to the conversation and changes nothing before it, which is the ideal shape for a prefix cache. The pattern is to put a marker on the last block of the newest turn on every request. Each request then reads everything from the previous turn's entry and writes only the new tail. Here is the same nine-turn session from the [token compounding diagram](/thoughts/how-claude-code-works#layer-3-tool-calling), with and without that moving marker: The billed token count is identical in both modes. What changes is that most of those tokens are reads at a tenth of the price, and the prefill for a 50k-token context becomes a load instead of a recompute. Tools like Claude Code and Cursor are built around this: the system prompt and tool list stay frozen for the whole session, and anything that varies per turn goes at the end of the conversation, so the long prefix keeps hitting. --- ## Step 7: What breaks it Because the cache is a prefix match over the rendered sequence, the damage from a change depends on where in the sequence it lands. Tools render first, then system, then messages, and a change invalidates its own tier and everything after it. Some of these have workarounds. Toggling `tool_choice` or thinking, or attaching an image, only touches the message tier, so it is fine to vary per request. Editing the top-level system prompt mid-conversation is expensive, but on Opus 5, Fable 5, and Opus 4.8 (not Sonnet 5) you can append a `{ role: "system", content: "..." }` message inside `messages` instead, which lands after the cached history and leaves it intact. Adding or reordering tools, and switching models, are the two changes that rebuild everything, so keep the tool set fixed within a conversation and serialize it in a stable order. A **fork** (a subagent, a summarizer, a compaction call) spins up a separate request. If it rebuilds `system` or `tools` with any difference from the parent, it misses the parent's cache entirely. Copy the parent's system, tools, and model verbatim, then append the fork-specific content at the end. ### The silent invalidator The most common failure is a small dynamic value in the wrong place. The markers are in place and nothing errors, and the response still never reports any reads. Any of these inside the prefix does the same thing: | Pattern | Why it breaks | |---|---| | `new Date()` / `datetime.now()` in the system prompt | Prefix changes every request | | A request ID or UUID early in the content | Every request is unique | | Serializing objects without a fixed key order, or iterating a set | Same data, different bytes | | Interpolating the user or session ID into the system prompt | Per-user prefix, no cross-user sharing | | `if (flag) system += ...` | Every flag combination is a distinct prefix | | Building the tool list per user | Tools render at position zero, so nothing caches across users | The fix is always one of three: move the dynamic piece after the last marker, make it deterministic, or delete it if it wasn't doing anything. --- ## Step 8: Checking that it works Three fields in `usage` add up to the total prompt size and tell you what happened (a fourth, `cache_creation`, splits the written tokens by TTL into `ephemeral_5m_input_tokens` and `ephemeral_1h_input_tokens`, which is the only way to see which write price you paid): *[Terminal-style code/command display]* Log all three fields, not just `input_tokens`. On its own, `input_tokens` is only the uncached remainder, so an agent that ran for an hour can show `input_tokens: 400` while the real prompt was 90k. And alert on `cache_read_input_tokens` being zero across repeated requests with what should be the same prefix. When that happens, diff the rendered request bytes between two calls, and whatever differs is what is invalidating the cache. If a request is *not* creating entries either (`cache_creation_input_tokens: 0` as well), the usual causes are a prefix under the minimum size, or a marker that isn't reaching the API because the SDK call is building the request differently than you think. ### The two ratios to watch Per-request fields are for debugging. Over a day or a week, two ratios derived from their totals matter more, and Anthropic's console shows both: - **Cache read ratio**: read tokens divided by read plus uncached tokens (the console's definition). Of the input that was not being written, this is the share that came from cache instead of being recomputed. Agent-heavy traffic in good shape sits around 85 to 90 percent. - **Write amortization**: read tokens divided by written tokens, or how many times each cached token got read back. This says whether the write premium is paying for itself. The break-even is low: with the 5-minute TTL a written token only needs to be read about 0.3 times to come out ahead (1.25 + 0.1R against 1 + R), and about 1.1 times with the 1-hour TTL. At 8 reads per written token, which is a normal number for agent traffic, a token costs 1.25 + 0.8 = 2.05x for nine uses instead of 9x. About three quarters of the cost of that token is gone. The presets in the diagram are the common shapes. A high write count with almost no reads is the timestamp bug from earlier, at fleet scale. Zero writes and zero reads means the markers are not doing anything at all. And when the console breaks miss tokens down by reason (messages changed, system changed, tools changed, model changed), that is exactly the [invalidation hierarchy](#step-7-what-breaks-it) from Step 7 measured on your own traffic: a spike in "system changed" or "tools changed" points at something upstream of the conversation being rebuilt per request. --- ## Step 9: Parallel work and cold starts A cache entry becomes readable only once the first request that writes it starts streaming its response. Requests that start before that point find nothing and each write their own copy. For a burst of parallel requests over the same prefix, send one, wait for its first token, then send the rest. For interactive products where the first request of the day is user-visible, you can pre-warm at startup with a `max_tokens: 0` request that carries the same system prompt and markers as real traffic. It runs prefill, writes the entry, and returns immediately with no output. The pre-warm request has to be non-streaming, with thinking off and no forced `tool_choice`, or the API rejects `max_tokens: 0`. Skip pre-warming when traffic is continuous, since real requests keep the entry alive on their own. --- ## The rules in one place - The cache is a prefix match over the rendered sequence: tools, then system, then messages. Any difference invalidates everything after it. - Order content by stability. Frozen things first, per-request things last. - Put a marker at the end of the stable region, not the end of the whole prompt. In a conversation, move a marker to the end of the newest turn on every request. - Writes cost 1.25x (5m) or 2x (1h), reads cost 0.1x. It pays off from the second request. - Keep tools and model fixed within a conversation. Forks must copy the parent's prefix exactly. - No timestamps, IDs, or conditional sections in the prefix. Make serialization deterministic. - Per request, watch `cache_read_input_tokens`. If it stays at zero, something in the prefix is changing. Over time, watch the read ratio and the write amortization; anything above roughly 0.3 reads per written token (1.1 on the 1-hour TTL) is already paying off. - For fan-out, let the first request start streaming before sending the rest. The model's work on a prompt can be saved from position zero up to the first thing that changed. Everything above is about arranging prompts so that the thing that changes comes last. ### Why Agent Frameworks Cannot Win *2026-08-08, [thoughts](https://nem035.com/thoughts/why-agent-frameworks-cannot-win)* Generic agent frameworks cannot hold a stable abstraction over models that keep absorbing their features and products that need direct control. In July 2023, I wrote this about LangChain: ![My July 2023 X post arguing that LangChain stacked hard opinions on top of experimental technology.](/images/agent-frameworks/langchain-post-2023.png) *My original LangChain post from July 16, 2023.* I had found LangChain useful for getting a prototype running, but every attempt to make the prototype behave exactly as I wanted required fighting abstractions I had not chosen. The framework encoded a particular way to build with language models at a time when nobody, including the framework authors, knew what the durable patterns would be. Three years later, the strongest confirmation comes from LangChain itself. ## What happened to LangChain LangChain did not fail as a project or a company. As I write this, its [Python repository has more than 143,000 GitHub stars](https://github.com/langchain-ai/langchain). The company [raised $125 million at a $1.25 billion valuation](https://www.langchain.com/blog/series-b) in October 2025, and its commercial platform processes a large and growing volume of production agent traces. The original abstraction did not survive intact, though. In his [three-year retrospective](https://www.langchain.com/blog/three-years-langchain), founder Harrison Chase describes the early library as a collection of integrations and high-level templates for RAG, extraction, and question answering. He also describes what happened when people tried to take those templates into production: the interfaces that made LangChain easy to start with got in the way when developers needed control. LangGraph was built in response. It moved down the stack and gave developers explicit control over prompts, state, branching, streaming, human approval, and durable execution. LangChain later summarized the lesson even more directly: [high-level abstractions helped people get started, while lower-level flexibility handled production requirements](https://www.langchain.com/blog/is-langgraph-used-in-production). Then LangChain 1.0 substantially narrowed the original library. Its [own history of the project](https://docs.langchain.com/oss/python/langchain/philosophy) says that all of the old chains and agents were replaced with one agent abstraction built on LangGraph. The previous APIs moved into a package named `langchain-classic`. The commercial product also moved toward less opinionated ground. LangSmith provides tracing, evaluation, and deployment, and it works whether an application uses LangChain or another framework. Those are useful capabilities precisely because they do not need to decide how an agent should think. The company adapted well by retiring much of its original conceptual layer and investing in runtime infrastructure around the model. This is good product strategy, and it also supports the concern I had in 2023. The high-level framework was excellent at packaging the current set of ideas, but the ideas changed too quickly to remain a foundation. ## Frameworks work when the thing underneath them is stable A conventional framework compresses a large amount of settled knowledge. A web framework can assume that an HTTP request has a method, headers, and a body. A database library can assume that transactions and indexes will continue to exist. The framework may evolve, but the concepts underneath it move slowly enough for useful abstractions to harden. An agent framework sits on a different kind of boundary. It tries to standardize both a changing interface and a changing set of capabilities. In 2023, frameworks had to implement tool use with prompts, parse model output, manage conversation history, assemble retrieval pipelines, and invent agent loops. Model providers have since absorbed much of that work into their APIs. OpenAI's [Responses API now includes hosted tools, multi-turn state, and remote MCP support](https://openai.com/index/new-tools-and-features-in-the-responses-api/). Its [Agents SDK intentionally advertises very few abstractions](https://openai.github.io/openai-agents-python/) around the loop. Anthropic gives similar advice in its guide to [building effective agents](https://www.anthropic.com/engineering/building-effective-agents): start with direct model APIs, because frameworks can hide prompts and responses and make debugging harder. The same change happens with model capability. A workflow that needed several carefully arranged calls can collapse into one call when a better model arrives. A retry or routing strategy that helped one generation of models can make the next generation slower or less accurate. An abstraction intended to preserve a best practice can quietly preserve an obsolete workaround. This leaves a generic framework with three choices: - It can wait for provider features to settle, which makes it lag behind the APIs it wraps. - It can expose only the features providers share, which produces the lowest common denominator. - It can add provider-specific options and raw escape hatches, which asks developers to understand both the framework and the provider underneath it. All three can be reasonable engineering decisions. None produces the stable, universal layer that the word *framework* implies. ## Vercel's AI SDK is running into the same pressure In January 2026, I made the same argument about Vercel's AI SDK: ![My January 2026 X post arguing that AI SDK risks becoming a rigid abstraction over unpredictable technology.](/images/agent-frameworks/ai-sdk-post-2026.png) *My AI SDK post from January 3, 2026.* Vercel built AI SDK and made it successful by marketing its library to developers who do not yet know what the abstraction will cost them. It appears throughout Vercel's templates, documentation, and examples as the obvious way to add AI to a TypeScript application. The SDK's [global provider defaults to Vercel AI Gateway](https://ai-sdk.dev/docs/ai-sdk-core/provider-management), and the easiest path offers [automatic authentication and Vercel dashboard observability](https://ai-sdk.dev/providers/ai-sdk-providers/ai-gateway) when the application is deployed on Vercel. By the time developers discover which provider-specific capabilities they gave up, their prompts, messages, streaming code, and UI are already expressed in AI SDK's types. The library can run elsewhere and can call providers directly, but its defaults steadily pull an application into the rest of Vercel's stack. Its adoption is evidence of Vercel's marketing and distribution, not the quality of the abstraction. Its evolution shows the cost of trying to normalize this layer. AI SDK 5 [completely redesigned the streaming architecture](https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0), changed the UI message model, and required a [separate migration path for persisted messages](https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0-data). [AI SDK 6 introduced a reusable `Agent` abstraction](https://vercel.com/blog/ai-sdk-6) in December 2025. Six months later, [AI SDK 7 expanded into an agent platform](https://vercel.com/blog/ai-sdk-7) with durable execution, sandboxes, harness adapters, observability, realtime sessions, and a migration skill that developers can give to their coding agent. Prompt caching is a good example of what the normalization takes away. With Anthropic, cache boundaries affect prompt structure, latency, and cost. AI SDK cannot represent that behavior through its common interface. Its own documentation tells developers to attach [`providerOptions.anthropic.cacheControl`](https://ai-sdk.dev/providers/ai-sdk-providers/anthropic) to individual messages or content blocks, then read Anthropic-specific cache statistics from `providerMetadata.anthropic`. The `UIMessage` type used by its UI hooks does not support these provider options, so messages have to be converted before the options can be applied. A good abstraction gives callers a smaller, stable interface and lets them forget the internals. This one requires the caller to understand Anthropic's caching semantics, AI SDK's message model, and how AI SDK translates one into the other. The application is now coupled to both layers instead of one. Switching providers does not make the caching logic portable; it makes the logic meaningless. The escape hatch keeps the library usable, but it defeats the purpose of the abstraction. When an abstraction has to expose configuration for the internals it claims to hide, it has failed to establish a real boundary. The developer pays the cost of learning the framework and still has to learn every provider underneath it. If choosing among many models is the product, that translation layer may earn its complexity. For most products, provider-specific capabilities matter more than theoretical portability. A thin adapter around the native provider SDK preserves those capabilities, avoids the extra dependency, and remains easier to replace because the application owns the boundary. ## Production pushes from the other direction Provider APIs squeeze frameworks from below by absorbing their common features. Production requirements squeeze them from above. Agent demos look similar. Give a model instructions and tools, keep calling it until it stops, then show the result. This common shape makes a generic abstraction feel natural. Production systems diverge around everything the demo leaves out. They have different permission boundaries, latency budgets, retry rules, approval steps, data retention policies, failure costs, audit requirements, and definitions of success. The important behavior moves into the details. A customer support agent may need to prove that a refund was actually recorded before telling a customer it succeeded. A coding agent needs filesystem isolation, command approval, test evidence, and a way to recover from a process crash. A research agent needs source quality rules and an evaluation system that can distinguish a well-supported answer from a plausible one. These requirements cannot be inferred from a generic `Agent` class. When a framework hides these details, teams eventually fight it. When it exposes all of them, it becomes a runtime and a collection of utilities. The lower layer is less exciting, but it is also more durable because state persistence, tracing, queues, permissions, and sandboxing have concrete contracts. ## OpenClaw shows why agent opinions do not generalize OpenClaw is the clearest current example of how large the appetite has become. The project was created in late 2025 and has accumulated [more than 385,000 GitHub stars](https://github.com/openclaw/openclaw) in under a year. People clearly want a personal agent that can live in their messaging apps, remember context, use their computer, and keep working after they close the chat. There is nothing wrong with OpenClaw making hard choices for that product. Its [Gateway](https://docs.openclaw.ai/architecture) connects to messaging apps and remote devices while owning sessions, queues, authentication, pairing, persistence, and delivery. Its runtime owns the model loop too. Those opinions are part of what makes OpenClaw usable as OpenClaw. The mistake would be treating those product decisions as a general agent abstraction. Agent development is still a wild west. Models, provider APIs, memory strategies, tool protocols, permission boundaries, deployment patterns, and human approval flows are all changing at once. Nobody has found one architecture that works equally well for a personal assistant, customer-support agent, coding agent, and research agent. OpenClaw's choices can be right for OpenClaw and painful when inherited by another product. Adopting its channel model, session model, memory, permissions, plugin system, and deployment shape means building inside someone else's assumptions. As soon as the product needs something different, the abstraction becomes work that has to be undone. Its [security documentation](https://docs.openclaw.ai/gateway/security) shows how specific these choices become. Plugins run as trusted code. A paired node can allow remote command execution. Multiple users with access to one tool-enabled agent share its delegated authority. Every agent product needs answers to these questions, but it cannot safely inherit the same answers. OpenClaw is useful as a product and as a toy for exploring what personal agents might become. Its popularity proves that people want the outcome. It does not prove that its internal architecture, or any current agent architecture, can serve as a durable framework for everyone else. ## The primitives work. The wiring does not generalize Some parts of agent software are already well understood. Stream tokens as the model generates them instead of waiting for the entire response. Define tool inputs and outputs with schemas. Store messages and application state outside the model. Record traces. Require approval before dangerous actions. Run code in a sandbox. None of these choices tells us how to build an agent. We still have to decide what enters the context, when the model runs again, which tools it can see, what requires approval, how work resumes after a failure, and when the loop stops. A coding agent, personal assistant, customer-support agent, and research agent can use the same primitives and connect them in completely different ways. That wiring depends on the product, its users, its risks, and the models and providers it uses. This is the part frameworks are trying to generalize too early. They give us someone else's way of connecting the pieces. It feels convenient while our product matches their assumptions. When it stops matching, we have to understand and undo their architecture before we can build our own. Streaming is a useful primitive. A universal streaming abstraction over every provider is a different claim. Tracing is a useful primitive. A framework deciding the agent loop is a different claim. OpenClaw's wiring can be right for OpenClaw without being right for another agent. ## How I build with agents now I start with the provider's API and keep the loop small. Tools remain ordinary functions with schemas I own. Prompts and message history stay visible. Application state is stored in a format that does not depend on a framework's internal message classes. Supporting libraries should have narrow jobs. A stream renderer can render a stream. A tracing client can record a trace. Neither should own my prompts, messages, application state, or agent loop. Frameworks are still good at getting a prototype running. That was true of LangChain in 2023 and it is true of agent frameworks now. The risk begins when the convenience of the prototype quietly becomes the architecture of the production system. The appetite for useful AI products will keep pulling new frameworks into popularity. Most will look better than they are because demand is so much larger than the available supply. Some of the companies will adapt and win, but they will do it by moving down into infrastructure or up into a specific product. The generic, opinionated layer in the middle has nowhere stable to stand. ### AI-Generated Code Should Come With Evidence *2026-08-04, [thoughts](https://nem035.com/thoughts/ai-generated-code-should-come-with-evidence)* Demo artifacts can replace some code reading by showing that AI-generated changes satisfy the behavior they were meant to produce. Since I started delegating more coding to AI, code review has become the least resolved part of the workflow. If I ask an agent to build a feature and then read every line it wrote, I can get to roughly the same confidence I had before. But a lot of the speed disappears because [comprehension is still the bottleneck](/thoughts/the-comprehension-bottleneck). The code was produced in minutes, while understanding it well enough to trust can take much longer. We already use abstractions without inspecting everything underneath them. When I write Python code that renames a file, I do not review how Python turns that call into an operating system operation. I trust the interpreter and the operating system to behave predictably, so I only need to review the layer I wrote. An AI coding agent looks like another abstraction layer. I describe the behavior I want and it produces the implementation. The problem is that this layer is not reliable enough yet. The agent can misunderstand the requirement, miss an edge case, use an outdated API, or make a change that works locally while breaking something elsewhere. If I skip reading the code, I am mostly trusting the agent's claim that it did a good job. A summary saying that the feature was implemented and the tests pass does not give me much confidence. Having [another AI review the change](/thoughts/you-cant-fix-ai-reliability-with-more-ai) helps catch some mistakes, but the same trust problem remains. The best proxy I have found is a small artifact that demonstrates the behavior. For UI work, nothing beats a before-and-after video. It shows the starting state, the interaction, and the result. A good recording can also show loading states, failed requests, validation, empty states, and the edge cases that are easy to omit from a written summary. We are already using this approach at [Flint](https://flintk12.com). Our AI QA system exercises product updates and records a before-and-after video automatically. In this example, the artifact shows clipped district-owner search results before the fix and confirms that the full list expands afterward. The recording is only seven seconds long, but it gives me the evidence I need for the behavior we changed.
*Video: Flint AI QA, made by my colleague [Zachary Reece](https://zacharyreece.dev/) and [shared publicly by Sohan Choudhury on LinkedIn](https://www.linkedin.com/feed/update/urn:li:activity:7486027236995694593/).* The same idea works outside of UI changes: - A CLI change can come with a recorded terminal session showing the command, output, exit code, and invalid input. - An API change can come with a script that runs representative requests and prints the responses. - A bug fix can show the original reproduction failing and the same reproduction passing after the change. - A data migration can include a dry run with row counts and invariants before and after it runs. These artifacts keep the review close to the original expectation. I asked for a behavior, and the agent shows me that behavior. I can judge the result without first building a full mental model of the implementation. Tests are part of this, but test output is often a weak artifact for a human reviewer. A green test suite tells me that some assertions passed. I still have to read the tests to know what they actually covered. A useful demo exposes the relevant inputs and outputs directly, even when tests or scripts produce it underneath. This is still lossy. A video can show that a flow works while missing an authorization bug, a race condition, a bad abstraction, or a hidden side effect. I would not accept an authentication change or a destructive database migration based only on a recording. Some changes still deserve careful code review because the important properties are not visible through normal product behavior. The artifact is also only as good as the scenarios it covers. If the agent chooses what to demonstrate after finishing the implementation, it can select the happy path and ignore everything uncomfortable. The acceptance criteria should be explicit before the work begins, and the artifact should show which criteria were exercised and which risks remain unverified. I want AI coding tools to return an evidence package with each meaningful change: the acceptance criteria, the relevant demonstrations, and a short list of what the agent could not verify. The diff is still there when I need it, but it stops being the only serious way to establish trust. For now, a 30-second before-and-after recording often gives me more confidence than a large diff and a confident summary. A recorded CLI session can do the same. Neither eliminates code review, but both make it easier to decide where reading the code still adds value. ## All Other Articles ### Thoughts - [How the AI Bubble Crash Could Happen](https://nem035.com/thoughts/how-the-ai-bubble-crash-could-happen.md): AI model labs can become very large companies and still disappoint public markets if valuation runs ahead of adoption. - [How Claude Code Works, Part 2: Context, Control, Configuration](https://nem035.com/thoughts/how-claude-code-works-part-2.md): How three agent codebases converged on the same patterns for context engineering, skills, hooks, custom subagents, and the Agent SDK. - [How Claude Code Works, From Tokens to Agents](https://nem035.com/thoughts/how-claude-code-works.md): A visual walkthrough of what happens inside AI coding tools, built up layer by layer from a basic prompt to a full agent loop. - [Value Decides, Design Differentiates](https://nem035.com/thoughts/value-decides-design-differentiates.md): For things we don't buy for their looks, people choose based on perceived value, and design only tips the scale when that value is roughly equal between options. - [The comprehension debt](https://nem035.com/thoughts/the-comprehension-debt.md): AI makes you faster but you might understand less of what you build. The long-term cost of that gap is still unclear. - [Keeping Taste Alive](https://nem035.com/thoughts/keeping-taste-alive.md): Taste depends on regular contact with work that challenges your standards and attempts that expose your limits. - [LLMs Are Not Conscious](https://nem035.com/thoughts/llms-are-not-conscious.md): LLMs reproduce a narrow slice of what minds do. They don't learn, remember, or act on their own. - [The Real Barrier to Fully Automated Software](https://nem035.com/thoughts/the-real-barrier-to-fully-automated-software.md): Software creation stays heavily assisted, not fully automated, as long as a human carries responsibility for the outcome. - [The Real Bottleneck Is Not Review, It Is Reliability](https://nem035.com/thoughts/ai-code-review-reliability-bottleneck.md): AI code reviewers do not solve the core problem. Reliability and earned trust are what actually matter. - [You Can't Fix AI Reliability With More AI](https://nem035.com/thoughts/you-cant-fix-ai-reliability-with-more-ai.md): Having one AI review another AI's work just pushes the problem further down without solving it. - [Software Engineering Can't Be Solved](https://nem035.com/thoughts/software-engineering-cant-be-solved.md): Every solution creates new problems. The idea that software development will be "solved" misunderstands the nature of problems themselves. - [CLAUDE.md Is Not the Problem](https://nem035.com/thoughts/claude-md-is-not-the-problem.md): The problem with CLAUDE.md is the same problem code comments have. Most of them are noise. - [LLMs can't introspect](https://nem035.com/thoughts/llm-introspection-illusion.md): Every LLM self-explanation is drawn from training data and prior tokens, same as everything else it says. - [Agent-First Development and the Cloud Shift](https://nem035.com/thoughts/agent-first-development-cloud-shift.md): When agents become the primary interface for building software, local machines become the bottleneck. - [AI gave us a faster brush](https://nem035.com/thoughts/ai-faster-brush-same-canvas.md): The joy of coding was never in writing code itself, but in solving problems and creating on an infinite canvas of bits. - [The Biggest Misconception About LLMs](https://nem035.com/thoughts/the-biggest-misconception-about-llms.md): The simplest way to understand a large language model is this - it works like an extremely smart printer - [The Illusion of Software Automation](https://nem035.com/thoughts/the-illusion-of-software-automation.md): Productivity and output does not scale with token budgets as the bottleneck is (and remains) human comprehension. - [Learn to Code](https://nem035.com/thoughts/learn-to-code.md): Coders will take everyone else's jobs long before anyone takes theirs. AI amplifies software literacy more than it replaces it. - [The Comprehension Bottleneck](https://nem035.com/thoughts/the-comprehension-bottleneck.md): Generation has a clear target. Comprehension does not. That difference explains why AI accelerates one and barely touches the other. - [Why AI Won't Kill SaaS](https://nem035.com/thoughts/saas-in-the-age-of-ai.md): AI makes building easier. That is exactly why you should not build. - [The Attention Trap](https://nem035.com/thoughts/the-attention-trap.md): Organizations fail not from lack of effort, but from systematically focusing on what's easy to measure instead of what matters. - [Why Technical Skills Matter More in the Age of AI](https://nem035.com/thoughts/why-technical-people-with-ai-are-unstoppable.md): AI amplifies vibe coders, but production reliability still belongs to engineers who know how things actually work. - [Two-stage AI calls for structured output](https://nem035.com/thoughts/two-stage-ai-calls-for-structured-output.md): Unless latency is critical, use 2 AI calls to generate structured output. - [A major edge human coders have over AI is short-term memory in the form of intuition](https://nem035.com/thoughts/human-coders-short-term-memory.md): Human developers build an intuitive cache for codebases that AI lacks, creating a significant advantage in development speed and understanding. - [Perceived Intelligence Is The Lowest Common Denominator](https://nem035.com/thoughts/perceived-inteligence.md): We may not see a big jump in perceived model intelligence from here. - [Perfection is the precursor to automation](https://nem035.com/thoughts/ai-experties.md): You can’t disrupt a field by automating expertise because judging the automation also requires expertise. - [AI LLMs think out loud](https://nem035.com/thoughts/ai-llms-think-out-loud.md): If you don't see its thoughts, it isn't thinking. It's just recalling information. - [AI Honeymoon Phase](https://nem035.com/thoughts/ai-honeymoon-phase.md): AI is undeniably a productivity boost, but there's many more bottlenecks to overcome - [AI gives devs more leverage](https://nem035.com/thoughts/ai-gives-devs-leverage.md): There's never been a better time to get into software engineering. - [Why Now is the Best Time to Learn to Code](https://nem035.com/thoughts/ai-swe-open-doors.md): AI turbocharges SWEs, but it's not a replacement for human developers - [How to Talk to GenAI](https://nem035.com/thoughts/how-to-talk-to-gen-ai.md): Great prompting is just good communication. Be clear, give context, and treat AI like a smart assistant, not a mind reader. - [There's never been a better time to learn to code](https://nem035.com/thoughts/ai-software-jobs.md): AI will unlock more dev jobs than ever before - [AI doesn't have to make you lazy](https://nem035.com/thoughts/ai-does-not-make-you-lazy.md): AI is a tool. It's up to you to use it to be lazy or not. - [AI Might Like 🍝 Code](https://nem035.com/thoughts/ai-might-like-spaghetti-code.md): Why AI systems might prefer what humans call "spaghetti code" - [AI is a tool, not a human](https://nem035.com/thoughts/ai-is-a-tool-not-a-human.md): You need the same skills to use AI as you do to write code. - [AI tools will create more coders, not less](https://nem035.com/thoughts/ai-tools-will-create-more-coders.md): AI assistants do not displace coders, they amplify them. - [Hands-off Leadership Transition](https://nem035.com/thoughts/leadership-transition.md): The struggle of stepping back and trusting your team. - [AI and Knowledge Work](https://nem035.com/thoughts/ai-knowledge-work.md): AI will not replace knowledge work, it will increase output / time. - [Learning to code is like learning a human language](https://nem035.com/thoughts/learning-to-code-human-language.md): Understanding the parallels between programming and human languages makes coding more approachable. - [How does an LLM perform actions?](https://nem035.com/thoughts/how-does-llm-perform-actions.md): The LLM doesn't perform actions itself; it facilitates the actions through textual commands. - [Why AI Won't Replace Developers](https://nem035.com/thoughts/ai-replaces-all-jobs-when-replacing-coders.md): Focusing on AI replacing software engineering jobs is like focusing on the fact that a flood in your city will damage your house while ignoring the effect on the city as a whole. - [Threads vs Insta](https://nem035.com/thoughts/threads-vs-insta.md): How Threads improved upon Twitter's social dynamics by removing the following count from profiles. - [Network Effect vs Trend](https://nem035.com/thoughts/network-effect-vs-trend.md): Network effects participants increase utility, trend participants increase growth. - [You love what you become good at (even though you suck at it at first)](https://nem035.com/thoughts/all-skills-take-time.md): The path to our calling is far less obvious for most of us. - [Every output of an LLM is a hallucination](https://nem035.com/thoughts/llm-always-hallucinates.md): LLMs don't do anything different when they "make up" information because they always "make it up". - [Work Life Balance](https://nem035.com/thoughts/work-life-balance.md): Extraordinary results do require extraordinary effort but a happy life comes in many forms. - [Mastery](https://nem035.com/thoughts/mastery.md): Success means ignoring your brain's persistent attempts to provide comfort in failure. - [Reading code like a human](https://nem035.com/thoughts/javascript-story.md): One unique aspect of JavaScript I personally appreciate is its flexibility in function declarations. - [Everything humans do is natural](https://nem035.com/thoughts/everything-humans-do-is-natural.md): It's a silly, self-centered mind trick to call anything we do as "natural" or "unnatural". - [Coding skills will be more useful with AI, not less](https://nem035.com/thoughts/learning-to-code-is-more-useful-with-ai.md): Learning to code and becoming technical is more important in the age of AI, not less. - [LLMs are dreamers.](https://nem035.com/thoughts/llm-dreamer.md): There's little relation between what an LLM says it can do and its actual capabilities. - [Art of Negotiation](https://nem035.com/thoughts/art-of-negotiation.md): Essential negotiation techniques to master - [Psychology of Manipulation](https://nem035.com/thoughts/psychology-of-manipulation.md): Key manipulation strategies to be aware of - [Learn the boring fundamentals](https://nem035.com/thoughts/learn-the-boring-fundamentals.md): Understanding the fundamentals is key to mastering any subject. - [Coding jobs are safest from AI](https://nem035.com/thoughts/coding-jobs-are-safest-from-ai.md): If AI can replace coders, it can replace anyone. - [An issue with the AI paperclip optimizer theory](https://nem035.com/thoughts/issue-with-ai-doomsday-paperclip-squiggle-optimizer-scenario.md): It's unlikely we'll give a monkey access to an AK47 - [2023 is start of AI augmentation, not replacement](https://nem035.com/thoughts/cusp-of-ai-augmentation-era.md): In the near future, most jobs will get better and few will get replaced. - [Why can AI chatbots produce scary outputs?](https://nem035.com/thoughts/ai-chatbot-scary.md): Most written content about AI focuses on doomsday scenarios, and that's what the models learned from. - [ChatGPT in School](https://nem035.com/thoughts/chatgpt-in-schools.md): With the right mindset, ChatGPT gives you a superpower. - [Most ideas are recycled](https://nem035.com/thoughts/original-thought-is-a-unicorn.md): Original thought is a unicorn. - [Short term happy is long term unhappy](https://nem035.com/thoughts/short-term-unhappy-longterm-happy.md): Hard choices, easy life. Easy choices, hard life. - [ChatGPT](https://nem035.com/thoughts/chatgpt.md): If you can write English and think systematically, you can be a junior dev. - [A real verdict by an imaginary jury](https://nem035.com/thoughts/self-worth.md): What we imagine people think of us guides our self-esteem. - [Product Builder Perspective](https://nem035.com/thoughts/product-builder-perspective.md): Opinions on product building I've formed over the years - [There's no Us and Them](https://nem035.com/thoughts/all-opinions-are-worthy-of-consideration.md): Ignoring or brushing over the opposing view creates a mental dishonesty. - [Neural Networks work but we don't know why](https://nem035.com/thoughts/neural-networks-mistery.md): They work but we don't know why - [Following through](https://nem035.com/thoughts/following-through.md): Action overcomes inertia. Consistent action compounds. - [Over-communication is far cheaper than the wrong direction.](https://nem035.com/thoughts/over-communication.md): As a default, it's better to repeat yourself when unnecessary than to risk not saying something that should be heard. - [Knowledge Checkpoints](https://nem035.com/thoughts/knowledge-checkpoints.md): Breadth is achievable through sequential units of depth. - [Coding principles](https://nem035.com/thoughts/coding-principles.md): Some coding lessons I've learned over the years - [There're many ways to software craftsmanship](https://nem035.com/thoughts/software-craftmanship.md): The path to competency is made up of many roads. - [Admire the distance, not the destination](https://nem035.com/thoughts/distance-over-destination.md): The starting point, not the finish line, determines the level of success - [Learning by doing](https://nem035.com/thoughts/learning-by-doing.md): True learning comes from understanding the journey taken towards an insight, not from the insight's factual existence. - [Random Remote Work Thoughts](https://nem035.com/thoughts/remote-work.md): Working remotely doesn't mean moving the same work habits out of the office. It's a fundamental shift in habits. - [Developing Async Sense in JavaScript](https://nem035.com/thoughts/developing-async-sense-i.md): Part I - Sync vs Async, Threads and the Event Loop - [Anyone can learn anything](https://nem035.com/thoughts/anyone-can-learn-anything.md): Anyone can learn anything and it's difficult for most of us. ### Notes - [7 Powers by Hamilton Helmer](https://nem035.com/notes/hamilton-helmers-7-powers.md): How companies achieve persistent differential returns against their competitors, and do so sustainably. - [How To Live An Asymmetric Life](https://nem035.com/notes/graham-weaver-asymmetric-life.md): Graham Weaver, Lecturer at Stanford Graduate School of Business and Founder of Alpine Investors, shares four ways to live an asymmetric life as part of the GSB 2023 Last Lecture Series. - [Layne Norton on fiber, cholesterol, creatine... | Huberman Lab Podcast](https://nem035.com/notes/huberman-podcast-layne-norton.md): Notes from the HubermanLab podcast episode with Layne Norton @biolayne - [How To Get Rich: One of the World's Greatest Entrepreneurs Shares His Secrets](https://nem035.com/notes/how-to-get-rich-felix-dennis.md): Book notes from Felix Dennis - [Morgan Housel - Psychology of Money](https://nem035.com/notes/morgan-housel-psychology-of-money.md): Timeless lessons on wealth, greed, and happiness - [Nir Eyal - Hooked](https://nem035.com/notes/nir-eyal-hooked.md): Use continuous innovation to create radically successful businesses - [Steve Jobs 1990 Interview](https://nem035.com/notes/steve-jobs-interview-1990.md): Steve jobs recognizing 30+ years of computing evolution - [How To Lie With Statistics](https://nem035.com/notes/how-to-lie-with-statistics.md): A concise guide on common techniques for distorting statistics. - [Happiness hypothesis](https://nem035.com/notes/happiness-hypothesis-jonathan-haidt.md): Finding Modern Truth in Ancient Wisdom - [ADHD & How Anyone Can Improve Their Focus | Huberman Lab Podcast](https://nem035.com/notes/huberman-podcast-adhd.md): Notes from Huberman Lab Podcast - [How to Increase Motivation & Drive | Huberman Lab Podcast](https://nem035.com/notes/huberman-podcast-motivation-and-drive.md): The chemistry of motivation is tightly wound in with the neurochemistry of movement. The same single molecule, dopamine, is responsible both for our sense of motivation and for movement. - [The Almanack of Naval Ravikant](https://nem035.com/notes/almanack-of-naval-ravikant.md): A guide to wealth and happiness - [Finite vs. Infinite Games](https://nem035.com/notes/infinite-games-simon-sinek.md): Those aiming to play outdo those aiming to win. - [How Your Brain Works | Huberman Lab Podcast](https://nem035.com/notes/huberman-podcast-how-your-brain-works.md): Everything you think, remember, feel and imagine from the day you're born to the day you die is a continuous loop of communication within your brain. - [How Your Brain Changes: Neuroplasticity | Huberman Lab Podcast](https://nem035.com/notes/huberman-podcast-neuroplasticity.md): Our brains have the ability to change their internal circuitry in response to experiences. - [Principles for Dealing with the Changing World Order by Ray Dalio](https://nem035.com/notes/principles-world-order-ray-dalio.md): Ray Dalio explains the big cycle of economic order of empires and how to deal with the changing world order. - [How The Economic Machine Works by Ray Dalio](https://nem035.com/notes/how-the-economic-machine-works-ray-dalio.md): Ray Dalio explains how the economy works - [Mental Models #1: Address "Important"; Ignore "Urgent"](https://nem035.com/notes/peter-hollins-mental-models-1.md): Separate priorities from impostors - [Mental Models #10: What Would Bayes Do](https://nem035.com/notes/peter-hollins-mental-models-10.md): Calculate probabilities and predict the future based on real events - [Mental Models #11: Do It Like Darwin](https://nem035.com/notes/peter-hollins-mental-models-11.md): Seek real, honest truth in a situation - [Mental Models #12: Think With System 2](https://nem035.com/notes/peter-hollins-mental-models-12.md): Think analytically instead of emotionally - [Mental Models #13: Peer Review Your Perspectives](https://nem035.com/notes/peter-hollins-mental-models-13.md): Understand the consensus view and why you might differ - [Mental Models #14: Find Your Own Flaws](https://nem035.com/notes/peter-hollins-mental-models-14.md): Scrutinize yourself before others can - [Mental Models #15: Separate Correlation From Causation](https://nem035.com/notes/peter-hollins-mental-models-15.md): Understand what truly needs to be addressed to solve a problem - [Mental Models #2: Visualize All the Dominoes](https://nem035.com/notes/peter-hollins-mental-models-2.md): Make decisions as informed as possible - [Mental Models #3: Make Reversible Decisions](https://nem035.com/notes/peter-hollins-mental-models-3.md): Remove indecisions and have a bias to action - [Mental Models #4: Seek "Satisfiction"](https://nem035.com/notes/peter-hollins-mental-models-4.md): Achieve your priorities and ignore what doesn't matter - [Mental Models #5: Stay within 40% - 70%](https://nem035.com/notes/peter-hollins-mental-models-5.md): Balance information with action - [Mental Models #6: Minimize Regret](https://nem035.com/notes/peter-hollins-mental-models-6.md): Consult the future you on decisions - [Mental Models #7: Ignore "Black Swans"](https://nem035.com/notes/peter-hollins-mental-models-7.md): Understand how outliers shouldn't change your thinking - [Mental Models #8: Look for Equilibrium Points](https://nem035.com/notes/peter-hollins-mental-models-8.md): Find real patterns in data and not be fooled - [Mental Models #9: Wait for the Regression to the Mean](https://nem035.com/notes/peter-hollins-mental-models-9.md): Find real patterns in data and not be fooled - [The Lean Startup](https://nem035.com/notes/erik-reis-lean-startup.md): Use continuous innovation to create radically successful businesses ### Projects - [sfeed.dev](https://nem035.com/projects/sfeed-dev.md): A CLI and MCP server for posting to Facebook Pages and Instagram from local files. - [Bertle](https://nem035.com/projects/bertle.md): Email your ideas, AI organizes them into living documents with research and insights. - [Lound.ai](https://nem035.com/projects/lound-ai.md): Voice journaling app that turns spoken thoughts into organized insights. - [Tim Ferriss AI](https://nem035.com/projects/tim-ferriss-ai.md): Ask AI questions about Tim Ferriss or any of his guests and get human-like answers based on what was said in any episode. ### Challenges - [200 pushups per day for 1 year](https://nem035.com/challenges/pushups.md): I did 200 pushups per day for 1 year. A few quick thoughts on what I learned. - [Bike from Miami to Key Largo](https://nem035.com/challenges/bike-miami-to-key-largo.md): Biking 150 miles from Miami to Key Largo and back, over the course of 3 days, on and off road, rain or shine.