on AI, software engineering, and the value of understanding what we build.
2026 began with a promise that was becoming increasingly difficult to ignore: software development was changing, perhaps much faster than most of us had anticipated.
With rapid improvements in large language models, especially frontier models, AI-assisted development was moving beyond autocomplete and isolated code generation. Agents were becoming capable of navigating repositories, implementing features, running tests, and participating in increasingly complex engineering workflows.
At the time, I was working for a US-based company in the cryptocurrency industry. We had a relatively small engineering team, and the pressure to become more productive was constantly increasing. Naturally, we began incorporating AI more aggressively into our development process, and I think this is happening across much of our industry.
Every week brought new models, tools, benchmarks, and promises about what developers would no longer need to do themselves. Somewhere between the excitement surrounding these technologies and the growing pressure to adopt them, I started asking myself a different question:
If everyone has access to increasingly capable AI agents, what will actually distinguish a good software engineer from someone who simply knows how to use them?
After finishing that project, I decided to make a deliberate change in my career. I reduced the time I was dedicating to client work and started investing more heavily in experimentation, open-source projects, and studying the technologies emerging around AI-assisted software development.
I explored agent workflows, retrieval-augmented generation (RAG), Model Context Protocol (MCP), context management, skills, and different approaches to orchestrating interactions with language models.
My intention was not simply to become more productive. I wanted to understand how these technologies were changing our profession, which skills were becoming less valuable, and, more importantly, what knowledge would remain relevant regardless of how capable the models became.
Interestingly, this investigation eventually brought me back to something familiar: the fundamentals of computer science.
we are not just writing code anymore
I have often compared software development to civil engineering. Traditionally, a software developer has been expected to play several roles simultaneously: the construction worker, the engineer, and sometimes even the architect, although that dynamic changed during the pandemic. We design the structure, make engineering decisions, and implement the solution ourselves.
AI is fundamentally changing the economics of that arrangement. The act of translating requirements into working code is becoming increasingly inexpensive. An agent can generate in minutes what might previously have required hours of manual work. This is part of the shift I explored in what comes after vibe coding?.
But being able to construct something does not necessarily mean understanding what needs to be built or why. Imagine building a house with machinery capable of laying bricks, installing pipes, and assembling entire structures almost automatically. The existence of that machinery does not eliminate the need to understand the project, structural integrity, materials, safety, or architectural constraints. And a few irresponsible decisions could bring the house down.
If anything, the ability to build faster makes the consequences of poor engineering decisions arrive faster as well. Software is no different. An AI agent can produce a working implementation without necessarily making the best decisions regarding memory allocation, concurrency, resource utilization, system boundaries, or long-term maintainability. Even when the generated implementation is correct, someone still needs to understand why it is correct.
This distinction becomes particularly important when software moves beyond a prototype and starts serving real users. Production systems have constraints that cannot always be captured by a prompt: unpredictable workloads, operational requirements, legacy integrations, infrastructure limitations, and business trade-offs.
Generating code is only one part of solving those problems.
the difference between making something work and understanding why it works
During this period, I also participated in several technical interview processes. Despite the increasing adoption of AI across the industry, many companies continued evaluating candidates through algorithmic exercises, LeetCode-style problems, and system design interviews.
There is certainly room to question how effectively these interviews measure engineering ability. I have discussed some of those limitations in my previous writing on technical interviews. Nevertheless, they reveal an interesting contradiction. While developers are increasingly encouraged to delegate implementation to AI, they are still frequently expected to demonstrate technical reasoning without relying on those tools. And there is a good reason to preserve at least part of that expectation.
A developer who can generate a solution is not necessarily capable of explaining its complexity, identifying its limitations, or adapting it when the underlying assumptions change. The ability to reason independently still matters. That is also what changed my view on LeetCode: using it to train reasoning rather than treating it as a judgement of worth.
But I became even more convinced of this through my technical studies and the engineering material from Ardan Labs. The more I study the internals of programming languages and computer systems through that material, the more I realize that this knowledge is not becoming obsolete because of AI.
It is making me better at using AI.
A deeper understanding of how software actually runs changes the way you review generated code, the questions you ask, and the optimizations you are capable of identifying. Unlike knowledge tied exclusively to a particular framework or tool, these concepts tend to remain relevant across generations of technology.
And this kind of knowledge does not come cheap. You may have a mental model of how your operating system works, but reaching that understanding is already difficult. Connecting it to a programming language and fitting everything into the same mental model takes time. And this kind of material is not always accessible or easy to find.
frameworks change. the underlying mechanisms remain
Consider what it means to become proficient in a programming language.
At the beginning, much of the learning process revolves around syntax, standard libraries, frameworks, and common development patterns. These are necessary skills, but they represent only part of the picture. As you develop deeper expertise, your attention gradually shifts from what a language allows you to write toward how that language actually behaves.
Recently, I have been exploring this distinction through Java, Go, and Rust.
These languages offer different approaches to memory management, compilation, runtime behavior, and concurrency. Java typically relies on a managed runtime and garbage collection. Go also uses garbage collection, but its runtime schedules goroutines onto operating-system threads. The scheduling and memory-management models differ between the two languages. Rust emphasizes ownership and borrowing, allowing many memory-safety guarantees to be checked at compile time without requiring a conventional garbage collector.
These differences are not merely academic. They influence allocation patterns, latency, resource consumption, performance characteristics, and the trade-offs involved in designing applications. When you understand these mechanisms, your relationship with the code changes.
Instead of asking an AI agent to simply optimize a function, you can investigate whether the actual bottleneck comes from unnecessary allocations, contention, data structures, garbage collection behavior, or a poor architectural decision.
You can use profiling and benchmarks to establish what is happening rather than assuming an implementation is efficient because it looks clean or because an AI model says it is. You also become better equipped to recognize when a proposed optimization is unnecessary or even counterproductive.
This is where the distinction between familiarity and expertise becomes especially visible.
Knowing how to use a language allows you to produce software. Understanding its underlying model allows you to reason about the behavior of that software. And the second ability becomes even more valuable when much of the implementation is delegated to an agent.
AI does not eliminate engineering. it creates another layer of it
There is another dimension to this transformation that deserves attention.
Working effectively with AI agents is itself becoming an engineering discipline. The difference between casually prompting a model and designing a reliable agent-assisted workflow can be substantial. I touched on this transition in from vibe coding to AI workflows, especially the pressure to turn faster implementation into greater productivity.
Once you begin exploring context management, tool execution, agent loops, memory, skills, and orchestration strategies, you realize that the quality of the output depends on much more than the wording of a prompt.
How tasks are decomposed matters. How context is selected matters. How the agent validates its work matters. And how you decide when to intervene matters.
A developer who understands the system being built can provide better constraints, establish meaningful validation steps, and identify problems before they become expensive. This does not mean that every developer needs to become an expert in language-model internals. It means that engineering judgment remains relevant even when the implementation mechanism changes.
The goal should not necessarily be to produce a complete feature in a single prompt. Sometimes, a well-designed workflow with explicit constraints, tests, and verification steps can produce a better result with fewer iterations and less wasted computation. Your mental model can help guide that process toward fewer tokens, fewer hallucinations, and less time spent. AI can accelerate implementation, but acceleration without quality is not necessarily progress.
the knowledge that no one can steal from you
I do not believe that every developer needs to become a specialist in compiler design, memory management, or runtime internals. The depth of knowledge required depends on the problems you solve and the stage of your career.
For someone beginning their journey, understanding data structures, algorithms, operating systems, networking, testing, and software design provides a foundation for reasoning independently.
For a more experienced developer, deeper knowledge of language internals, concurrency models, performance characteristics, and architectural trade-offs can become a meaningful competitive advantage.
And for specialists, understanding the mechanisms behind their tools is often what makes it possible to solve problems that cannot be addressed through conventional patterns alone.
These are different levels of depth, but they share something fundamental: they build the ability to understand problems rather than merely reproduce solutions. It is the same distinction between knowledge and experience that I discussed in on seniority in systems design.
I also do not believe that AI will remain incapable of performing sophisticated optimization, architecture, or debugging work. Models are improving, and many tasks that currently require expert intervention may eventually become highly automated. But that does not make understanding irrelevant.
A tool becoming more capable does not automatically make its operator more knowledgeable. And producing a working system does not prove that you understand the decisions that made it work. In fact, the ease of generating software may introduce a new kind of technical debt: systems created faster than their maintainers can develop an understanding of them.
That debt may remain invisible while everything works. It becomes much more expensive when something breaks.
The greatest opportunity AI offers developers, in my view, is not simply the ability to write more code. It is the possibility of using the time saved on implementation to develop a deeper understanding of the systems we build. We can explore unfamiliar languages, compare implementation strategies, investigate runtime behavior, and test hypotheses faster than before.
But access to information is not the same as acquiring knowledge.
Knowledge requires us to reason, experiment, make mistakes, validate assumptions, and gradually build mental models that allow us to recognize patterns independently. That process cannot be replaced by merely accepting an answer. Because the frameworks we use will change. The languages we work with may change. The models and agents we rely on will certainly change.
What remains valuable is our ability to understand the systems behind those abstractions, adapt that understanding to new environments, and make informed engineering decisions.
Because the most valuable thing you can build as a software engineer is not the code you produce. It is the understanding you develop while learning how and why that code works.
And that is knowledge no one can steal from you.