10 interesting stories served every morning and every evening.
People must be vouched for before interacting with certain parts of a project (the exact parts are configurable to the project to enforce). People can also be explicitly
denounced to block them from interacting with the project.
The implementation is generic and can be used by any project on any code forge, but we provide GitHub integration out of the box via GitHub actions and the CLI.
The vouch list is maintained in a single flat file using a minimal format that can be trivially parsed using standard POSIX tools and any programming language without external libraries.
Vouch lists can also form a web of trust. You can configure Vouch to read other project’s lists of vouched or denounced users. This way, projects with shared values can share their trust decisions with each other and create a larger, more comprehensive web of trust across the ecosystem. Users already proven to be trustworthy in one project can automatically be assumed trustworthy in another project, and so on.
Open source has always worked on a system of trust and verify.
Historically, the effort required to understand a codebase, implement a change, and submit that change for review was high enough that it naturally filtered out many low quality contributions from unqualified people. For over 20 years of my life, this was enough for my projects as well as enough for most others.
Unfortunately, the landscape has changed particularly with the advent of AI tools that allow people to trivially create plausible-looking but extremely low-quality contributions with little to no true understanding. Contributors can no longer be trusted based on the minimal barrier to entry to simply submit a change.
But, open source still works on trust! And every project has a definite group of trusted individuals (maintainers) and a larger group of probably trusted individuals (active members of the community in any form). So, let’s move to an explicit trust model where trusted individuals can vouch for others, and those vouched individuals can then contribute.
Who and how someone is vouched or denounced is left entirely up to the project integrating the system. Additionally, what consequences a vouched or denounced person has is also fully up to the project. Implement a policy that works for your project and community.
Integrating vouch into a GitHub project is easy with the
provided GitHub Actions. By choosing which actions to use, you can fully control how users are vouched and what they can or can’t do.
For an example, look at this repository! It fully integrates vouch.
Below is a list of the actions and a brief description of their function. See the linked README in the action directory for full usage details.
The CLI is implemented as a Nushell module and only requires Nushell to run. There are no other external dependencies.
This is Nushell, so you can get help on any command:
use vouch *
help add
help check
help denounce
help gh-check-pr
help gh-manage-by-issue
vouch check
# Preview new file contents (default)
vouch add someuser
# Write the file in-place
vouch add someuser –write
# Preview new file contents (default)
vouch denounce badactor
# With a reason
vouch denounce badactor –reason “Submitted AI slop”
# Write the file in-place
vouch denounce badactor –write
Requires the GITHUB_TOKEN environment variable. If not set and gh
is available, the token from gh auth token is used.
# Check PR author status (dry run)
vouch gh-check-pr 123 –repo owner/repo
# Auto-close unvouched PRs (dry run)
vouch gh-check-pr 123 –repo owner/repo –auto-close
# Actually close unvouched PRs
vouch gh-check-pr 123 –repo owner/repo –auto-close –dry-run=false
# Allow unvouched users, only block denounced
vouch gh-check-pr 123 –repo owner/repo –require-vouch=false –auto-close
# Dry run (default)
vouch gh-manage-by-issue 123 456789 –repo owner/repo
# Actually perform the action
vouch gh-manage-by-issue 123 456789 –repo owner/repo –dry-run=false
Responds to comments from collaborators with write access:
* vouch — vouches for the issue author with a reason
Keywords are customizable via –vouch-keyword and –denounce-keyword.
The module also exports a lib submodule for scripting:
use vouch/lib.nu *
let records = open VOUCHED.td
$records | check-user “mitchellh” –default-platform github # “vouched”, “denounced”, or “unknown”
$records | add-user “newuser” # returns updated table
$records | denounce-user “badactor” “reason” # returns updated table
$records | remove-user “olduser” # returns updated table
The vouch list is stored in a .td file. See
VOUCHED.example.td for an example. The file is looked up at VOUCHED.td or .github/VOUCHED.td by default.
* One handle per line (without @), sorted alphabetically.
* Optionally add details after a space following the handle.
The from td and to td commands are exported by the module, so Nushell’s open command works natively with .td files to decode into structured tables and encode back to the file format with comments and whitespace preserved.
...
Read the original on github.com »
You’re using AI to be more productive. So why are you more exhausted than ever? The paradox every engineer needs to confront.
You’re using AI to be more productive. So why are you more exhausted than ever? The paradox every engineer needs to confront.
I shipped more code last quarter than any quarter in my career. I also felt more drained than any quarter in my career. These two facts are not unrelated.
I build AI agent infrastructure for a living. I’m one of the core maintainers of OpenFGA (CNCF Incubating), I built agentic-authz for agent authorization, I built Distill for context deduplication, I shipped MCP servers. I’m not someone who dabbles with AI on the side. I’m deep in it. I build the tools that other engineers use to make AI agents work in production.
And yet, I hit a wall. The kind of exhaustion that no amount of tooling or workflow optimization could fix.
If you’re an engineer who uses AI daily - for design reviews, code generation, debugging, documentation, architecture decisions - and you’ve noticed that you’re somehow more tired than before AI existed, this post is for you. You’re not imagining it. You’re not weak. You’re experiencing something real that the industry is aggressively pretending doesn’t exist. And if someone who builds agent infrastructure full-time can burn out on AI, it can happen to anyone.
I want to talk about it honestly. Not the “AI is amazing and here’s my workflow” version. The real version. The one where you stare at your screen at 11pm, surrounded by AI-generated code you still need to review, wondering why the tool that was supposed to save you time has consumed your entire day.
Here’s the thing that broke my brain for a while: AI genuinely makes individual tasks faster. That’s not a lie. What used to take me 3 hours now takes 45 minutes. Drafting a design doc, scaffolding a new service, writing test cases, researching an unfamiliar API. All faster.
But my days got harder. Not easier. Harder.
The reason is simple once you see it, but it took me months to figure out. When each task takes less time, you don’t do fewer tasks. You do more tasks. Your capacity appears to expand, so the work expands to fill it. And then some. Your manager sees you shipping faster, so the expectations adjust. You see yourself shipping faster, so your own expectations adjust. The baseline moves.
Before AI, I might spend a full day on one design problem. I’d sketch on paper, think in the shower, go for a walk, come back with clarity. The pace was slow but the cognitive load was manageable. One problem. One day. Deep focus.
Now? I might touch six different problems in a day. Each one “only takes an hour with AI.” But context-switching between six problems is brutally expensive for the human brain. The AI doesn’t get tired between problems. I do.
This is the paradox: AI reduces the cost of production but increases the cost of coordination, review, and decision-making. And those costs fall entirely on the human.
Before AI, my job was: think about a problem, write code, test it, ship it. I was the creator. The maker. That’s what drew most of us to engineering in the first place - the act of building.
After AI, my job increasingly became: prompt, wait, read output, evaluate output, decide if output is correct, decide if output is safe, decide if output matches the architecture, fix the parts that don’t, re-prompt, repeat. I became a reviewer. A judge. A quality inspector on an assembly line that never stops.
This is a fundamentally different kind of work. Creating is energizing. Reviewing is draining. There’s research on this - the psychological difference between generative tasks and evaluative tasks. Generative work gives you flow states. Evaluative work gives you decision fatigue.
I noticed it first during a week where I was using AI heavily for a new microservice. By Wednesday, I couldn’t make simple decisions anymore. What should this function be named? I didn’t care. Where should this config live? I didn’t care. My brain was full. Not from writing code - from judging code. Hundreds of small judgments, all day, every day.
The cruel irony is that AI-generated code requires more careful review than human-written code. When a colleague writes code, I know their patterns, their strengths, their blind spots. I can skim the parts I trust and focus on the parts I don’t. With AI, every line is suspect. The code looks confident. It compiles. It might even pass tests. But it could be subtly wrong in ways that only surface in production, under load, at 3am.
So you read every line. And reading code you didn’t write, that was generated by a system that doesn’t understand your codebase’s history or your team’s conventions, is exhausting work.
This is also why I think agent security and authorization matter so much. If we can’t review everything AI produces - and we can’t, not at scale - then we need systems that constrain what agents can do in the first place. Least-privilege access, scoped tokens, audit trails. The less you have to worry about “did the AI do something dangerous,” the more cognitive budget you have for the work that actually matters. This isn’t just a security problem. It’s a human sustainability problem.
Engineers are trained on determinism. Same input, same output. That’s the contract. That’s what makes debugging possible. That’s what makes reasoning about systems possible.
I had a prompt that worked perfectly on Monday. Generated clean, well-structured code for an API endpoint. I used the same prompt on Tuesday for a similar endpoint. The output was structurally different, used a different error handling pattern, and introduced a dependency I didn’t ask for.
Why? No reason. Or rather, no reason I can access. There’s no stack trace for “the model decided to go a different direction today.” There’s no log that says “temperature sampling chose path B instead of path A.” It just… happened differently.
For someone whose entire career is built on “if it broke, I can find out why,” this is deeply unsettling. Not in a dramatic way. In a slow, grinding, background-anxiety way. You can never fully trust the output. You can never fully relax. Every interaction requires vigilance.
I tried to fight this. I version-controlled my prompts. I built elaborate system messages. I created templates. Some of it helped. None of it solved the fundamental problem: you are collaborating with a probabilistic system, and your brain is wired for deterministic ones. That mismatch is a constant, low-grade source of stress.
This frustration is actually what led me to build Distill - deterministic context deduplication for LLMs. No LLM calls, no embeddings, no probabilistic heuristics. Pure algorithms that clean your context in ~12ms. I wanted at least one part of the AI pipeline to be something I could reason about, debug, and trust. If the model’s output is going to be nondeterministic, the least I can do is make sure the input is clean and predictable.
The engineers I’ve talked to who handle this best are the ones who’ve made peace with it. They treat AI output like a first draft from a smart but unreliable intern. They expect to rewrite 30% of it. They budget time for that rewriting. They don’t get frustrated when the output is wrong because they never expected it to be right. They expected it to be useful. There’s a difference.
Take a breath and try to keep up with just the last few months. Claude Code ships sub-agents, then skills, then an Agent SDK, then Claude Cowork. OpenAI launches Codex CLI, then GPT-5.3-Codex - a model that literally helped code itself. New coding agents announce background mode with hundreds of concurrent autonomous sessions. Google drops Gemini CLI. GitHub adds an MCP Registry. Acquisitions happen weekly. Amazon Q Developer gets agentic upgrades. CrewAI, AutoGen, LangGraph, MetaGPT - pick your agent framework, there’s a new one every week. Google announces A2A (Agent-to-Agent protocol) to compete with Anthropic’s MCP. OpenAI ships its own Swarm framework. Kimi K2.5 drops with agent swarm architecture orchestrating 100 parallel agents. “Vibe coding” becomes a thing. OpenClaw launches a skills marketplace and within one week, researchers find 400+ malicious agent skills uploaded to ClawHub. And somewhere in the middle of all this, someone on LinkedIn posts “if you’re not using AI agents with sub-agent orchestration in 2026, you’re already obsolete.”
That’s not a year. That’s a few months. And I’m leaving stuff out.
I fell into this trap hard. I was spending weekends evaluating new tools. Reading every changelog. Watching every demo. Trying to stay at the frontier because I was terrified of falling behind.
Here’s what that actually looked like: I’d spend Saturday afternoon setting up a new AI coding tool. By Sunday I’d have a basic workflow. By the following Wednesday, someone would post about a different tool that was “way better.” I’d feel a pang of anxiety. By the next weekend, I’d be setting up the new thing. The old thing would sit unused. One coding assistant to the next to the next and back to the first one. Each migration cost me a weekend and gave me maybe a 5% improvement that I couldn’t even measure properly.
Multiply this by every category - coding assistants, chat interfaces, agent frameworks, multi-agent orchestration platforms, MCP servers, context management tools, prompt libraries, swarm architectures, skills marketplaces - and you get a person who is perpetually learning new tools and never getting deep with any of them. The Hacker News front page alone is enough to give you whiplash. One day it’s “Show HN: Autonomous Research Swarm” and the next it’s “Ask HN: How will AI swarms coordinate?” Nobody knows. Everyone’s building anyway.
The worst part is the knowledge decay. I spent two weeks building a sophisticated prompt engineering workflow in early 2025. Carefully crafted system prompts, few-shot examples, chain-of-thought templates. It worked well. Three months later, the model updated, the prompting best practices shifted, and half my templates produced worse results than a simple one-liner. Those two weeks were gone. Not invested. Spent. The same thing happened with my MCP server setup - I built five custom servers (Dev.to publisher, Apple Notes integration, Python and TypeScript sandboxes, more), then the protocol evolved, then the MCP Registry launched on GitHub and suddenly there were thousands of pre-built ones. Some of my custom work became redundant overnight.
The agent framework churn is even worse. I watched teams go from LangChain to CrewAI to AutoGen to custom orchestration in the span of a year. Each migration meant rewriting integrations, relearning APIs, rebuilding workflows. The people who waited and did nothing often ended up in a better position than the people who adopted early and had to migrate twice.
I’ve since adopted a different approach. Instead of chasing every new tool, I go deep on the infrastructure layer underneath them. Tools come and go. The problems they solve don’t. Context efficiency, agent authorization, audit trails, runtime security - these are durable problems regardless of which framework is trending this month. That’s why I built agentic-authz on OpenFGA instead of tying it to any specific agent framework. That’s why Distill works at the context level, not the prompt level. Build on the layer that doesn’t churn.
I still track the landscape closely - you have to when you’re building infrastructure for it. But I track it to understand where the ecosystem is going, not to adopt every new thing. There’s a difference between being informed and being reactive.
This one is insidious. You’re trying to get AI to generate something specific. The first output is 70% right. So you refine your prompt. The second output is 75% right but broke something the first one had correct. Third attempt: 80% right but now the structure is different. Fourth attempt: you’ve been at this for 45 minutes and you could have written the thing from scratch in 20.
I call this the prompt spiral. It’s the AI equivalent of yak shaving. You started with a clear goal. Thirty minutes later you’re debugging your prompt instead of debugging your code. You’re optimizing your instructions to a language model instead of solving the actual problem.
The prompt spiral is especially dangerous because it feels productive. You’re iterating. You’re getting closer. Each attempt is slightly better. But the marginal returns are diminishing fast, and you’ve lost sight of the fact that the goal was never “get the AI to produce perfect output.” The goal was to ship the feature.
I now have a hard rule: three attempts. If the AI doesn’t get me to 70% usable in three prompts, I write it myself. No exceptions. This single rule has saved me more time than any prompting technique I’ve ever learned.
Engineers tend toward perfectionism. We like clean code. We like tests that pass. We like systems that behave predictably. This is a feature, not a bug - it’s what makes us good at building reliable software.
AI output is never perfect. It’s always “pretty good.” 70-80% there. The variable names are slightly off. The error handling is incomplete. The edge cases are ignored. The abstraction is wrong for your codebase. It works, but it’s not right.
For a perfectionist, this is torture. Because “almost right” is worse than “completely wrong.” Completely wrong, you throw away and start over. Almost right, you spend an hour tweaking. And tweaking AI output is uniquely frustrating because you’re fixing someone else’s design decisions - decisions that were made by a system that doesn’t share your taste, your context, or your standards.
I had to learn to let go. Not of quality - I still care about quality. But of the expectation that AI would produce quality. I now treat every AI output as a rough draft. A starting point. Raw material. I mentally label it “draft” the moment it appears, and that framing change alone reduced my frustration by half.
The engineers who struggle most with AI are often the best engineers. The ones with the highest standards. The ones who notice every imperfection. AI rewards a different skill: the ability to extract value from imperfect output quickly, without getting emotionally invested in making it perfect.
This is the one that scares me most.
I noticed it during a design review meeting. Someone asked me to reason through a concurrency problem on the whiteboard. No laptop. No AI. Just me and a marker. And I struggled. Not because I didn’t know the concepts - I did. But because I hadn’t exercised that muscle in months. I’d been outsourcing my first-draft thinking to AI for so long that my ability to think from scratch had degraded.
It’s like GPS and navigation. Before GPS, you built mental maps. You knew your city. You could reason about routes. After years of GPS, you can’t navigate without it. The skill atrophied because you stopped using it.
The same thing is happening with AI and engineering thinking. When you always ask AI first, you stop building the neural pathways that come from struggling with a problem yourself. The struggle is where learning happens. The confusion is where understanding forms. Skip that, and you get faster output but shallower understanding.
I now deliberately spend the first hour of my day without AI. I think on paper. I sketch architectures by hand. I reason through problems the slow way. It feels inefficient. It is inefficient. But it keeps my thinking sharp, and that sharpness pays dividends for the rest of the day when I do use AI - because I can evaluate its output better when my own reasoning is warmed up.
Social media is full of people who seem to have AI figured out. They post their workflows. Their productivity numbers. Their “I built this entire app in 2 hours with AI” threads. And you look at your own experience - the failed prompts, the wasted time, the code you had to rewrite - and you think: what’s wrong with me?
Nothing is wrong with you. Those threads are highlight reels. Nobody posts “I spent 3 hours trying to get Claude to understand my database schema and eventually gave up and wrote the migration by hand.” Nobody posts “AI-generated code caused a production incident because it silently swallowed an error.” Nobody posts “I’m tired.”
The comparison trap is amplified by the fact that AI skill is hard to measure. With traditional engineering, you can look at someone’s code and roughly gauge their ability. With AI, the output depends on the model, the prompt, the context, the temperature, the phase of the moon. Someone’s impressive demo might not reproduce on your machine with your codebase.
I became much more selective about AI content on social media. I still follow the space closely - I have to, it’s my job. But I shifted from consuming everyone’s hot takes to focusing on people who are actually building and shipping, not just demoing. The ratio of signal to anxiety matters. If a feed is making you feel behind instead of informed, it’s not serving you.
I’ll be specific about what changed my relationship with AI from adversarial to sustainable.
Time-boxing AI sessions. I don’t use AI in an open-ended way anymore. I set a timer. 30 minutes for this task with AI. When the timer goes off, I ship what I have or switch to writing it myself. This prevents the prompt spiral and the perfectionism trap simultaneously.
Separating AI time from thinking time. Morning is for thinking. Afternoon is for AI-assisted execution. This isn’t rigid - sometimes I break the rule. But having a default structure means my brain gets both exercise and assistance in the right proportions.
Accepting 70% from AI. I stopped trying to get perfect output. 70% usable is the bar. I’ll fix the rest myself. This acceptance was the single biggest reducer of AI-related frustration in my workflow.
Being strategic about the hype cycle. I track the AI landscape because I build infrastructure for it. But I stopped adopting every new tool the week it launches. I use one primary coding assistant and know it deeply. I evaluate new tools when they’ve proven themselves over months, not days. Staying informed and staying reactive are different things.
Logging where AI helps and where it doesn’t. I kept a simple log for two weeks: task, used AI (yes/no), time spent, satisfaction with result. The data was revealing. AI saved me significant time on boilerplate, documentation, and test generation. It cost me time on architecture decisions, complex debugging, and anything requiring deep context about my codebase. Now I know when to reach for it and when not to.
Not reviewing everything AI produces. This was hard to accept. But if you’re using AI to generate large amounts of code, you physically cannot review every line with the same rigor. I focus my review energy on the parts that matter most - security boundaries, data handling, error paths - and rely on automated tests and static analysis for the rest. Some roughness in non-critical code is acceptable.
The tech industry has a burnout problem that predates AI. AI is making it worse, not better. Not because AI is bad, but because AI removes the natural speed limits that used to protect us.
Before AI, there was a ceiling on how much you could produce in a day. That ceiling was set by typing speed, thinking speed, the time it takes to look things up. It was frustrating sometimes, but it was also a governor. You couldn’t work yourself to death because the work itself imposed limits.
AI removed the governor. Now the only limit is your cognitive endurance. And most people don’t know their cognitive limits until they’ve blown past them.
I burned out in late 2025. Not dramatically - I didn’t quit or have a breakdown. I just stopped caring. Code reviews became rubber stamps. Design decisions became “whatever AI suggests.” I was going through the motions, producing more than ever, feeling less than ever. It took me a month to realize what had happened and another month to recover.
The recovery wasn’t about using less AI. It was about using AI differently. With boundaries. With intention. With the understanding that I am not a machine and I don’t need to keep pace with one. Working at Ona helped me see this clearly - when you’re building AI agent infrastructure for enterprise customers, you see the human cost of unsustainable AI workflows at scale. The problems aren’t just personal. They’re systemic. And they need to be solved at the tooling level, not just the individual level.
Ironically, the burnout period is when some of my best work happened. When I stopped trying to use every AI tool and started thinking about what was actually broken, I saw the problems clearly for the first time. Context windows filling up with garbage - that became Distill. Agents with all-or-nothing API key access - that became agentic-authz. The inability to audit what an agent actually did - that’s becoming AgentTrace. The fatigue forced me to stop consuming and start building. Not building more features faster, but building the right things deliberately.
Here’s what I think the real skill of the AI era is. It’s not prompt engineering. It’s not knowing which model to use. It’s not having the perfect workflow.
It’s knowing when to stop.
Knowing when the AI output is good enough. Knowing when to write it yourself. Knowing when to close the laptop. Knowing when the marginal improvement isn’t worth the cognitive cost. Knowing that your brain is a finite resource and that protecting it is not laziness - it’s engineering.
We optimize our systems for sustainability. We add circuit breakers. We implement backpressure. We design for graceful degradation. We should do the same for ourselves.
AI is the most powerful tool I’ve ever used. It’s also the most draining. Both things are true. The engineers who thrive in this era won’t be the ones who use AI the most. They’ll be the ones who use it the most wisely.
If you’re tired, it’s not because you’re doing it wrong. It’s because this is genuinely hard. The tool is new, the patterns are still forming, and the industry is pretending that more output equals more value. It doesn’t. Sustainable output does.
I’m still building in this space every day. Agent authorization, context engineering, audit trails, runtime security - the infrastructure that makes AI agents actually work in production. I’m more committed to AI than ever. But I’m committed on my terms, at my pace, building things that matter instead of chasing things that trend.
Take care of your brain. It’s the only one you’ve got, and no AI can replace it.
If this resonated, I’d love to hear your experience. What does AI fatigue look like for you? Find me on X or LinkedIn, or join the discussion on Hacker News.
I write about AI agent infrastructure, security, context engineering, and the human side of building with AI. You can find all my writing on my writing page.
...
Read the original on siddhantkhare.com »
I felt the familiar feeling of depression and lethargy creep in while my eyes darted from watching claude-code work and my phone. “What’s the point of it all?” I thought, LLMs can generate decent-ish and correct-ish looking code while I have more time to do what? doomscroll? This was the third time I gave claude-code a try. I felt the same feelings every single time and ended up deleting claude-code after 2-3 weeks, and whaddyouknow? Every. Single. Time. I rediscovered the joy of coding.
Yes, coding is not software engineering, but for me, it is a fun and essential part of it. In order to be effective at software engineering, you must be familiar with the problem space, and this requires thinking and wrestling with the problem. You can’t truly know the pain of using an API by just reading its documentation or implementation. You have to use it to experience it. The act of writing code, despite being slower, was a way for me to wrestle with the problem space, a way for me to find out that my initial ideas didn’t work, a way for thinking. Vibe coding interfered with that.
If you’re thinking without writing, you only think you’re thinking.
The other major part of the job is to ensure correctness. For me, it is much harder to verify the correctness of code I didn’t write compared to code I wrote. The process of writing code helps internalize the context and is easier for my brain to think deeply about it. If I outsource this to an LLM, I skip over the process of internalizing the problem domain and I can’t be certain that the generated code is correct.
By design, vibe coding has an addictive nature to it, you write some instructions, and code that looks correct is generated. Bam! Dopamine hit! If the code isn’t correct, then it’s just one prompt away from being correct, right? right?
Vibe coding also has the profound effect of turning my brain off and passively accepting changes. When it is time to use my brain, the inertia is much harder to overcome and it is easy to choose the lazy way out. At my lowest point, I even asked it to do a find-and-replace in a file. Something that takes a few seconds, now took minutes and a network call.
Even if I generate a 1,000 line PR in 30 minutes I still need to understand and review it. Since I am responsible for the code I ship, this makes me the bottleneck.
The common view of vibe coding is that it is neither good nor bad, it is a tool. But tools shape your workflow and your thought process, and if a tool prevents you from thinking deeply, I don’t think it is a good tool. If you are a knowledge worker, your core competency is your ability to think, and if a tool interferes with that, be afraid, be very afraid.
Now, I would be lying if I said I didn’t use LLMs to generate code. I still use Claude, but I do so in a more controlled manner. I copy-paste files that I think are necessary to provide the context, and then I copy-paste code and ask it to make changes to it or write tests for it. This friction has several benefits. I can’t make changes that span multiple files, this means the generated diff isn’t too large, and if I have to manually change other files I know how the code fits in. Manually giving claude the context forces me to be familiar with the codebase myself, rather than tell it to just “cook”. It turns code generation from a passive action to a deliberate thoughtful action. It also keeps my brain engaged and active, which means I can still enter the flow state. I have found this to be the best of both worlds and a way to preserve my happiness at work.
Ultimately, life is too short to not optimize for happiness. Maybe (a big maybe) generating entire features would make me more productive, but if it causes existential dread and makes me depressed, I don’t see it being productive in the long run. Maybe you relate to some of the feelings. Maybe you don’t. But don’t be afraid to choose differently.
...
Read the original on abhinavomprakash.com »
What if this is as good as software is ever going to be? What if AI stops getting better and what if people stop caring?
Imagine if this is as good as AI gets. If this is where it stops, you’d still have models that can almost code a web browser, almost code a compiler—and can even present a pretty cool demo if allowed to take a few shortcuts. You’d still get models that can kinda-sorta simulate worlds and write kinda-sorta engaging stories. You’d still get self-driving cars that almost work, except when they don’t. You get AI that can make you like 90% of a thing!
90% is a lot. Will you care about the last 10%?
I’m terrified of the good enough to ship—and I’m terrified of nobody else caring. I’m less afraid of AI agents writing apps that they will never experience than I am of the AI herders who won’t care enough to actually learn what they ship. And I sure as hell am afraid of the people who will experience the slop and will be fine with it.
As a woodworking enthusiast I am slowly making my peace with standing in the middle of an IKEA. But at the rate things are going in this dropshipping hell, IKEA would be the dream. Software temufication stings much more than software commoditization.
I think Claude and friends can help with crafting good software and with learning new technologies and programming languages—though I sure as hell move slower when I stop to learn and understand than the guy playing Dwarf Fortress with 17 agents. But at the same time AI models seem to constantly nudge towards that same median Next-React-Tailwind, good enough app. These things just don’t handle going off the beaten path well.
Spend all the tokens you want, trying to make something unique like Paper by FiftyThree with AI tools will just end up looking normal and uninspired.
Mind you, it’s not like slop is anything new. A lot of human decisions had to happen before your backside ended up in an extremely uncomfortable chair, your search results got polluted by poorly-written SEO-optimized articles, and your brain had to deal with a ticket booking website with a user interface so poorly designed that it made you cry. So it’s a people problem. Incentives just don’t seem to align to make good software. Move fast and break things, etc, etc. You’ll make a little artisan app, and if it’s any good, Google will come along with a free clone, kill you, then kill its clone—and the world will be left with net zero new good software. And now, with AI agents, it gets even worse as agent herders can do the same thing much faster.
Developers aside, there’s also the users. AI models can’t be imaginative, and the developers can’t afford to, but surely with AI tools, the gap between users and developers will be bridged, ChatGPT will become the new HyperCard and people will turn their ideas into reality with just a few sentences? There’s so many people out there who are coding without knowing it, from Carol in Accounting making insane Excel spreadsheets to all the kids on TikTok automating their phones with Apple Shortcuts and hacking up cool Notion notebooks.
But what if those people are an aberration? What if this state of tech learned helplessness cannot be fixed? What if people really do just want a glorified little TV in their pocket? What if most people truly just don’t care about tech problems, about privacy, about Liquid Glass, about Microsoft’s upsells, about constantly dealing with apps and features which just don’t work? What if there will be nobody left to carry the torch? What if the future of computing belongs not to artisan developers or Carol from Accounting, but to whoever can churn out the most software out the fastest? What if good enough really is good enough for most people?
I’m terrified that our craft will die, and nobody will even care to mourn it.
...
Read the original on ezhik.jp »
Not sure if it’s just me, but I often get a primal satisfaction whenever I see intricate patterns emerging out of seemingly disordered environments.
Think about the galleries of ant colonies, the absurdly perfect hexagons of honeycombs, or the veins on a leaf. No architect, no blueprint. Just simple rules stacking on each other that result in beautiful patterns. I can’t explain why, but seeing those structures always felt good.
Humans do this too. And for me, one of the most fascinating patterns we’ve come up with is the roads.
Sometimes I imagine aliens from faraway galaxies discovering Earth long after we’re gone. Forests reclaimed by nature, cities reduced to rubble, yet between them, a faintly pattern is still visible - the road network. I like to think they will feel the same way I do when looking at nature patterns. - “Man, someone really thought this through.”
I’ve got to say, roads have fascinated me since I was a kid.
I still remember playing SimCity 2000 for the first time when I was about five or six years old. I didn’t understand much. Definitely didn’t know what zoning, taxes, or demand were. But roads fascinated me from the start.
I think roads lie at the heart of every city builder. It’s the fabric on which cities are built. Since that moment, I’ve played almost every modern-themed city builder out there. In the meantime, I’ve also started noticing them in the real world. Examining them in more detail.
Despite every game bringing an improvement over the one before, something always felt… off.
SimCity 4 added elevation and diagonal roads. SimCity 2013 introduced curved roads. Then came Cities: Skylines with a ton of freedom. You could know freeplace roads and merge them into intersections at any angle, build flyovers at different elevations to construct crazy, yet unrealistic, interchanges. I think this was the largest breakthrough.
But something was still nagging me. Highway ramps were unrealistically sharp or wobbly, lanes that were supposed to be high-speed bent too sharply at certain points, and the corner radii of intersections looked strange.
I mean look at this. This is probably what highway engineers have nightmares about.
And then came the mods. Mods changed everything. The great community enabled a new kind of freedom. One could build almost anything: perfect merge lanes, realistic markings, and smooth transitions. It was a total game-changer. I am particularly proud of this 5-lane turbo roundabout:
But even then, mods didn’t feel completely natural. They were still limited by the game’s original system.
Cities: Skylines 2 pushed it even further, with lanes becoming even more realistic and markings as well. I think at this point, a non-trained eye won’t know the difference from reality.
Then I stopped stumbling around and started asking why? I tried to understand how engineers design roads and how game developers code them.
That’s when I ran straight into the fundamental issue - right at the base of it. And it comes to something every developer knows about and loves:
If you’re a Unity or Unreal developer or played with basically any vector graphics editing software, you already know them well. Bezier curves are an elegant, intuitive, and incredibly powerful way to smoothly interpolate between two points while taking into consideration some direction of movement (the tangent).
That’s exactly what roads are supposed to do, right? Of course, developers naturally think they are the perfect tool.
They’ve got their beauty, I need to admit. But hidden beneath the surface lies an uncomfortable truth.
You see, the shapes of roads in real life come from an underlying essential fact: the wheel axles of a vehicle. No matter how you drive a car, the distance between the left and right wheels remains constant. You can notice this in tyre tracks in snow or sand. Two perfectly parallel paths, always the same distance apart maintaining a consistent curved shape.
Here’s the issue with Bezier splines: they don’t preserve shape and curvature when offset.
At gentle curves, they kinda look fine, but once you have tighter bends, the math falls apart. In mathy terms: The offset of a Bezier curve is not a Bezier curve.
When game engines try to generate a road mesh along a Bezier spline, the geometry often fails at tight angles. The inner edge curves at a different rate than the outer edge. This creates “pinching,” self-intersecting geometry.
Here is the best example of how they start to fail in extreme scenarios.
To sum up: Bézier curves are unconstrained. The freedom they enable is exactly the “Achilles’ heel”. Real roads are engineered with the constraints of real motion in mind. A car’s path can’t magically self-intersect.
Ok, so what preserves parallelism? If you’ve already been through kindergarten, you’re already familiar with it: It’s the CIRCLE.
It has almost like a magical property: no matter how much you offset it, the result is still a circular arc. Perfectly parallel with the initial one. So satisfying.
Scrapping Bezier curves for Circle Arcs also yields a nice, unexpected bonus. To procedurally build intersections, the engine has to perform many curve-curve intersection operations multiple times per frame. The intersection between two Bezier curves is notoriously complex. On one side, you have polynomial root finding, iterative numerical methods, de Castelaju’s method + bounding boxes, and multiple convergence checks vs a simple, plain O(1) formula in Circle Arcs.
By stitching together circular arcs of different radii, you can create any shape while adhering to proper engineering principles.
But this is not the end of the story. Circle arcs have issues as well (Oh no). The problem with circles in infrastructure is that they have constant curvature. What this means is that when entering a circular curve from a straight line, the lateral force jumps from 0 to a fixed constant value (determined by the radius of the circle). If you were in a car or train entering at high speed into this kind of curve, it would feel terrible.
Civil engineers have to account for this as well. So then, what curve maintains parallelism when offset and has a smoothly increasing curvature?
Introduce you to: transition curves - most famously, the clothoid.
A clothoid gradually increases curvature over distance. You start almost straight, then slowly turn tighter and tighter. The steering wheel rotates smoothly. The forces ramp up naturally, and a passenger’s body barely notices the transition.
These curves provide comfortable rides at high speeds by maintaining parallel offsets and continuous curvature changes.
And they are also… a math nightmare. Differential geometry. Integrals. Oh my… Which is probably why most games don’t even dare.
Vehicles move slowly on city streets. For intersections of urban roads, circular arcs are more than a decent choice.
Does everything I just rambled about matter? Do 99% of city-builder players care what shape the corner radius of the intersection has? Most likely, no. Then why bother?
First, because of curiosity. As any other nerd overly obsessed with the nitty-gritty details of a very specific subject, I just wanted to see how I would implement it. Like challenging the status quo.
Second, even if established titles might not accurately render roads, they are still light-years ahead of what solutions an indie developer can find online. The tutorials and assets for this are just sad. I personally got bored with grids, and I just wanted to built a better solution to share with anyone who wants to build a city builder.
In the next blog post, I’ll discuss more technicalities and dive into how I’ve built my own solution. If you want to follow along or get notified when I release this asset, scribble your email below.
...
Read the original on sandboxspirit.com »
A friend of mine recently attended an open forum panel about how engineering orgs can better support their engineers. The themes that came up were not surprising:
Sacrificing quality makes it hard to feel proud of the work. No acknowledgement of current velocity. If we sprint to deliver, the expectation becomes to keep sprinting, forever.
I’ve been hearing variations of this for a while now, but now I’m also hearing and agreeing with “AI doesn’t always speed us up”.
Developers used to google things. You’d read a StackOverflow answer, or an article, or a GitHub issue. You did some research, verified it against your own context, and came to your own conclusion. Nobody said “Google did it for me” or “it was the top result so it must be true.”
Now I’m starting to hear “AI did it for me.”
That’s either overhyping what happened, or it means the developer didn’t come to their own conclusion. Both are bad. If someone on my team ever did say Google wrote their code because they copied a StackOverflow answer, I’d be worried about the same things I’m worried about now with AI: did you actually understand what you pasted?
Vibe coding is fun. At first. For prototyping or low-stakes personal projects, it’s useful. But when the stakes are real, every line of code has consequences.
On a personal project, I asked an AI agent to add a test to a specific file. The file was 500 lines before the request and 100 lines after. I asked why it deleted all the other content. It said it didn’t. Then it said the file didn’t exist before. I showed it the git history and it apologised, said it should have checked whether the file existed first. (Thank you git).
Now imagine that in a healthcare codebase instead of a side project.
AI assistance can cost more time than it saves. That sounds backwards, but it’s what happened here. I spent longer arguing with the agent and recovering the file than I would have spent writing the test myself.
Using AI as an investigation tool, and not jumping straight to AI as solution provider, is a step that some people skip. AI-assisted investigation is an underrated skill that’s not easy, and it takes practice to know when AI is wrong. Using AI-generated code can be effective, but if we give AI more of the easy code-writing tasks, we can fall into the trap where AI assistance costs more time than it saves.
Most people miss this about AI-assisted development. Writing code is the easy part of the job. It always has been. The hard part is investigation, understanding context, validating assumptions, and knowing why a particular approach is the right one for this situation. When you hand the easy part to AI, you’re not left with less work. You’re left with only the hard work. And if you skipped the investigation because AI already gave you an answer, you don’t have the context to evaluate what it gave you.
Reading and understanding other people’s code is much harder than writing code. AI-generated code is other people’s code. So we’ve taken the part developers are good at (writing), offloaded it to a machine, and left ourselves with the part that’s harder (reading and reviewing), but without the context we’d normally build up by doing the writing ourselves.
My friend’s panel raised a point I keep coming back to: if we sprint to deliver something, the expectation becomes to keep sprinting. Always. Tired engineers miss edge cases, skip tests, ship bugs. More incidents, more pressure, more sprinting. It feeds itself.
This is a management problem, not an engineering one. When leadership sees a team deliver fast once (maybe with AI help, maybe not), that becomes the new baseline. The conversation shifts from “how did they do that?” to “why can’t they do that every time?”
My friend was saying:
When people claim AI makes them 10x more productive, maybe it’s turning them from a 0.1x engineer to a 1x engineer. So technically yes, they’ve been 10x’d. The question is whether that’s a productivity gain or an exposure of how little investigating they were doing before.
Burnout and shipping slop will eat whatever productivity gains AI gives you. You can’t optimise your way out of people being too tired to think clearly.
I’ve used the phrase “AI is senior skill, junior trust” to explain how AI coding agents work in practice. They’re highly skilled at writing code but we have to trust their output like we would a junior engineer. The code looks good and probably works, but we should check more carefully because they don’t have the experience.
Another way to look at it: an AI coding agent is like a brilliant person who reads really fast and just walked in off the street. They can help with investigations and could write some code, but they didn’t go to that meeting last week to discuss important background and context.
Developers need to take responsible ownership of every line of code they ship. Not just the lines they wrote, the AI-generated ones too.
If you’re cutting and pasting AI output because someone set an unrealistic velocity target, you’ve got a problem 6 months from now when a new team member is trying to understand what that code does. Or at 2am when it breaks. “AI wrote it” isn’t going to help you in either situation.
The other day there was a production bug. A user sent an enquiry to the service team a couple of hours after a big release. There was an edge case timezone display bug. The developer who made the change had 30 minutes before they had to leave to teach a class, and it was late enough for me to already be at home. So I used AI to help investigate, letting it know the bug must be based on recent changes and explaining how we could reproduce. Turned out some deprecated methods were taking priority over the current timezone-aware ones, so the timezone was never converting correctly. Within 15 minutes I had the root cause, a solution idea, and investigation notes in the GitHub issue. The developer confirmed the fix, others tested and deployed, and I went downstairs to grab my DoorDash dinner.
No fire drill. No staying late. AI did the investigation grunt work, I provided the context and verified, the developer confirmed the solution. That’s AI helping with the hard part.
...
Read the original on www.blundergoat.com »
Check out the code, download the ROMsMaking it work on the Game BoyThe Game Boy has no multiply instructionAll scalars and lookups are 8-bit fractionsHow fast is it?An overall failed attempt at using AI
Check out the code, download the ROMsMaking it work on the Game BoyThe Game Boy has no multiply instructionAll scalars and lookups are 8-bit fractionsHow fast is it?An overall failed attempt at using AI
I made a Game Boy Color game that renders images in real time. The player controls an orbiting light and spins an object.
Before really diving into this project, I experimented with the look in Blender to see if it would even look good. IMO it did, so I went ahead with it!
I experimented with a “pseudo-dither” on the Blender monkey by adding a small random vector to each normal.
It doesn’t really matter what software I used to produce the normal maps. Blender was the path of least resistance for me, so I chose that.
For the teapot, I simply put in a teapot, rotated a camera around it, and exported the normal AOV as a PNG sequence. Pretty straight-forward.
For the spinning Game Boy Color, I wanted to ensure that certain colors were solid, so I used cryptomattes in the compositor to identify specific geometry and output hard-coded values in the output.
The geometry in the screen was done by rendering a separate scene, then compositing it in the final render using a cryptomatte for the screen.
The above animations are normal map frames that are used to solve the value of each pixel
Normal maps are a core concept of this project. They’re already used everywhere in 3D graphics.
And indeed, normal map images are secretly a vector field. The reason normal maps tend to have a blue-ish baseline color, is because everyone likes to associate XYZ with RGB, and +Z is the forward vector by convention.
In a typical 3D workflow, a normal map is used to encode the normal vector at any given point on a textured mesh.
The simplest way to shade a 3D object is using the dot product:
where is the normal vector, and is the light position when it points towards the origin (or equivalently: the negative light direction).
Expanded out component-wise, this is:
When the light vector is constant for all pixels, it models what most 3D graphics software calls a “distant light”, or a “sun light”.
To speed up computation on the Game Boy, I use an alternate version of the dot product, using spherical coordinates.
A spherical coordinate is a point represented by a radius , a primary angle “theta”, and a secondary angle “phi”. This is represented as a tuple:
The dot product of two spherical coordinates:
Because all normal vectors are unit length, and the light vector is unit length, we can just assume the radius is equal to 1. This simplifies to:
And using the previous variable names, we get the formula:
In the ROM, I decided to fix “L-theta” to a constant value for performance reasons. The player gets to control “L-phi”, creating an orbiting light effect.
This means that we can extract constant coefficients and and rewrite the formula:
The ROM encodes each pixel as a 3-byte tuple of .
Not only does the SM83 CPU not support multiplication, but it also doesn’t support floats. That’s a real bummer.
We have to get really creative when the entire mathematical foundation of this project involves multiplying non-integer numbers.
What do we do instead? We use logarithms and lookup tables!
Logarithms have this nice property of being able to factor products to outside the . This way, we can add values instead!
This requires two lookups: a log lookup, and a pow lookup.
In pseudocode, multiplying 0.3 and 0.5 looks like this:
pow = [ … ] # A 256-entry lookup table
# float_to_logspace() is compile-time. Accepts -1.0 to +1.0.
# x and y are 8-bit values in log-space
x = float_to_logspace(0.3)
y = float_to_logspace(0.5)
result = pow[x + y]
One limitation of this is that it’s not possible to take the log of a negative number. e.g. has no real solution.
We can overcome this by encoding a “sign” bit in the MSB of the log-space value. When adding two log-space values together, the sign bit is effectively XOR’d (toggled). We just need to ensure the remaining bits don’t overflow into it. We ensure this by keeping the remaining bits small enough.
The pow lookup accounts for this bit and returns a positive or negative result based on it.
It’s advantageous to restrict numbers to a single byte, for both run-time performance and ROM size. 8-bit fractions are pretty extreme by today’s standards, but believe it or not, it works. It’s lossy as hell, but it works!
All scalars we’re working with are between -1.0 and +1.0.
Addition and multiplication both use… addition!
Consider adding the two bytes: 5 + 10 = 15
Why is the denominator 127 instead of 128? It’s because I needed to represent both positive and negative 1. In a two’s-complement encoding, signed positive 128 doesn’t exist.
You might notice that the log-space values cycle and become negative at byte 128. The log-space values use bit 7 of the byte to encode the “sign” bit. As mentioned in the previous section, this is important for toggling the sign during multiplication.
The log-space values also use as a base, because I chose this as a sufficiently small base to meet the requirement that adding 3 of these log-space values won’t overflow (42+42+42 = 126). Bytes 43 thru 127 are near 0, so in practice the ROM doesn’t encode these values.
The lookup tables look like this:
Reconstructed functions look like this. The precision error is shown in the jagged “staircase” patterns:
It may look like there’s a lot of error, but it’s fast and it’s passable enough to look alright! ;)
It’s basically a combined . This exists because in practice, cosine is always used with a multiplication.
The core calculation for the shader is:
And we can rewrite it as:
The procedure processes 15 tiles per frame. It can process more if some of the tile’s rows are empty (all 0), but it’s guaranteed to process at least 15.
Figure: Mesen’s “Event Viewer” window, showing a dot for each iteration (tile row) of the shader’s critical loop.
There’s some intentional visual tearing as well. The image itself is more than 15 tiles, so the ROM actually switches to rendering different portions of the image for each frame. The tearing is less noticeable because of ghosting on the LCD display, so I thought it was acceptable.
A pixel takes about 130 cycles, and an empty row’s pixel takes about 3 cycles.
At one point I had calculated 15 tiles rendering at exactly 123,972 cycles, including the call and branch overhead. This is an overestimate now, because I since added an optimization for empty rows.
The Game Boy Color’s CPU runs up to 8.388608 MHz, or roughly 139,810 T-cycles per frame (1/60 of a second).
About 89% of a frame’s available CPU time goes to rendering the 15 tiles per frame. The remaining time goes to other functionality like responding to user input and performing hardware IO.
Figure: A hex representation of the shader subroutine instructions in RAM. The blue digits show a patch to change sub a, 0 into sub a, 8.
The core shader subroutine contains a hot path that processes about 960 pixels per frame. It’s really important to make this as fast as possible!
Self-modifying code is a super-effective way to make code fast. But most modern developers don’t do this anymore, and there are good reasons: It’s difficult, rarely portable, and it’s hard to do it right without introducing serious security vulnerabilities. Modern developers are spoiled by an abundance of processing power, super-scalar processors that take optimal paths, and modern JIT (Just-In-Time) runtimes that generate code on the fly. But we’re on the Game Boy, baybeee, so we don’t have those options.
If you’re a developer who uses higher-level languages like Python and JavaScript, the closest equivalent to self-modifying code is eval(). Think about how nervous eval() makes you feel. That’s almost exactly how native developers feel about modifying instructions.
On the Game Boy’s SM83 processor, it’s faster to add and subtract by a hard-coded number than it is to load that number from memory.
unsigned char Ltheta = 8;
// Slower
v = (*in++) - Ltheta;
// Faster
v = (*in++) - 8;
In SM83 assembly, this looks like:
; Slower: 28 cycles
ld a, [Ltheta] ; 12 cycles: Read variable “Ltheta” from HRAM
ld b, a ; 4 cycles: Move value to B register
ld a, [hl+] ; 8 cycles: Read from the HL pointer
sub a, b ; 4 cycles: A = A - B
; Faster: 16 cycles
ld a, [hl+] ; 8 cycles: Read from the HL pointer
sub a, 8 ; 8 cycles: A = A - 8
The faster way shaves off 12 cycles. If we’re rendering 960 pixels, this saves a total of 11,520 cycles. This doesn’t sound like a lot, but it’s roughly 10% of the shader’s runtime!
“AI Will Be Writing 90% of Code in 3 to 6 Months”
— Dario Amodei, CEO of Anthropic (March 2025 - 9 months ago as of writing)
95% of this project was made by hand. Large language models struggle to write Game Boy assembly. I don’t blame them.
Update: 2026-02-03: I attempted to use AI to try out the process, mostly because 1) the industry won’t shut up about AI, and 2) I wanted a grounded opinion of it for novel projects, so I have a concrete and personal reference point when talking about it in the wild. At the end of the day, this is still a hobbyist project, so AI really isn’t the point! But still…
I believe in disclosing all attempts or actual uses of generative AI output, because I think it’s unethical to deceive people about the process of your work. Not doing so undermines trust, and amounts to disinformation or plagiarism. Disclosure also invites people who have disagreements to engage with the work, which they should be able to. I’m open to feedback, btw.
I’ll probably write something about my experiences with AI in the future.
As far as disclosures go, I used AI for:
Python: Reading OpenEXR layers, as part of a conversion script to read normal map data
Python/Blender: Some Python scripts for populating Blender scenes, to demo the process in Blender
SM83 assembly: Snippets for Game Boy Color features like double-speed and VRAM DMA. Unsurprising, because these are likely available somewhere else.
I attempted - and failed - to use AI for:
SM83 assembly: (Unused) Generating an initial revision of the shader code
I’ll also choose to disclose what I did NOT use AI for:
The algorithms, lookups, all other SM83 assembly
The soul 🌟 (AI techbros are groaning right now)
Just to see what it would do, I fed pseudocode into Claude Sonnet 4 (the industry claims that it’s the best AI model for coding in 2025), and got it to generate SM83 assembly:
It was an interesting process. To start, I chewed Claude’s food and gave it pseudocode, because I had a data format in mind, and I assumed it’d struggle with a higher-level description.
I was skeptical that it wouldn’t do well, but it did better than I thought it would. It even produced code that worked when I persisted it and guided it enough. However, it wasn’t very fast, and it made some initial mistakes by assuming the SM83 processor was the Z80 processor. I attempted to get Claude to optimize it by offering suggestions. It did well initially, but it introduced errors until I reached the conversation limit.
After that point, I manually rewrote everything. My final implementation is aggressively optimized and barely has any resemblance to Claude’s take.
And it loved telling me how “absolutely right” I always was. 🥺
...
Read the original on blog.otterstack.com »
Clipboard, Search History, and several other advanced features are temporarily unavailable.
Skip to main page content
An official website of the United States government
The .gov means it’s official.
Federal government websites often end in .gov or .mil. Before sharing sensitive information, make sure you’re on a federal government site.
The site is secure.
The https:// ensures that you are connecting to the official website and that any information you provide is encrypted and transmitted securely.
Email address has not been verified. Go to
My NCBI account settings
to confirm your email and then refresh this page.
Name must be less than 100 characters
Unable to load your collection due to an error
Please try again
Unable to load your delegates due to an error
Please try again
Would you like email updates of new search results?
Send even when there aren’t any new results
Name must be less than 100 characters
Unable to load your collection due to an error
Please try again
Blood omega-3 is inversely related to risk of early-onset dementia
2 Fatty Acid Research Institute, Sioux Falls, SD, USA; Department of Population Health Nursing Science, College of Nursing, University of Illinois-Chicago, Chicago, IL, USA.
4 Fatty Acid Research Institute, Sioux Falls, SD, USA; Department of Internal Medicine, Sanford School of Medicine, University of South Dakota, Sioux Falls, SD, USA.
Blood omega-3 is inversely related to risk of early-onset dementia
2 Fatty Acid Research Institute, Sioux Falls, SD, USA; Department of Population Health Nursing Science, College of Nursing, University of Illinois-Chicago, Chicago, IL, USA.
4 Fatty Acid Research Institute, Sioux Falls, SD, USA; Department of Internal Medicine, Sanford School of Medicine, University of South Dakota, Sioux Falls, SD, USA.
Background & aims:
Early-onset dementia (EOD, defined as diagnosis < age 65) imposes a high socio-economic burden. It is less prevalent and less investigated than late-onset dementia (LOD). Observational data indicate that many EOD cases are associated with potentially modifiable risk factors, yet the relationship between diet and EOD has been under-explored. Omega-3 fatty acids are promising dietary factors for dementia prevention; however, existing research has primarily focused on cohorts aged >65. We examined the associations between omega-3 blood levels (which objectively reflect dietary intake) and incident EOD by leveraging data from the UK Biobank cohort.
Methods:
We included participants aged 40-64, free of dementia at baseline and for whom plasma omega-3 levels and relevant covariates were available. We modeled the relationships between the three omega-3 exposures (total omega-3, DHA, and non-DHA omega-3) and incident EOD with quintiles (Q) and continuous linear relationships. We constructed Cox proportional hazards adjusting for sex, age at baseline and APOE-ε4 allele load, besides other lifestyle variables reported to relate to incident EOD. We also assessed the interaction between each exposure of interest and APOE-ε4 allele load.
Results:
The study included 217,122 participants. During the mean follow-up of 8.3 years, 325 incident EOD cases were ascertained. Compared to participants at Q1 of total omega-3, those at Q4 and Q5 showed a statistically significantly lower risk of EOD (Q4, hazard ratio [95 % confidence interval] = 0.62 [0.43, 0.89]; Q5, 0.60 [0.42, 0.86]). A statistically significant inverse association was also observed for total omega-3 as a continuous variable. Compared to participants at Q1 of DHA, those at Q5 of non-DHA showed a significant lower risk of EOD. A statistically significant lower risk was observed in Q3, Q4 and Q5 of non-DHA omega-3. Finally, we observed no evidence of interaction omega-3 × APOE-ε4 allele load.
Conclusions:
This study expands the evidence of a beneficial association of omega-3 and LOD to EOD as well. These findings suggest that an increased intake of omega-3 fatty acids earlier in life may slow the development of EOD. Additional research is needed to confirm our findings, particularly in more diverse populations.
Copyright © 2025 Elsevier Ltd and European Society for Clinical Nutrition and Metabolism. All rights reserved.
...
Read the original on pubmed.ncbi.nlm.nih.gov »
Anthropic recently published a blog post about building a C compiler entirely with Claude
. They called it CCC (Claude’s C Compiler) and claimed it could compile the Linux kernel. 100% of the code was written by Claude Opus 4.6, a human only guided the process by writing test cases. That sounded interesting enough to test the claim and benchmark CCC against the industry standard GCC.
The source code of CCC is available at claudes-c-compiler
. It is written entirely in Rust, targeting x86-64, i686, AArch64 and RISC-V 64. The frontend, SSA-based IR, optimizer, code generator, peephole optimizers, assembler, linker and DWARF debug info generation are all implemented from scratch with zero compiler-specific dependencies. That is a lot of work for an AI to do.
Before we jump into the comparison, it helps to understand what happens when you compile a C program. There are four stages involved.
Image credit: The four stages of the gcc compiler
Preprocessor: Handles #include, #define and other directives. It takes the source code and produces expanded source code.
Compiler: Takes the preprocessed source code and translates it into assembly language. This is where the real heavy lifting happens, understanding the C language, type checking, optimizations, register allocation and so on.
Assembler: Converts the assembly language into machine code (object files). It has to know the exact instruction encoding for the target CPU architecture.
Linker: Takes one or more object files and combines them into a single executable. It resolves references between files, sets up memory layout and produces the final binary.
Writing a programming language is hard (prior vibe coding). Writing a compiler is on another level entirely. A programming language defines the rules. A compiler has to understand those rules, translate them into machine instructions, optimize the output for speed and size, handle edge cases across different CPU architectures and produce correct code every single time.
GCC has been in development since 1987. That is close to 40 years of work by thousands of contributors. It supports dozens of architectures, hundreds of optimization passes and millions of edge cases that have been discovered and fixed over the decades. The optimization passes alone (register allocation, function inlining, loop unrolling, vectorization, dead code elimination, constant propagation) represent years of PhD-level research. This is one of the reasons why it’s ubiquitous.
This is why CCC being able to compile real C code at all is noteworthy. But it also explains why the output quality is far from what GCC produces. Building a compiler that parses C correctly is one thing. Building one that produces fast and efficient machine code is a completely different challenge.
Ironically, among the four stages, the compiler (translation to assembly) is the most approachable one for an AI to build. It is mostly about pattern matching and rule application: take C constructs and map them to assembly patterns.
The assembler is harder than it looks. It needs to know the exact binary encoding of every instruction for the target architecture. x86-64 alone has thousands of instruction variants with complex encoding rules (REX prefixes, ModR/M bytes, SIB bytes, displacement sizes). Getting even one bit wrong means the CPU will do something completely unexpected.
The linker is arguably the hardest. It has to handle relocations, symbol resolution across multiple object files, different section types, position-independent code, thread-local storage, dynamic linking and format-specific details of ELF binaries. The Linux kernel linker script alone is hundreds of lines of layout directives that the linker must get exactly right.
The Linux kernel is one of the most complex C codebases in the world. It has millions of lines of code, uses GCC-specific extensions, inline assembly, linker scripts and countless tricks that push the compiler to its limits. It is not a good first test for a new compiler.
SQLite, on the other hand, is distributed as a single amalgamation file (one big .c file). It is standard C, well-tested and self-contained. If your compiler can handle SQLite, it can handle a lot. If it cannot handle SQLite correctly, there is no point testing anything bigger.
That is why I tested both. SQLite tells us about correctness and runtime performance. The kernel tells us about scale and compatibility.
CCC was built with the gcc_m16 Cargo feature, which delegates 16-bit real-mode boot code (-m16 flag) to GCC. This is needed because CCC’s i686 backend produces code too large for the 32KB real-mode limit. The x86_64 C code is compiled entirely by CCC.
A ccc_wrapper.sh script routes .S assembly files to GCC (CCC does not process assembly) and all .c files to CCC.
Compilers are usually measured on below scenarios. Hence, tests are also designed around them.
Same hardware — identical VM specs for both compilers
Both run to completion — no tests killed prematurely
CCC gets help where needed — gcc_m16 feature for boot code, wrapper for assembly files
Same benchmark script — benchmark_sqlite.sh runs identically on both VMs
The benchmark was designed to be CPU-bound:
* No correlated subqueries (O(n^2) queries were replaced with GROUP BY)
The fair comparison is CCC vs GCC at -O0 (no optimization): CCC takes 87s vs GCC’s 65s — CCC is 1.3x slower. The “5x faster” number only appears because GCC is doing 7 minutes of optimization work that CCC simply skips.
CCC compiled every single C source file in the Linux 6.9 kernel without a single compiler error (0 errors, 96 warnings). This is genuinely impressive for a compiler built entirely by an AI.
However, the build failed at the linker stage with around 40,784 undefined reference errors. The errors follow two patterns:
__jump_table relocations — CCC generates incorrect relocation entries for kernel jump labels (used for static keys/tracepoints)
These are linker-visible bugs in CCC’s relocation/symbol generation, not C language compilation bugs. This is a good example of why the linker is the hardest part. The compiler did its job fine, but the generated relocations were not quite right for the kernel’s complex linker script.
CCC -O0 and -O2 produce byte-identical binaries (4,374,024 bytes). CCC has 15 SSA optimization passes, but they all run at every optimization level. There is no tiered optimization — the -O flag is accepted but completely ignored.
When you ask GCC to compile with -O2, it performs dozens of extra optimization passes:
* Register allocation: fitting variables into CPU registers so they do not spill to slow memory
* Vectorization: using SIMD instructions (SSE/AVX) to process multiple values at once
GCC’s -O2 spends 7 minutes doing this work, and the payoff is clear: the resulting binary runs 1.7x faster (6.1s vs 10.3s).
CCC does none of this at any optimization level. Comparing “CCC compile time vs GCC -O2 compile time” is like comparing a printer that only prints in black-and-white vs one that does full color. The black-and-white printer is faster, but it isn’t doing the same job.
CCC-compiled SQLite is functionally correct — it produces the same query results as GCC-compiled SQLite. All 5 crash/edge-case tests passed. But it is very slow.
No failures observed during these tests:
The per-query breakdown shows that CCC’s slowdown is not uniform. Simple queries are only 1-7x slower, but complex operations involving nested loops blow up:
The pattern is clear: operations that involve nested iteration (subqueries, JOINs) are orders of magnitude slower, while simple sequential operations are only slightly slower.
Modern CPUs have a small set of fast storage locations called registers. A good compiler tries to keep frequently used variables in these registers. When there are more variables than registers, the compiler “spills” them to the stack (regular RAM), which is much slower.
CCC’s biggest performance problem is excessive register spilling. SQLite’s core execution engine sqlite3VdbeExec is a single function with 100+ local variables and a massive switch statement. CCC does not have good register allocation, so it spills almost all variables to the stack.
movq -0x1580(%rbp), %rax ; load from deep stack offset
movq %rax, -0x2ae8(%rbp) ; store to another deep stack offset
movq -0x1588(%rbp), %rax ; load next value
movq %rax, -0x2af0(%rbp) ; store to next offset
; … dozens more memory-to-memory copies
CCC uses stack offsets up to -0x2ae8 (11,000 bytes deep) for a function with 32 variables. Every operation goes: stack -> rax -> stack, using %rax as a shuttle register.
CCC is 4.2x slower than GCC O0 for register-heavy code. In sqlite3VdbeExec with 100+ variables and 200+ switch cases, this ratio compounds to 100x+.
CCC runs the same 15-pass SSA pipeline at all optimization levels:
This means -O2 provides zero benefit. Every binary CCC produces is effectively -O0 quality, regardless of what flag you pass.
The 2.78x code bloat means more instruction cache misses, which compounds the register spilling penalty.
CCC-compiled binaries lack internal function symbols (nm reports 0 symbols, readelf shows only 90 PLT stubs vs GCC’s 1,500+ functions). This makes profiling and debugging impossible.
The NOT IN (subquery) pattern causes SQLite to execute a nested loop: for each of the around 100,000 rows in the outer table, it scans through around 10,000 rows in the inner table. That is roughly 1 billion iterations through SQLite’s main execution function (sqlite3VdbeExec), which is basically a giant switch statement.
With CCC’s roughly 4x per-iteration overhead from register spilling, plus extra cache misses from the 2.78x larger binary (the CPU cannot keep all the instructions in its fast cache), the slowdown compounds:
* Cache pressure: around 2-3x additional penalty (instructions do not fit in L1/L2 cache)
This is why simple queries (INSERT, DROP TABLE) are only 1-2x slower, but nested operations blow up to 100,000x+ slower.
Correctness: Compiled every C file in the kernel (0 errors) and produced correct SQLite output for all queries
Stability: Zero crashes, zero segfaults across all tests
Memory usage: 5.9x more RAM for compilation (1.6 GB vs 272 MB for SQLite)
Compilation speed: Could only be compared with -O0 as CCC does not do anything beyond this. CCC is around 25% slower vs GCC (87s vs 65s)
Within hours of Anthropic releasing CCC, someone opened issue #1
– “Hello world does not compile”. The example straight from the README did not work on a fresh Fedora or Ubuntu install:
$ ./target/release/ccc -o hello hello.c
/usr/include/stdio.h:34:10: error: stddef.h: No such file or directory
/usr/include/stdio.h:37:10: error: stdarg.h: No such file or directory
ccc: error: 2 preprocessor error(s) in hello.c
Meanwhile, GCC compiled it just fine. The issue was that CCC’s preprocessor did not search the right system include paths for stddef.h and stdarg.h (these come from the compiler, not the C library). It got 288 thumbs-up reactions, over 200 comments and turned into one of those legendary GitHub threads where people tag @claude asking it to fix the bug, ask @grok for summaries and post comments like “my job is safe”.
Someone got it working on Compiler Explorer and remarked that the assembly output “reminds me of the quality of an undergraduate’s compiler assignment”. Which, to be fair, is both harsh and not entirely wrong when you look at the register spilling patterns.
The issue is still open at the time of writing.
Claude’s C Compiler is a remarkable achievement. It is a working C compiler built entirely by an AI that can correctly compile 2,844 files from the Linux kernel without a single error. It produces functionally correct code (verified with SQLite — all queries return correct results, all crash tests pass).
But it is not ready for real use:
The output code is very slow. CCC-compiled SQLite takes 2 hours to run a benchmark that GCC finishes in 10 seconds. The root cause is poor register allocation — CCC uses a single register as a shuttle to move values between stack locations, turning every operation into multiple memory accesses.
The “compiles the kernel” claim needs a footnote. CCC compiles all the C source files, but the final binary cannot be produced because CCC generates incorrect relocations for kernel data structures (__jump_table, __ksymtab).
Optimization flags are decorative. Passing -O2 or -O3 to CCC does literally nothing — the output binary is byte-identical to -O0.
For Anthropic’s stated goal of demonstrating that Claude can build complex software, CCC is a genuine success. For anyone wanting to compile software to actually run efficiently, GCC (or Clang, or any production compiler) remains the only real option.
All scripts, results and graphs are available at compare-claude-compiler
Part of this work was assisted by AI. The Python scripts used to generate benchmark results and graphs were written with AI assistance. The benchmark design, test execution, analysis and writing were done by a human with AI helping where needed.
...
Read the original on harshanu.space »
Imagine a world where improvements to your repositories are automatically delivered as pull requests each morning, ready for you to review. Issues are automatically triaged, CI failures analyzed, documentation maintained, test coverage improved and compliance monitored - all defined via simple markdown files.
GitHub Agentic Workflows deliver this: repository automation, running the coding agents you know and love, in GitHub Actions, with strong guardrails and security-first design principles.
Use GitHub Copilot, Claude by Anthropic or OpenAI Codex for event-triggered, recurring and scheduled jobs to improve, document and analyze your repository. GitHub Agentic Workflows are designed to augment your existing, deterministic CI/CD with Continuous AI capabilities
GitHub Agentic Workflows has been developed by GitHub Next and Microsoft Research with guardrails in mind. Agentic workflows run with minimal permissions by default, with explicit allowlisting for write operations and sandboxed execution to help keep your repository safe.
Workflows run with read-only permissions by default. Write operations require explicit approval through sanitized safe outputs (pre-approved GitHub operations), with sandboxed execution, tool allowlisting, and network isolation ensuring AI agents operate within controlled boundaries.
Write - Create a .md file with your automation instructions in natural language
Compile - Run gh aw compile to transform it into a GitHub Actions workflow with guardrails (.lock.yml)
Run - GitHub Actions executes your workflow automatically based on your triggers
Here’s a simple workflow that runs daily to create an upbeat status report:
The gh aw cli converts this into a GitHub Actions Workflow (.yml) that runs an AI agent (Copilot, Claude, Codex, …) in a containerized environment on a schedule or manually.
The AI coding agent reads your repository context, analyzes issues, generates visualizations, and creates reports - all defined in natural language rather than complex code.
Install the extension, add a sample workflow, and trigger your first run - all from the command line in minutes.
Create custom agentic workflows directly from the GitHub web interface using natural language.
...
Read the original on github.github.io »
To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".
10HN is also available as an iOS App
If you visit 10HN only rarely, check out the the best articles from the past week.
If you like 10HN please leave feedback and share
Visit pancik.com for more.