Two things can be true at once, and in the case of large language models and software development, both of them are. LLMs are making developers faster. The productivity gains are real, measurable in daily work, and significant enough that teams using these tools well have a genuine advantage over teams that are not. I am not going to pretend otherwise. But the same tools, used the same way, are also making developers shallower—quietly narrowing the range of problems they can reason about independently, eroding the habit of deep investigation, and replacing the slow uncomfortable work of genuine understanding with the fast comfortable feeling of a working answer. Faster and shallower at the same time. Both true. And the industry is celebrating the first half of that sentence while almost entirely ignoring the second.
The productivity gain is real and worth naming precisely
Before the critique lands, the honest accounting matters. LLM productivity gains for developers are not evenly distributed across all work, but in the categories where they apply, they are substantial:
- Boilerplate and scaffolding that used to consume real time now generates in seconds
- Unfamiliar syntax and API surface that required documentation archaeology now surfaces immediately
- Test case generation for known behavior is dramatically faster
- First drafts of documentation, commit messages, and pull request descriptions compress from minutes to seconds
- Exploration of alternative approaches that would have required research now happens conversationally
These are genuine time savings. Accumulated across a working day, they represent a meaningful reduction in friction on the implementation work that consumes a significant portion of most developers' time. I notice the difference on days when I work with these tools versus days when I do not. The gap is real.
But implementation friction is not the only thing worth optimizing for in software development, and it is not the primary bottleneck for the hardest categories of work. That is where the shallowness cost starts to matter.
What shallowness actually means in this context
When I say LLMs are making developers shallower, I mean something specific. I am not making a generational complaint about attention spans or work ethic. I am describing a concrete shift in how developers relate to the problems they are solving.
Depth, in engineering terms, is the ability to hold a complex system in your head, reason about its behavior under non-obvious conditions, trace causality through multiple layers of abstraction, and form independent judgments about what is actually happening and why. It is the thing that lets an experienced engineer look at a production incident and have a hypothesis within minutes based on pattern recognition built from years of deep engagement with similar systems. It is not fast. It develops slowly, through repeated cycles of encountering a hard problem, sitting with it, building a wrong mental model, testing it, correcting it, and building a better one.
LLMs interrupt that cycle at every step. The hard problem arrives. Instead of sitting with it, the developer queries the model. The model returns a plausible answer. The developer implements it. If it works, the cycle ends. The mental model that would have developed through struggle never forms. The developer is faster. They are also less equipped for the next hard problem of the same type, because the understanding that would have transferred did not get built.
Repeat this pattern across months and years and the aggregate effect is a developer who can move quickly through familiar territory with AI assistance and who struggles significantly with genuinely novel problems that require reasoning the model cannot shortcut.
The specific skills being quietly eroded
The shallowness is not uniform. It clusters around specific capabilities:
- Debugging from first principles. When something breaks and the answer is not immediately retrievable via an LLM query, the developer who has spent years debugging independently has a systematic approach and a library of mental models to draw on. The developer who has primarily used LLMs to resolve errors has neither, because the debugging work was always outsourced before those models could develop.
- Reading unfamiliar codebases. Understanding code you did not write, in a codebase with no documentation, requires patient close reading and the ability to build mental models from sparse evidence. This is a skill that develops through practice. LLMs are reducing the frequency of that practice by providing summaries and explanations that bypass the reading process.
- Holding system complexity in working memory. Reasoning about how a change in one part of a system propagates through other parts requires building and maintaining a mental model of the whole. LLMs answer local questions well. They do not help you build the global model, and the global model is what prevents the kinds of changes that look correct locally and break things systemically.
- Tolerating ambiguity long enough to understand it. Hard engineering problems are often hard because they are genuinely ambiguous. The right approach requires sitting with that ambiguity, exploring it, and letting understanding develop before committing to a solution. LLMs provide answers to ambiguous questions, which feels helpful but often forecloses the exploration that would have produced better understanding.
- Knowing what you do not know. The Dunning-Kruger risk with LLMs is real: a developer who has gotten working answers from the model across many problems develops a confidence in their understanding that may not be warranted. The model filled the gaps without making the gaps visible. The developer does not know what they do not know, and the model did not help them find out.
The counterargument: tools have always changed what skills matter
The pushback here is worth engaging seriously. Every major tool shift in software development changed the skill profile of effective developers. Assembly programmers worried that high-level languages would make developers who did not understand memory. Those developers worried that garbage collection would make developers who did not understand allocation. Each generation of tooling abstracted away complexity that previous generations managed manually, and the industry survived and mostly thrived.
Why is the LLM shift different?
The previous abstraction shifts removed complexity that was genuinely incidental to the hard parts of software development. Managing memory manually is not what makes software architecture interesting or difficult. Garbage collection did not abstract away the reasoning about system design, data modeling, or algorithmic tradeoffs. It abstracted away a mechanical concern so developers could focus more on the substantive ones.
LLMs are different in kind because they abstract away reasoning itself, not just mechanical complexity. When an LLM answers "how should I structure this module?" or "what is causing this bug?" or "what are the tradeoffs between these two approaches?", it is not removing incidental complexity. It is removing the cognitive work that builds the judgment that makes developers valuable on hard problems. That is a qualitatively different abstraction, and treating it as continuous with previous tool shifts understates the risk.
The compounding problem across career stages
The shallowness risk is not evenly distributed across experience levels, and the distribution matters for how seriously to take it.
Senior engineers using LLMs are, in most cases, drawing down depth they already have. They use the model to accelerate implementation of things they already understand. When the model is wrong, they catch it because they have the mental models to recognize the error. When the model shortcuts a debugging process, they still have the debugging skills from years of practice before the model existed. The shallowness risk for experienced developers is real but bounded by existing depth.
Junior and mid-level developers using LLMs face a different situation. They are in the phase of their career where depth is supposed to be accumulating through exactly the kind of slow, uncomfortable, independent problem-solving that LLMs are most effective at shortcutting. A junior developer who uses an LLM to resolve every debugging challenge, answer every architectural question, and scaffold every new module is not building the mental models that a senior developer draws on. They are getting to working code faster while bypassing the process that was supposed to make them a senior developer eventually.
This does not mean junior developers should avoid these tools. It means the way they use them matters enormously for their long-term development, and most teams are not thinking carefully about this distinction at all.
How to get the speed without losing the depth
The answer is not to avoid LLMs. It is to be deliberate about which cognitive work you protect from them:
Struggle first, query second
Set a personal rule: spend genuine time on a hard problem before querying the model. Not as punishment, but as deliberate depth-building practice. The struggle period is where mental models form. The model is most valuable after you have developed a position of your own, because then you can evaluate its answer rather than simply absorbing it.
Use LLMs to check understanding, not replace it
After working through a problem independently, use the model to pressure-test your solution. Ask it to find flaws in your approach, suggest alternatives, or identify edge cases you might have missed. This preserves the depth-building work while using the model's breadth to catch what independent reasoning missed.
Read the code, do not just prompt about it
When encountering an unfamiliar codebase or library, resist the immediate instinct to ask the model to explain it. Read it first. Build your own mental model. Then use the model to fill specific gaps. The reading practice is the depth practice. Skipping it is skipping the thing that makes you better at reading the next unfamiliar codebase.
Debug manually before querying
Debugging is one of the highest-value depth-building activities in software development. Every debugging session where you form a hypothesis, test it, and update your mental model is a deposit in the account that pays out during production incidents. Protect that practice. Use the model to explore hypotheses you have already formed, not to replace forming them.
Where I land
LLMs are making developers faster. That is genuinely good and I am not giving up the productivity gains. But they are also, used without deliberate thought about what cognitive work to protect, making developers shallower in ways that will not be visible for months or years—until the hard problem arrives that requires depth the developer was supposed to have built by now and did not.
The developers who will be most valuable five years from now are not the ones who used LLMs most aggressively to maximize output velocity. They are the ones who used them selectively, protected the slow uncomfortable work of genuine understanding, and arrived at hard problems with real depth to draw on—depth that the model could accelerate but could not substitute for.
Faster and shallower at the same time. You get to choose which half of that sentence defines your relationship with these tools. Choose carefully. The depth you skip building now is the depth you will not have when you need it most.