10 interesting stories served every morning and every evening.
This is a brief guide to my new art project microgpt, a single file of 200 lines of pure Python with no dependencies that trains and inferences a GPT. This file contains the full algorithmic content of what is needed: dataset of documents, tokenizer, autograd engine, a GPT-2-like neural network architecture, the Adam optimizer, training loop, and inference loop. Everything else is just efficiency. I cannot simplify this any further. This script is the culmination of multiple projects (micrograd, makemore, nanogpt, etc.) and a decade-long obsession to simplify LLMs to their bare essentials, and I think it is beautiful 🥹. It even breaks perfectly across 3 columns:
Where to find it:
This GitHub gist has the full source code: microgpt.py
It’s also available on this web page: https://karpathy.ai/microgpt.html
Also available as a Google Colab notebook
The following is my guide on stepping an interested reader through the code.
The fuel of large language models is a stream of text data, optionally separated into a set of documents. In production-grade applications, each document would be an internet web page but for microgpt we use a simpler example of 32,000 names, one per line:
# Let there be an input dataset `docs`: list[str] of documents (e.g. a dataset of names)
if not os.path.exists(‘input.txt’):
import urllib.request
names_url = ’https://raw.githubusercontent.com/karpathy/makemore/refs/heads/master/names.txt’
urllib.request.urlretrieve(names_url, ‘input.txt’)
docs = [l.strip() for l in open(‘input.txt’).read().strip().split(‘\n’) if l.strip()] # list[str] of documents
random.shuffle(docs)
print(f”num docs: {len(docs)}“)
The dataset looks like this. Each name is a document:
The goal of the model is to learn the patterns in the data and then generate similar new documents that share the statistical patterns within. As a preview, by the end of the script our model will generate (“hallucinate”!) new, plausible-sounding names. Skipping ahead, we’ll get:
It doesn’t look like much, but from the perspective of a model like ChatGPT, your conversation with it is just a funny looking “document”. When you initialize the document with your prompt, the model’s response from its perspective is just a statistical document completion.
Under the hood, neural networks work with numbers, not characters, so we need a way to convert text into a sequence of integer token ids and back. Production tokenizers like tiktoken (used by GPT-4) operate on chunks of characters for efficiency, but the simplest possible tokenizer just assigns one integer to each unique character in the dataset:
# Let there be a Tokenizer to translate strings to discrete symbols and back
uchars = sorted(set(‘’.join(docs))) # unique characters in the dataset become token ids 0..n-1
BOS = len(uchars) # token id for the special Beginning of Sequence (BOS) token
vocab_size = len(uchars) + 1 # total number of unique tokens, +1 is for BOS
print(f”vocab size: {vocab_size}“)
In the code above, we collect all unique characters across the dataset (which are just all the lowercase letters a-z), sort them, and each letter gets an id by its index. Note that the integer values themselves have no meaning at all; each token is just a separate discrete symbol. Instead of 0, 1, 2 they might as well be different emoji. In addition, we create one more special token called BOS (Beginning of Sequence), which acts as a delimiter: it tells the model “a new document starts/ends here”. Later during training, each document gets wrapped with BOS on both sides: [BOS, e, m, m, a, BOS]. The model learns that BOS initates a new name, and that another BOS ends it. Therefore, we have a final vocavulary of 27 (26 possible lowercase characters a-z and +1 for the BOS token).
Training a neural network requires gradients: for each parameter in the model, we need to know “if I nudge this number up a little, does the loss go up or down, and by how much?”. The computation graph has many inputs (the model parameters and the input tokens) but funnels down to a single scalar output: the loss (we’ll define exactly what the loss is below). Backpropagation starts at that single output and works backwards through the graph, computing the gradient of the loss with respect to every input. It relies on the chain rule from calculus. In production, libraries like PyTorch handle this automatically. Here, we implement it from scratch in a single class called Value:
class Value:
__slots__ = (‘data’, ‘grad’, ‘_children’, ‘_local_grads’)
def __init__(self, data, children=(), local_grads=()):
self.data = data # scalar value of this node calculated during forward pass
self.grad = 0 # derivative of the loss w.r.t. this node, calculated in backward pass
self._children = children # children of this node in the computation graph
self._local_grads = local_grads # local derivative of this node w.r.t. its children
def __add__(self, other):
other = other if isinstance(other, Value) else Value(other)
return Value(self.data + other.data, (self, other), (1, 1))
def __mul__(self, other):
other = other if isinstance(other, Value) else Value(other)
return Value(self.data * other.data, (self, other), (other.data, self.data))
def __pow__(self, other): return Value(self.data**other, (self,), (other * self.data**(other-1),))
def log(self): return Value(math.log(self.data), (self,), (1/self.data,))
def exp(self): return Value(math.exp(self.data), (self,), (math.exp(self.data),))
def relu(self): return Value(max(0, self.data), (self,), (float(self.data > 0),))
def __neg__(self): return self * -1
def __radd__(self, other): return self + other
def __sub__(self, other): return self + (-other)
def __rsub__(self, other): return other + (-self)
def __rmul__(self, other): return self * other
def __truediv__(self, other): return self * other**-1
def __rtruediv__(self, other): return other * self**-1
def backward(self):
topo = []
visited = set()
def build_topo(v):
if v not in visited:
visited.add(v)
for child in v._children:
build_topo(child)
topo.append(v)
build_topo(self)
self.grad = 1
for v in reversed(topo):
for child, local_grad in zip(v._children, v._local_grads):
child.grad += local_grad * v.grad
I realize that this is the most mathematically and algorithmically intense part and I have a 2.5 hour video on it: micrograd video. Briefly, a Value wraps a single scalar number (.data) and tracks how it was computed. Think of each operation as a little lego block: it takes some inputs, produces an output (the forward pass), and it knows how its output would change with respect to each of its inputs (the local gradient). That’s all the information autograd needs from each block. Everything else is just the chain rule, stringing the blocks together.
Every time you do math with Value objects (add, multiply, etc.), the result is a new Value that remembers its inputs (_children) and the local derivative of that operation (_local_grads). For example, __mul__ records that \(\frac{\partial(a \cdot b)}{\partial a} = b\) and \(\frac{\partial(a \cdot b)}{\partial b} = a\). The full set of lego blocks:
The backward() method walks this graph in reverse topological order (starting from the loss, ending at the parameters), applying the chain rule at each step. If the loss is \(L\) and a node \(v\) has a child \(c\) with local gradient \(\frac{\partial v}{\partial c}\), then:
\[\frac{\partial L}{\partial c} \mathrel{+}= \frac{\partial v}{\partial c} \cdot \frac{\partial L}{\partial v}\]
This looks a bit scary if you’re not comfortable with your calculus, but this is literally just multiplying two numbers in an intuitive way. One way to see it looks as follows: “If a car travels twice as fast as a bicycle and the bicycle is four times as fast as a walking man, then the car travels 2 x 4 = 8 times as fast as the man.” The chain rule is the same idea: you multiply the rates of change along the path.
We kick things off by setting self.grad = 1 at the loss node, because \(\frac{\partial L}{\partial L} = 1\): the loss’s rate of change with respect to itself is trivially 1. From there, the chain rule just multiplies local gradients along every path back to the parameters.
Note the += (accumulation, not assignment). When a value is used in multiple places in the graph (i.e. the graph branches), gradients flow back along each branch independently and must be summed. This is a consequence of the multivariable chain rule: if \(c\) contributes to \(L\) through multiple paths, the total derivative is the sum of contributions from each path.
After backward() completes, every Value in the graph has a .grad containing \(\frac{\partial L}{\partial v}\), which tells us how the final loss would change if we nudged that value.
Here’s a concrete example. Note that a is used twice (the graph branches), so its gradient is the sum of both paths:
a = Value(2.0)
b = Value(3.0)
c = a * b # c = 6.0
L = c + a # L = 8.0
L.backward()
print(a.grad) # 4.0 (dL/da = b + 1 = 3 + 1, via both paths)
print(b.grad) # 2.0 (dL/db = a = 2)
This is exactly what PyTorch’s .backward() gives you:
This is the same algorithm that PyTorch’s loss.backward() runs, just on scalars instead of tensors (arrays of scalars) - algorithmically identical, significantly smaller and simpler, but of course a lot less efficient.
Let’s spell what the .backward() gives us above. Autograd calculated that if L = a*b + a, and a=2 and b=3, then a.grad = 4.0 is telling us about the local influence of a on L. If you wiggle the inmput a, in what direction is L changing? Here, the derivative of L w.r.t. a is 4.0, meaning that if we increase a by a tiny amount (say 0.001), L would increase by about 4x that (0.004). Similarly, b.grad = 2.0 means the same nudge to b would increase L by about 2x that (0.002). In other words, these gradients tell us the direction (positive or negative depending on the sign), and the steepness (the magnitude) of the influence of each individual input on the final output (the loss). This then allows us to interately nudge the parameters of our neural network to lower the loss, and hence improve its predictions.
The parameters are the knowledge of the model. They are a large collection of floating point numbers (wrapped in Value for autograd) that start out random and are iteratively optimized during training. The exact role of each parameter will make more sense once we define the model architecture below, but for now we just need to initialize them:
n_embd = 16 # embedding dimension
n_head = 4 # number of attention heads
n_layer = 1 # number of layers
block_size = 16 # maximum sequence length
head_dim = n_embd // n_head # dimension of each head
matrix = lambda nout, nin, std=0.08: [[Value(random.gauss(0, std)) for _ in range(nin)] for _ in range(nout)]
state_dict = {‘wte’: matrix(vocab_size, n_embd), ‘wpe’: matrix(block_size, n_embd), ‘lm_head’: matrix(vocab_size, n_embd)}
for i in range(n_layer):
state_dict[f’layer{i}.attn_wq’] = matrix(n_embd, n_embd)
state_dict[f’layer{i}.attn_wk’] = matrix(n_embd, n_embd)
state_dict[f’layer{i}.attn_wv’] = matrix(n_embd, n_embd)
state_dict[f’layer{i}.attn_wo’] = matrix(n_embd, n_embd)
state_dict[f’layer{i}.mlp_fc1′] = matrix(4 * n_embd, n_embd)
state_dict[f’layer{i}.mlp_fc2′] = matrix(n_embd, 4 * n_embd)
params = [p for mat in state_dict.values() for row in mat for p in row]
print(f”num params: {len(params)}“)
...
Read the original on karpathy.github.io »
Ask questions about this page
Switch to Claude without starting overBring your preferences and context from other AI providers to Claude. With one copy-paste, Claude updates its memory and picks up right where you left off. Memory is available on all paid plans. Import what matters in under a minuteYou’ve spent months teaching another AI how you work. That context shouldn’t disappear because you want to try something new. Claude can import what matters, so your first conversation feels like your hundredth.Copy and paste the provided prompt into a chat with any AI provider. It’s written specifically to help you get all of your context in one chat.Copy and paste the results into Claude’s memory settings. That’s it! Claude will update its memory and you’re good to go.Memory that understands how you workClaude learns your preferences across conversations, keeps project context separate so nothing bleeds together, and lets you see and edit everything it remembers. Your AI should know you from day oneStart your Pro plan, import your memory when you’re ready, and see for yourself.
...
Read the original on claude.com »
Ghostty is a fast, feature-rich, and cross-platform terminal emulator that uses platform-native UI and GPU acceleration.
Ghostty is a fast, feature-rich, and cross-platform terminal emulator that uses platform-native UI and GPU acceleration.
Install Ghostty and run!
Zero configuration required to get up and running.
Ready-to-run binaries for macOS. Packages or build from source for Linux.
...
Read the original on ghostty.org »
Iran’s supreme leader, Ayatollah Ali Khamenei, was killed in Israeli attacks, with U. S. support, on Saturday. He was 86 years old.
His death was confirmed by President Trump, who joined Israeli leaders in calling for the overthrow of Khamenei’s authoritarian regime as the U. S. and Israel launched airstrikes across Iran. The Israeli military said its forces killed Khamenei. The Iranian government confirmed the supreme leader’s death and announced 40 days of mourning.
During his 36-year rule, Khamenei was unwavering in his steadfast antipathy to the U. S. and Israel and to any efforts to reform and bring Iran into the 21st century.
Khamenei was born in July 1939 into a religious family in the Shia Muslim holy city of Mashhad in northeastern Iran and attended theological school. An outspoken opponent of the U. S.-backed Shah Mohammad Reza Pahlavi, Khamenei was arrested several times.
He was surrounded by other Iranian activists, including Ayatollah Ruhollah Khomeini, who became Iran’s first supreme leader following the country’s Islamic Revolution in the late 1970s.
Khamenei survived an assassination attempt in 1981 that cost him the use of his right arm. He served as Iran’s president before succeeding Khomeini as supreme leader in 1989.
Alex Vatanka, a senior fellow at the Middle East Institute in Washington, D. C., says Khamenei was an unlikely candidate. Then a midlevel cleric, Khamenei lacked religious credentials, which left him feeling vulnerable, Vatanka says.
“He knew himself. He didn’t have the prestige, the gravitas to be … the successor to the founder of the Islamic Republic, Ayatollah Khomeini,” he says.
“He spent the first few years in power being very nervous,” says Vatanka. “He really literally felt that somebody is going to, you know, take him down from the position of power.”
But Khamenei was cunning and able to outwit other senior political figures in the Islamic Republic, according to Ali Vaez, director of the Iran Project at the International Crisis Group. He says that with the help of the formidable Islamic Revolutionary Guard Corps, Khamenei built up his power base to become the longest-serving leader in the Middle East.
“Ayatollah Khamenei was a man with strategic patience and was able to calculate a few steps ahead,” he says. “That’s why I think he managed — on the back of the Revolutionary Guards — to increasingly appropriate all the levers of power in his hands and sideline everyone else.”
Khamenei’s close ties to the Revolutionary Guards allowed Iran’s military to develop a vast commercial empire in control of many parts of the economy, while ordinary Iranians struggled to get by.
Vaez says Khamenei also began to build up Iran’s defensive policies, such as developing proxies like Hezbollah in Lebanon and Hamas in the Gaza Strip to deter a direct attack on Iranian soil.
“And then also becoming self-reliant in developing a viable conventional deterrence, which took the form of Iran’s ballistic missile program,” Vaez says.
As supreme leader, Khamenei also had the final word on anything to do with Iran’s nuclear program.
Over time, Khamenei increasingly injected himself into politics. Such was the case in 2009, when he intervened in the presidential election to ensure that his favored candidate, the controversial conservative Mahmoud Ahmadinejad, won office.
Iranians took to the streets to protest what was widely seen as a fraudulent election. Khamenei brutally crushed those demonstrations, triggering both a backlash and more protest movements over the years.
Iran killed thousands of its citizens under Khamenei’s rule, including more than 7,000 people killed during weeks of mass protests that started in late December 2025, according to the Human Rights Activists News Agency, a U. S.-based organization that closely tracks rights abuses in Iran.
“Khamenei had always supported and endorsed repressive government crackdown, recognizing that these protests were damaging to the stability and legitimacy of the state,” says Sanam Vakil, an Iran expert at Chatham House, a London-based think tank.
But Khamenei was unconcerned about getting to the root of the protests, says the Middle East Institute’s Vatanka, and remained stuck in an Islamic revolutionary mindset against the West.
“He on so many occasions refused point-blank to accept the basic reality that where he was in terms of his worldview was not where the rest of his people were,” Vatanka says.
He adds that 75% of Iran’s 90 million people were born after the revolution and have watched other countries in the region modernize and integrate with the international community.
“The 75% he should have catered to, listened to and address[ed] policies to satisfy their aspirations,” he says. “He failed in that miserably.”
The International Crisis Group’s Vaez says after the Arab Spring uprisings in 2011, Khamenei did start worrying about the survival of his regime. Iran’s economy was crumbling, due in large part to stringent Western sanctions, fueling more unrest.
In 2013, Khamenei agreed to secret negotiations with the U. S. about Iran’s nuclear program, which eventually led to the 2015 Joint Comprehensive Plan of Action nuclear agreement. Vaez says Khamenei deeply distrusted the U.S. and was skeptical about the deal.
“His argument has always been that the U. S. is always looking for pretexts, for putting pressure on Iran,” he says. “And if Iran concedes on the nuclear issue, then the U.S. would put pressure on Iran because of its missiles program or because of human rights violations or because of its regional policies.”
President Trump’s withdrawal from the nuclear deal during his first term in office gave some credence to Khamenei’s cynicism. Analysts say Iran increased its nuclear enrichment after that to a point where it was close to being able to build a bomb.
In early 2025, when Trump reached out to Iran about a new deal, Khamenei dragged out negotiations until they began in mid-April.
But time ran out. In June, Israel made good on its threat to neutralize Iran’s nuclear program, launching strikes on key facilities and killing scientists and generals. Iran retaliated, and the two sides exchanged several days of missile strikes.
On June 21, 2025, the U. S. launched major airstrikes on three of Iran’s nuclear enrichment sites. Trump said the facilities had been “completely and totally obliterated,” although there was debate among the White House and nuclear experts as to how serious Iran’s nuclear program had been set back.
Vakil, of Chatham House, says Khamenei underestimated what Israel and the U. S. would do.
“I think that Khamenei always assumed that he could play for time, and what he really didn’t understand is that the world around Iran had very much changed,” she says. “The world had tired of Khamenei and Iranian foot-dragging and antics … and so that was a miscalculation.”
But it was Iran’s use of proxy militias across the region that eventually led to Khamenei’s downfall.
When Hamas — the Palestinian Islamist group backed by Iran — attacked Israel on Oct. 7, 2023, killing nearly 1,200 people and kidnapping 251 others, it triggered a cascade of events that ultimately led to Israel’s attack on Iran.
The day after the 2023 Hamas-led attack, Iran-backed Hezbollah in Lebanon started firing rockets into Israel, triggering a conflict that led to the Shia militia’s top brass being decimated — including top leader Hassan Nasrallah.
Israel and Iran traded direct airstrikes for the first time in 2024 as part of that conflict.
Israel’s bombing of Iranian weapons shipments in Syria also helped weaken the regime of Syria’s then-dictator, Bashar al-Assad, an important ally of Iran. Assad fell in December 2024 and fled to Russia in early January 2025.
By the time Khamenei died, his legacy was in tatters. Israel had hobbled two key proxies, Hamas and Hezbollah, and had wiped out Iran’s air defenses. With U. S. help, it left Iran’s nuclear program in shambles.
What remains is a robust ballistic missile program, the brainchild of Khamenei. It’s unclear who will replace him to lead a now weakened and vulnerable Iran.
...
Read the original on www.npr.org »
A satirical (but real!) demo of what AI chat could look like in an ad-supported future. Chat with an AI while experiencing every monetization pattern imaginable — banners, interstitials, sponsored responses, freemium gates, and more.
Join 2 million professionals who think faster, focus better, and accomplish more. AI-powered goal tracking, habit building, and memory enhancement. First 30 days FREE!Think 10x Faster with AI. First Month FREE! 🧠Did you know? The average person wastes $200/month on unused subscriptions. Let MoneyMind’s AI find and cancel them for you!Your AI assistant, proudly powered by the finest advertising money can buy 💸⚠️ Warning: This AI may spontaneously recommend products at any time🏷️ This conversation is proudly powered by BrainBoost Pro™ • Ad-supported free tier • Remove adsStressed by all these ads? 10 minutes of AI-guided meditation changes everything. AI-curated meal prep kits delivered weekly. $30 off your first box!🎨 Today’s chat theme sponsored by BrainBoost Pro • Colors, fonts, and vibes curated by our advertising team
This tool is a satirical but fully functional demonstration of what AI chat assistants could look like if they were monetized through advertising — similar to how free apps, websites, and streaming services fund themselves today. As AI chat becomes mainstream, companies face a fundamental question: how do you make it free for users while covering the significant compute costs? Advertising is one obvious answer — and this demo shows every major ad pattern that could be applied to a chat interface.We built this as an educational tool to help marketers, product managers, and developers understand the landscape of AI monetization, and to give users a glimpse of the future they might want to avoid (or embrace, depending on your perspective).
This demo covers the full spectrum of advertising patterns that could appear in an AI chat product.
This tool is educational and useful for a wide range of professionals thinking about the future of AI products.
Are the ads in this demo real?No — all brands and ads are completely fictional and created for this demo. BrainBoost Pro, QuickLearn Academy, ZenFocus, TaskMaster AI, ReadyMeal, and all other brands are made up. No actual advertising revenue is being generated. Does this show what AI chat will actually look like?It shows one possible future. Some ad-supported AI products already exist and use several of these patterns. Others are speculative. The goal is to make these possibilities concrete and tangible so people can have informed conversations about what kind of AI future they want.Is the AI actually working or is everything scripted?The AI is real — your messages are processed by a live language model and you get genuine responses. The ads are the scripted part. Some AI responses will include sponsored product mentions as part of the demonstration.What happens to my chat data?Like all our free tools, conversations are logged to improve the service. We do not sell this data to advertisers — this is a demo, not an actual ad network.How does the freemium gate work?After 5 free messages, you can either ‘watch an ad’ (a simulated 5-second countdown) to unlock 5 more messages, or you can upgrade to our actual ad-free service. This mirrors how real freemium products work.
All of our tools are genuinely free — no ads, no paywalls, no sponsored responses. Just AI that works.
Build Your Own AI Chatbot — No Ads RequiredNow that you’ve seen what ad-supported AI looks like, imagine giving your customers a clean, focused AI experience with zero interruptions. With 99helpers, you can deploy an AI chatbot trained on your content in minutes. No credit card required • Setup in minutes • No ads, ever
...
Read the original on 99helpers.com »
Yes, writing code is easier than ever.
AI assistants autocomplete your functions. Agents scaffold entire features. You can describe what you want in plain English and watch working code appear in seconds. The barrier to producing code has never been lower.
And yet, the day-to-day life of software engineers has gotten more complex, more demanding, and more exhausting than it was two years ago.
This is not a contradiction. It is the reality of what happens when an industry adopts a powerful new tool without pausing to consider the second-order effects on the people using it.
If you are a software engineer reading this and feeling like your job quietly became harder while everyone around you celebrates how easy everything is now, you are not imagining things. The job changed. The expectations changed. And nobody sent a memo.
There is a phenomenon happening right now that most engineers feel but struggle to articulate. The expected output of a software engineer in 2026 is dramatically higher than it was in 2023. Not because anyone held a meeting and announced new targets. Not because your manager sat you down and explained the new rules. The baseline just moved.
It moved because AI tools made certain tasks faster. And when tasks become faster, the assumption follows immediately: you should be doing more. Not in the future. Now.
A February 2026 study published in Harvard Business Review tracked 200 employees at a U. S. tech company over eight months. The researchers found something that will sound familiar to anyone living through this shift. Workers did not use AI to finish earlier and go home. They used it to do more. They took on broader tasks, worked at a faster pace, and extended their hours, often without anyone asking them to. The researchers described a self-reinforcing cycle: AI accelerated certain tasks, which raised expectations for speed. Higher speed made workers more reliant on AI. Increased reliance widened the scope of what workers attempted. And a wider scope further expanded the quantity and density of work.
The numbers tell the rest of the story. Eighty-three percent of workers in the study said AI increased their workload. Burnout was reported by 62 percent of associates and 61 percent of entry-level workers. Among C-suite leaders? Just 38 percent. The people doing the actual work are carrying the intensity. The people setting the expectations are not feeling it the same way.
This gap matters enormously. If leadership believes AI is making everything easier while engineers are drowning in a new kind of complexity, the result is a slow erosion of trust, morale, and eventually talent.
A separate survey of over 600 engineering professionals found that nearly two-thirds of engineers experience burnout despite their organizations using AI in development. Forty-three percent said leadership was out of touch with team challenges. Over a third reported that productivity had actually decreased over the past year, even as their companies invested more in AI tooling.
The baseline moved. The expectations rose. And for many engineers, no one acknowledged that the job they signed up for had fundamentally changed.
Here is something that gets lost in all the excitement about AI productivity: most software engineers became engineers because they love writing code.
Not managing code. Not reviewing code. Not supervising systems that produce code. Writing it. The act of thinking through a problem, designing a solution, and expressing it precisely in a language that makes a machine do exactly what you intended. That is what drew most of us to this profession. It is a creative act, a form of craftsmanship, and for many engineers, the most satisfying part of their day.
Now they are being told to stop.
Not explicitly, of course. Nobody walks into a standup and says “stop writing code.” But the message is there, subtle and persistent. Use AI to write it faster. Let the agent handle the implementation. Focus on higher-level tasks. Your value is not in the code you write anymore, it is in how well you direct the systems that write it for you.
For early adopters, this feels exciting. It feels like evolution. For a significant portion of working engineers, it feels like being told that the thing they spent years mastering, the skill that defines their professional identity, is suddenly less important.
One engineer captured this shift perfectly in a widely shared essay, describing how AI transformed the engineering role from builder to reviewer. Every day felt like being a judge on an assembly line that never stops. You just keep stamping those pull requests. The production volume went up. The sense of craftsmanship went down.
This is not a minor adjustment. It is a fundamental shift in professional identity. Engineers who built their careers around deep technical skill are being asked to redefine what they do and who they are, essentially overnight, without any transition period, training, or acknowledgment that something significant was lost in the process.
Having led engineering teams for over two decades, I have seen technology shifts before. New frameworks, new languages, new methodologies. Engineers adapt. They always have. But this is different because it is not asking engineers to learn a new way of doing what they do. It is asking them to stop doing the thing that made them engineers in the first place and become something else entirely.
That is not an upgrade. That is a career identity crisis. And pretending it is not happening does not make it go away.
While engineers are being asked to write less code, they are simultaneously being asked to do more of everything else.
More product thinking. More architectural decision-making. More code review. More context switching. More planning. More testing oversight. More deployment awareness. More risk assessment.
The scope of what it means to be a “software engineer” expanded dramatically in the last two years, and it happened without a pause to catch up.
This is partly a direct consequence of AI acceleration. When code gets produced faster, the bottleneck shifts. It moves from implementation to everything surrounding implementation: requirements clarity, architecture decisions, integration testing, deployment strategy, monitoring, and maintenance. These were always part of the engineering lifecycle, but they were distributed across roles. Product managers handled requirements. QA handled testing. DevOps handled deployment. Senior architects handled system design.
Now, with AI collapsing the implementation phase, organizations are quietly redistributing those responsibilities to the engineers themselves. The Harvard Business Review study documented this exact pattern. Product managers began writing code. Engineers took on product work. Researchers started doing engineering tasks. Roles that once had clear boundaries blurred as workers used AI to handle jobs that previously sat outside their remit.
The industry is openly talking about this as a positive development. Engineers should be “T-shaped” or “full-stack” in a broader sense. Nearly 45 percent of engineering roles now expect proficiency across multiple domains. AI tools augment generalists more effectively, making it easier for one person to handle multiple components of a system.
On paper, this sounds empowering. In practice, it means that a mid-level backend engineer is now expected to understand product strategy, review AI-generated frontend code they did not write, think about deployment infrastructure, consider security implications of code they cannot fully trace, and maintain a big-picture architectural awareness that used to be someone else’s job.
That is not empowerment. That is scope creep without a corresponding increase in compensation, authority, or time.
From my experience building and scaling teams in fintech and high-traffic platforms, I can tell you that role expansion without clear boundaries always leads to the same outcome: people try to do everything, nothing gets done with the depth it requires, and burnout follows. The engineers who survive are the ones who learn to say no, to prioritize ruthlessly, and to push back when the scope of their role quietly doubles without anyone acknowledging it.
There is an irony at the center of the AI-assisted engineering workflow that nobody wants to talk about: reviewing AI-generated code is often harder than writing the code yourself.
When you write code, you carry the context of every decision in your head. You know why you chose this data structure, why you handled this edge case, why you structured the module this way. The code is an expression of your thinking, and reviewing it later is straightforward because the reasoning is already stored in your memory.
When AI writes code, you inherit the output without the reasoning. You see the code, but you do not see the decisions. You do not know what tradeoffs were made, what assumptions were baked in, what edge cases were considered or ignored. You are reviewing someone else’s work, except that someone is not a colleague you can ask questions. It is a statistical model that produces plausible-looking code without any understanding of your system’s specific constraints.
A survey by Harness found that 67 percent of developers reported spending more time debugging AI-generated code, and 68 percent spent more time reviewing it than they did with human-written code. This is not a failure of the tools. It is a structural property of the workflow. Code review without shared context is inherently more demanding than reviewing code you participated in creating.
Yet the expectation from management is that AI should be making everything faster. So engineers find themselves in a bind: they are producing more code than ever, but the quality assurance burden has increased, the context-per-line-of-code has decreased, and the cognitive load of maintaining a system they only partially built is growing with every sprint.
This is the supervision paradox. The faster AI generates code, the more human attention is required to ensure that code actually works in the context of a real system with real users and real business constraints. The production bottleneck did not disappear. It moved from writing to understanding, and understanding is harder to speed up.
What makes all of this especially difficult is the self-reinforcing nature of the cycle.
AI makes certain tasks faster. Faster tasks create the perception of more available capacity. More perceived capacity leads to more work being assigned. More work leads to more AI reliance. More AI reliance leads to more code that needs review, more context that needs to be maintained, more systems that need to be understood, and more cognitive load on engineers who are already stretched thin.
The Harvard Business Review researchers described this as “workload creep.” Workers did not consciously decide to work harder. The expansion happened naturally, almost invisibly. Each individual step felt reasonable. In aggregate, it produced an unsustainable pace.
Before AI, there was a natural ceiling on how much you could produce in a day. That ceiling was set by thinking speed, typing speed, and the time it takes to look things up. It was frustrating sometimes, but it was also a governor. A natural speed limit that prevented you from outrunning your own ability to maintain quality.
AI removed the governor. Now the only limit is your cognitive endurance. And most people do not know their cognitive limits until they have already blown past them.
This is where many engineers find themselves right now. Shipping more code than any quarter in their career. Feeling more drained than any quarter in their career. The two facts are not unrelated.
The trap is that it looks like productivity from the outside. Metrics go up. Velocity charts look great. More features shipped. More pull requests merged. But underneath the numbers, quality is quietly eroding, technical debt is accumulating faster than it can be addressed, and the people doing the work are running on fumes.
If the picture is difficult for experienced engineers, it is even harder for those starting their careers.
Junior engineers have traditionally learned by doing the simpler, more task-oriented work. Fixing small bugs. Writing straightforward features. Implementing well-defined tickets. This hands-on work built the foundational understanding that eventually allowed them to take on more complex challenges.
AI is rapidly consuming that training ground. If an agent can handle the routine API hookup, the boilerplate module, the straightforward CRUD endpoint, what is left for a junior engineer to learn from? The expectation is shifting toward needing to contribute at a higher level almost from day one, without the gradual ramp-up that previous generations of engineers relied on.
Entry-level hiring at the 15 largest tech firms fell 25 percent from 2023 to 2024. The HackerRank 2025 Developer Skills Report confirmed that expectations are rising faster than productivity gains, and that early-career hiring remains sluggish compared to senior-level roles. Companies are prioritizing experienced talent, but the pipeline that produces experienced talent is being quietly dismantled.
This is a problem that extends beyond individual career concerns. If junior engineers do not get the opportunity to build foundational skills through hands-on work, the industry will eventually face a shortage of senior engineers who truly understand the systems they oversee. You cannot supervise what you never learned to build.
As I have written before, code is for humans to read. If the next generation of engineers never develops the fluency to read, understand, and reason about code at a deep level, no amount of AI tooling will compensate for that gap.
If you lead engineering teams, the most important thing you can do right now is acknowledge that this transition is genuinely difficult. Not theoretically. Not abstractly. For the actual people on your team.
The career they signed up for changed fast. The skills they were hired for are being repositioned. The expectations they are working under shifted without a clear announcement. Acknowledging this reality is not a sign of weakness. It is a prerequisite for maintaining a team that trusts you.
Start with empathy, but do not stop there.
Give your team real training. Not a lunch-and-learn about prompt engineering. Real investment in the skills that the new engineering landscape actually requires: system design, architectural thinking, product reasoning, security awareness, and the ability to critically evaluate code they did not write. These are not trivial skills. They take time to develop, and your team needs structured support to build them.
Give them space to experiment without the pressure of immediate productivity gains. The engineers who will thrive in this environment are the ones who have room to figure out how AI fits into their workflow without being penalized for the learning curve. Every experienced technologist I know who has successfully integrated AI tools went through an adjustment period where they were less productive before they became more productive. That adjustment period is normal, and it needs to be protected.
Set explicit boundaries around role scope. If you are asking engineers to take on product thinking, planning, and risk assessment in addition to their technical work, name it. Define it. Compensate for it. Do not let it happen silently and then wonder why your team is burned out.
Rethink your metrics. If your engineering success metrics are still centered on velocity, tickets closed, and lines of code, you are measuring the wrong things in an AI-assisted world. System stability, code quality, decision quality, customer outcomes, and team health are better indicators of whether your engineering organization is actually producing value or just producing volume.
Protect the junior pipeline. If you have stopped hiring junior engineers because AI can handle entry-level tasks, you are solving a short-term efficiency problem by creating a long-term talent crisis. The senior engineers you rely on today were junior engineers who learned by doing the work that AI is now consuming. That path still matters.
And finally, keep challenging your team. I have never met a good engineer who did not love a good challenge. The engineers on your team are not fragile. They are capable, intelligent people who signed up for hard problems. They can handle this transition. Just make sure they are set up to meet it.
If you are an engineer navigating this shift, here is what I would tell you based on two decades of watching technology cycles reshape this profession.
First, do not abandon your fundamentals. The pressure to become an “AI-first” engineer is real, but the engineers who will be most valuable in five years are the ones who deeply understand the systems they work on. AI is a tool. Understanding architecture, debugging complex systems, reasoning about performance and security: these skills are not becoming less important. They are becoming more important because someone needs to be the adult in the room when AI-generated code breaks in production at 2 AM.
Second, learn to set boundaries with the acceleration trap. Just because you can produce more does not mean you should. Sustainable pace matters. The engineers who burn out trying to match the theoretical maximum output AI makes possible are not the ones who build lasting careers. The ones who learn to work with AI deliberately, choosing when to use it and when to think independently, are the ones who will still be thriving in this profession a decade from now.
Third, embrace the parts of the expanded role that genuinely interest you. If the engineering role now includes more product thinking, more architectural decision-making, more cross-functional communication, treat that as an opportunity rather than an imposition. These are skills that senior engineers and technical leaders need. You are being given access to a broader set of capabilities earlier in your career than any previous generation of engineers. That is not a burden. It is a head start.
Fourth, talk about what you are experiencing. The isolation of feeling like you are the only one struggling with this transition is one of the most damaging aspects of the current moment. You are not the only one. The data confirms it. Two-thirds of engineers report burnout. The expectation gap between leadership and engineering teams is well documented. Talking openly about these challenges, with your team, with your manager, with your broader network, is not complaining. It is professional honesty.
And fifth, remember that this profession has survived every prediction of its demise. COBOL was supposed to eliminate programmers. Expert systems were supposed to replace them. Fourth-generation languages, CASE tools, visual programming, no-code platforms, outsourcing. Every decade brings a new technology that promises to make software engineers obsolete, and every decade the demand for skilled engineers grows. AI will not be different. The tools change. The fundamentals endure.
AI made writing code easier and made being an engineer harder. Both of these things are true at the same time, and pretending that only the first one matters is how organizations lose their best people.
The engineers who are struggling right now are not struggling because they are bad at their jobs. They are struggling because their jobs changed underneath them while the industry celebrated the part that got easier and ignored the parts that got harder.
Expectations rose without announcement. Roles expanded without boundaries. Output demands increased without corresponding increases in support, training, or acknowledgment. And the engineers who raised concerns were told, implicitly or explicitly, that they just needed to adapt faster.
That is not how you build a sustainable engineering culture. That is how you build a burnout machine.
The industry needs to name this paradox honestly. AI is an incredible tool. It is also placing enormous new demands on the people using it. Both things can be true. Both things need to be addressed.
The organizations that get this right, that invest in their people alongside their tools, that acknowledge the human cost of rapid technological change while still pushing forward, those are the organizations that will attract and retain the best engineering talent in the years ahead.
The ones that do not will discover something that every technology cycle eventually teaches: tools do not build products. People do. And people have limits that no amount of AI can automate away.
If this resonated with you, I would love to hear your perspective. What has changed most about your engineering role in the last year? Drop me a message or connect with me on LinkedIn. I write regularly about the intersection of AI, software engineering, and leadership at ivanturkovic.com. Follow along if you want honest, experience-driven perspectives on how technology is actually changing this profession.
...
Read the original on www.ivanturkovic.com »
Let’s pretend we’re farmers with a new plot of land. Given only the Diameter and Height of a tree trunk, we must determine if it’s an Apple, Cherry, or Oak tree. To do this, we’ll use a Decision Tree. Almost every tree with a Diameter ≥ 0.45 is an Oak tree! Thus, we can probably assume that any other trees we find in that region will also be one.
This first decision node will act as our root node. We’ll draw a vertical line at this Diameter and classify everything above it as Oak (our first leaf node), and continue to partition our remaining data on the left. We continue along, hoping to split our plot of land in the most favorable manner. We see that creating a new decision node at Height ≤ 4.88 leads to a nice section of Cherry trees, so we partition our data there.
Our Decision Tree updates accordingly, adding a new leaf node for Cherry. And Some More After this second split we’re left with an area containing many Apple and some Cherry trees. No problem: a vertical division can be drawn to separate the Apple trees a bit better.
Once again, our Decision Tree updates accordingly. And Yet Some More The remaining region just needs a further horizontal division and boom - our job is done! We’ve obtained an optimal set of nested decisions.
That said, some regions still enclose a few misclassified points. Should we continue splitting, partitioning into smaller sections?
Hmm… If we do, the resulting regions would start becoming increasingly complex, and our tree would become unreasonably deep. Such a Decision Tree would learn too much from the noise of the training examples and not enough generalizable rules.
Does this ring familiar? It is the well known tradeoff that we have explored in our explainer on The Bias Variance Tradeoff! In this case, going too deep results in a tree that overfits our data, so we’ll stop here.
We’re done! We can simply pass any new data point’s Height and Diameter values through the newly created Decision Tree to classify them as either an Apple, Cherry, or Oak tree!
Decision Trees are supervised machine learning algorithms used for both regression and classification problems. They’re popular for their ease of interpretation and large range of applications. Decision Trees consist of a series of decision nodes on some dataset’s features, and make predictions at leaf nodes.
Scroll on to learn more! Decision Trees are widely used algorithms for supervised machine learning. They’re popular for their ease of interpretation and large range of applications. They work for both regression and classification problems. A Decision Tree consists of a series of sequential decisions, or decision nodes, on some data set’s features. The resulting flow-like structure is navigated via conditional control statements, or if-then rules, which split each decision node into two or more subnodes. Leaf nodes, also known as terminal nodes, represent prediction outputs for the model. To train a Decision Tree from data means to figure out the order in which the decisions should be assembled from the root to the leaves. New data may then be passed from the top down until reaching a leaf node, representing a prediction for that data point.
We just saw how a Decision Tree operates at a high-level: from the top down, it creates a series of sequential rules that split the data into well-separated regions for classification. But given the large number of potential options, how exactly does the algorithm determine where to partition the data? Before we learn how that works, we need to understand Entropy.
Entropy measures the amount of information of some variable or event. We’ll make use of it to identify regions consisting of a large number of similar (pure) or dissimilar (impure) elements. Given a certain set of events that occur with probabilities , the total entropy can be written as the negative sum of weighted probabilities: The quantity has a number of interesting properties: only if all but one of the are zero, this one having the value of 1. Thus the entropy vanishes only when there is no uncertainty in the outcome, meaning that the sample is completely unsurprising. is maximum when all the are equal. This is the most uncertain, or ‘impure’, situation. Any change towards the equalization of the probabilities increases . The entropy can be used to quantify the impurity of a collection of labeled data points: a node containing multiple classes is impure whereas a node including only one class is pure. Above, you can compute the entropy of a collection of labeled data points belonging to two classes, which is typical for binary classification problems. Click on the Add and Remove buttons to modify the composition of the bubble. Did you notice that pure samples have zero entropy whereas impure ones have larger entropy values? This is what entropy is doing for us: measuring how pure (or impure) a set of samples is. We’ll use it in the algorithm to train Decision Trees by defining the Information Gain.
With the intuition gained with the above animation, we can now describe the logic to train Decision Trees. As the name implies, information gain measures an amount the information that we gain. It does so using entropy. The idea is to subtract from the entropy of our data before the split the entropy of each possible partition thereafter. We then select the split that yields the largest reduction in entropy, or equivalently, the largest increase in information.
The core algorithm to calculate information gain is called ID3. It’s a recursive procedure that starts from the root node of the tree and iterates top-down on all non-leaf branches in a greedy manner, calculating at each depth the difference in entropy:
To be specific, the algorithm’s steps are as follows: Calculate the entropy associated to every feature of the data set. Partition the data set into subsets using different features and cutoff values. For each, compute the information gain as the difference in entropy before and after the split using the formula above. For the total entropy of all children nodes after the split, use the weighted average taking into account , i.e. how many of the samples end up on each child branch. Identify the partition that leads to the maximum information gain. Create a decision node on that feature and split value. When no further splits can be done on a subset, create a leaf node and label it with the most common class of the data points within it if doing classification or with the average value if doing regression. Recurse on all subsets. Recursion stops if after a split all elements in a child node are of the same type. Additional stopping conditions may be imposed, such as requiring a minimum number of samples per leaf to continue splitting, or finishing when the trained tree has reached a given maximum depth. Of course, reading the steps of an algorithm isn’t always the most intuitive thing. To make things easier to understand, let’s revisit how information gain was used to determine the first decision node in our tree. Recall our first decision node split on Diameter ≤ 0.45. How did we choose this condition? It was the result of maximizing information gain.
Each of the possible splits of the data on its two features (Diameter and Height) and cutoff values yields a different value of the information gain.
The line chart displays the different split values for the Diameter feature. Move the decision boundary yourself to see how the data points in the top chart are assigned to the left or right children nodes accordingly. On the bottom you can see the corresponding entropy values of both children nodes as well as the total information gain.
The ID3 algorithm will select the split point with the largest information gain, shown as the peak of the black line in the bottom chart of 0.574 at Diameter = 0.45. Recall our first decision node split on Diameter ≤ 0.45. How did we choose this condition? It was the result of maximizing information gain.
Each of the possible splits of the data on its two features (Diameter and Height) and cutoff values yields a different value of the information gain.
The visualization on the right allows to try different split values for the Diameter feature. Move the decision boundary yourself to see how the data points in the top chart are assigned to the left or right children nodes accordingly. On the bottom you can see the corresponding entropy values of both children nodes as well as the total information gain.
The ID3 algorithm will select the split point with the largest information gain, shown as the peak of the black line in the bottom chart of 0.574 at Diameter = 0.45. An alternative to the entropy for the construction of Decision Trees is the Gini impurity. This quantity is also a measure of information and can be seen as a variation of Shannon’s entropy. Decision trees trained using entropy or Gini impurity are comparable, and only in a few cases do results differ considerably. In the case of imbalanced data sets, entropy might be more prudent. Yet Gini might train faster as it does not make use of logarithms.
Another Look At Our Decision Tree Let’s recap what we’ve learned so far. First, we saw how a Decision Tree classifies data by repeatedly partitioning the feature space into regions according to some conditional series of rules. Second, we learned about entropy, a popular metric used to measure the purity (or lack thereof) of a given sample of data. Third, we learned how Decision Trees use entropy in information gain and the ID3 algorithm to determine the exact conditional series of rules to select. Taken together, the three sections detail the typical Decision Tree algorithm.
To reinforce concepts, let’s look at our Decision Tree from a slightly different perspective.
The tree below maps exactly to the tree we showed in How to Build a Decision Tree section above. However, instead of showing the partitioned feature space alongside our trees structure, let’s look at the partitioned data points and their corresponding entropy at each node itself:
From the top down, our sample of data points to classify shrinks as it gets partitioned to different decision and leaf nodes. In this manner, we could trace the full path taken by a training data point if we so desired. Note also that not every leaf node is pure: as discussed previously (and in the next section), we don’t want the structure of our Decision Trees to be too deep, as such a model likely won’t generalize well to unseen data.
Without question, Decision Trees have a lot of things going for them. They’re simple models that are easy to interpret. They’re fast to train and require minimal data preprocessing. And they hand outliers with ease. Yet they suffer from a major limitation, and that is their instability compared with other predictors. They can be extremely sensitive to small perturbations in the data: a minor change in the training examples can result in a drastic change in the structure of the Decision Tree. Check for yourself how small random Gaussian perturbations on just 5% of the training examples create a set of completely different Decision Trees:
Why Is This A Problem? In their vanilla form, Decision Trees are unstable.
If left unchecked, the ID3 algorithm to train Decision Trees will work endlessly to minimize entropy. It will continue splitting the data until all leaf nodes are completely pure - that is, consisting of only one class. Such a process may yield very deep and complex Decision Trees. In addition, we just saw that Decision Trees are subject to high variance when exposed to small perturbations of the training data.
Both issues are undesirable, as they lead to predictors that fail to clearly distinguish between persistent and random patterns in the data, a problem known as overfitting. This is problematic because it means that our model won’t perform well when exposed to new data. There are ways to prevent excessive growth of Decision Trees by pruning them, for instance constraining their maximum depth, limiting the number of leaves that can be created, or setting a minimum size for the amount of items in each leaf and not allowing leaves with too few items in them.
As for the issue of high variance? Well, unfortunately it’s an intrinsic characteristic when training a single Decision Tree.
...
Read the original on mlu-explain.github.io »
Although I have to have a machine running macOS Tahoe to support our customers, I personally don’t like the look of Liquid Glass, nor do I like some of the functional changes Apple has made in macOS Tahoe.
So I have macOS Tahoe on my laptop, but I’m keeping my desktop Mac on macOS Sequoia for now. Which means I have the joy of seeing things like this wonderful notification on a regular basis.
Or I did, until I found a way to block them, at least in 90 day chunks. Now when I open System Settings → General → Software Update, I see this:
The secret? Using device management profiles, which let you enforce policies on Macs in your organization, even if that “organization” is one Mac on your desk. One of the available policies is the ability to block activities related to major macOS updates for up to 90 days at a time (the max the policy allows), which seems like exactly what I needed.
Not being anywhere near an expert on device profiles, I went looking to see what I could find, and stumbled on the Stop Tahoe Update project. The eventual goals of this project are quite impressive, but what they’ve done so far is exactly what I needed: A configuration profile that blocks Tahoe update activities for 90 days.
I first tried to get things working by following the Read Me, but it’s missing some key steps. After some fumbling about, I managed to get it working by using these modified instructions:
Clone the repo and switch to its directory in Terminal; run the two commands as shown in the project’s Read Me:$ git clone https://github.com/travisvn/stop-tahoe-update.git
$ cd stop-tahoe-updateSet all the scripts to executable (not in the instructions):$ chmod 755 ./scripts/*.shCreate and insert two UUIDs into the profile (not in the instructions). To do this, use your favorite text editor to edit the file named in the folder. Look for two lines like this:You need to replace that text with two distinct UUIDs; the easiest way to do that is to run twice in Terminal, then copy and paste each UUID, replacing each text with the UUID. Save the changes and quit the editor, unless you want to make the following optional change…Optional step: I didn’t want to defer normal updates, just the major OS update, so I changed the section to look like this:
This way, I’ll still get notifications for updates other than the major OS update, in case Apple releases anything further for macOS Sequoia. Remember to save your changes, then quit the editor. Run the script as described in the project’s Read Me:./scripts/install-profile.sh profiles/deferral-90days.mobileconfigWhen run, you’ll see output in Terminal indicating that you’re not done yet:Installing profile: profiles/deferral-90days.mobileconfig
profiles tool no longer supports installs. Use System Settings Profiles to add configuration profiles.
Done. You may need to open System Settings → Privacy & Security → Profiles to approve.You’ll also get an onscreen alert saying basically the same thing.To finish the installation, open System Settings and click on the Profile Downloaded entry in the sidebar. This will take you to a screen showing the profile you just added. Double-click on that profile, and a dialog appears showing the settings; here’s how mine looked, reflecting the changes I made to remove minor updates from the policy:Click the Install button, which will lead you to Yet Another Dialog; again click Install and you’ll finally be done. Quit and relaunch System Settings, and you should see a message like mine at the top of the Software Update panel.
As I’ve just done all this today, I’m not sure exactly what happens in 90 days. I imagine I may be notified that the policy has expired, or maybe I’ll just see a macOS Tahoe update notification. Either way, you can reinstall the policy again by just running the command again. Alternatively, and to make things much simpler, here’s what I’ve done…
I copied my modified profile (the file in the folder) to one of my utility folders, so I could remove the repo as I won’t need it any more. Then I looked at the install script, which tries to install the profile using the command, and if that fails, it then opens the profile to install it. In Sequoia, you can’t use to install a profile, so only the part of the command is needed.
Once I figured out I only needed to use the command, I added a simple in my configuration file:
# Reinstall the no-Tahoe 90 day policy
alias notahoe=‘open “/path/to/deferral-90days.mobileconfig”; sleep 2; open “x-apple.systempreferences:com.apple.preferences.configurationprofiles”’
Now I just have to type every 90 days, and the profile will be reinstalled, and System Settings will open to the profiles panel, where a few clicks will finish activating the installed profile. We’ll see how that goes in April :).
I am so much happier now, not being interrupted with the Tahoe update notification, and not having the glaring red “1” on the System Settings icon.
...
Read the original on robservatory.com »
MinIO’s open-source repo has been officially archived. No more maintenance. End of an era — but open source doesn’t die that easily.
I created a MinIO fork, restored the admin console, rebuilt the binary distribution pipeline, and brought it back to life.
If you’re running MinIO, swap minio/minio for pgsty/minio. Everything else stays the same. (CVE fixed, and the console GUI is back)
On December 3, 2025, MinIO announced “maintenance mode” on GitHub. I wrote about it in MinIO Is Dead.
On February 12, 2026, MinIO updated the repo status from “maintenance mode” to “no longer maintained”, then officially archived the repository. Read-only. No PRs, no issues, no contributions accepted. A project with 60k stars and over a billion Docker pulls became a digital tombstone.
If December was the clinical death, this February commit was the death certificate.
Today (Feb 14), a widely circulated article titled How MinIO went from open source darling to cautionary tale laid out the full timeline.
Percona founder Peter Zaitsev also raised concerns about open-source infrastructure sustainability on LinkedIn. The consensus in the international community is clear:
Looking back at the timeline over the past years, this wasn’t a sudden death. It was a slow, deliberate wind-down:
A company that raised $126M at a billion-dollar valuation spent five years methodically dismantling the open-source ecosystem it built.
Normally this is where the story ends — a collective sigh, and everyone moves on.
But I want to tell a different story. Not an obituary — a resurrection.
MinIO Inc. can archive a repo, but they can’t archive the rights that the AGPL grants to the community.
Ironically, AGPL was MinIO’s own choice. They switched from Apache 2.0 to AGPL to use it as leverage in their disputes with Nutanix and Weka — keeping the “open source” label while adding enforcement teeth. But open-source licenses cut both ways — the same license now guarantees the community’s right to fork.
Once code is released under AGPL, the license is irrevocable. You can set a repo to read-only, but you can’t claw back a granted license. That’s the beauty of open-source licensing by design: a company can abandon a project, but it can’t take the code with it.
So — MinIO is dead, but MinIO can live again.
That said, forking is the easy part. Anyone can click the Fork button. The real question isn’t “can we fork it” but “can someone actually maintain it as a production component?”
I didn’t set out to take this on. But after MinIO entered maintenance mode, I waited a couple of weeks for someone in the community to step up.
But I didn’t find one. So I did it myself.
Some background: I maintain Pigsty — a batteries-included PostgreSQL distribution with 460+ extensions, cross-built for 14 Linux distros. I also maintain build pipelines for 290 PG extensions, several PG forks, and dozens of Go Projects (Victoria, Prometheus, etc.) packaging across all major platforms. Adding one more to the pipeline was a piece of cake.
I’m not new to MinIO either. Back in 2018, we ran an internal MinIO fork at TanTan (back when it was still Apache 2.0), managing ~25 PB of data — one of the earliest and largest MinIO deployments in China at the time.
More importantly, MinIO is an optional module in Pigsty. Many users run it as the default backup repository for PostgreSQL in production. We did consider several alternatives, but none were a drop-in replacement for MinIO-based workflows.
We use MinIO ourselves, so keeping the supply chain alive was not optional — it had to be done.
As early as December 2025, when MinIO announced maintenance mode, I had already built CVE-patched binaries and switched to them.
As of today, three things.
This was the change that frustrated the community the most.
In May 2025, MinIO stripped the full admin console from the community edition, leaving behind a bare-bones object browser. User management, bucket policies, access control, lifecycle management — all gone overnight. Want them back? Pay for the enterprise edition. (~$100,000)
The ironic part: this didn’t even require reverse engineering. You just revert the minio/console submodule to the previous version. They swapped a dependency version to replace the full console with a stripped-down one. The code was always there.
In October 2025, MinIO stopped distributing pre-built binaries and Docker images, leaving only source code. “Use go install to build it yourself” — that was their answer.
For the vast majority of users, the value of open-source software isn’t just a copy of the source — supply chain stability is what matters.
You need a stable artifact you can put in a Dockerfile, an Ansible playbook, or a CI/CD pipeline — not a requirement to install a Go compiler before every deployment.
If you’re using Docker, just swap minio/minio for pgsty/minio.
For native Linux installs, grab RPM/DEB packages from the GitHub Release page. You can also use pig (the PG extension package manager) for easy installation, or configure the pigsty-infra APT/DNF repo to install from it:
curl https://repo.pigsty.io/pig | bash;
pig repo add infra -u; pig install minio
MinIO’s official documentation was also at risk — links had started redirecting to their commercial product, AIStor.
We forked minio/docs, fixed broken links, restored removed console documentation, and deployed it here.
The docs use the same CC Attribution 4.0 license as the original, with necessary maintenance.
Some things worth stating up front to set expectations.
MinIO as an S3-compatible object store is already feature-complete. It’s a finished software. It doesn’t need more bells and whistles — it needs a stable, reliable, continuously available build. (I already have PostgreSQL for these, so I don’t need something like S3 table or S3 vector. A stable S3 core is all I need)
What we’re doing: making sure you can get a working, complete MinIO binary, with the admin console included and CVE fixed.
RPM, DEB, Docker images — built automatically via CI/CD, drop-in compatible with your existing minio. We keep the existing minio naming and behavior where legally and technically feasible.
We run these builds ourselves and have been dogfooding them in production for three months. If something breaks, we detect it early and patch it quickly.
I build this primarily for Pigsty and our own usage, but I hope it helps others too.
If you run into issues, feel free to report them at pgsty/minio. I’ll do my best to fix these — but please don’t treat this as a commercial SLA.
Given that AI coding tools have made bug fixing dramatically cheaper, and that we’re explicitly not adding any new features, I believe the maintenance workload is manageable. (how often do you see one?)
Trademark Notice: MinIO® is a registered trademark of MinIO, Inc. This project (pgsty/minio) is an independently maintained community fork under the AGPL license. It has no affiliation with, endorsement by, or connection to MinIO, Inc. Use of “MinIO” in this post refers solely to the open-source software project itself and implies no commercial association.
AGPLv3 gives us clear rights to fork and distribute, but trademark law is a separate domain. We’ve marked this clearly everywhere as an independent community-maintained build.
If MinIO Inc. raises trademark concerns, we’ll cooperate and rename (probably something like silo or stow). Until then, we think descriptive use of the original name in an AGPL fork is reasonable — and renaming all the minio references doesn’t serve users.
You might ask: can one person really maintain this?
It’s 2026. Things are different now.
AI coding tools are changing the economics of open-source maintenance.
With tools like Claude Code & Codex, the cost of locating and fixing bugs in a complex Go project has dropped by more than an order of magnitude. What used to require a dedicated team to maintain a complex infra project can now be handled by one experienced engineer with an AI copilot.
Maintaining a MinIO build without adding new features is a manageable task. The key requirement is testing and validation. and we already have that scenario, which lets us verify compatibility, reliability, and security in practice.
Consider: Elon cut X/Twitter’s engineering team down to ~30 people and the system still runs. Maintaining a MinIO fork without new features is considerably less daunting
MinIO Inc. can archive a GitHub repo, but they can’t archive the demand behind 60k stars, or the dependency graph behind a billion Docker pulls. That demand doesn’t disappear — it just finds its way out.
HashiCorp’s Terraform got forked into OpenTofu, and it’s doing fine. MinIO’s situation is actually more favorable —
AGPL is more permissive for forks than BSL, with no legal gray area for community forks.
A company can abandon a project, but open-source licenses are specifically designed so the code can’t die.
Fork is the most powerful spell in open source. When a company decides to shut the door, the community only needs two words:
Disclaimer: This article is polished and translated from zh-cn by Claude.
...
Read the original on blog.vonng.com »
Can you figure it out?
...
Read the original on apps.apple.com »
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.