https://www.linkedin.com/posts/ali-hamie_im-an-engineer-at-m...
> I'm an engineer at Meta and I never felt this uncomfortable doing my job. I'm writing code these days and it all feels like a black box. This new standard of shipping with AI is moving so fast that if we're going to spend our time checking each line of code, it's going to take forever. That means you can end up with systems you've built where you don't truly understand how all the code works. You understand the high level, but not every detail. And maybe this is becoming the new standard. It goes against every instinct of being a software engineer. I'm used to understanding what I'm building. Now I'm trying to figure out how much I need to understand before I'm comfortable moving forward. Are other engineers feeling this too?
How are you handling this?
By that I mean that, rather than let the LLM do the entire structure, I take the time to create the full hierarchy of abstractions and actors that I want my code to do. I decide, at the UML level, how abstractions will sit and interact — and then I make the LLM implement those gaps.
The key thing here is that, by doing this, I DO know how the code works. I may not know how each unit is implemented, but I know each unit’s responsibilities and how they interact with one another. I’ve found that LLMs are HORRIBLE at writing decent abstractions, regardless of how much prompting and skills I’ve given it. It will always get the job done, but it will often be unmaintainable and require bigger and heavier refactors as I introduce more and more requirement changes.
But by flexing my basic design skills, I don’t have those issues. Future refactoring fit into easy abstractions that I’ve designed. And by documenting it up front, I also have what becomes a living document on how this work will turn out.
It takes a few hours up front to plan ahead, but it makes the LLMs job easier and use less tokens, and it makes the code better long term. I don’t care if one unit is suboptimal, as long as the abstraction is right — because at worst I can change an implementation for a better one later.
That’s also roughly what I find after a long English-only session with a coding agent. Our conversation covers a lot of ground... and leaves out details at every level. When I look at what got built, the misunderstandings are fractal too.
I agree it’s important to design it yourself, but UML is only one level. Lately I’ve been using a scratch Python file (or even a scratch repo) that Claude and I both edit. Never mind that I’m writing Rust. Python reads like pseudocode, and pass and ... (Ellipsis) let me skip whatever I don’t care about. At any level where I do care, I can say exactly what a method’s signature should look like, what fields a domain model should have, or how it should maintain key invariants. Everywhere else I leave an ellipsis, maybe a docstring, and let the agent decide.
It works both ways: I ask the agent to sketch the current system in Python, with some details elided and others spelled out. Then I edit the sketch and have the agent push the changes back into the much bigger Rust+Tauri app.
Time will tell whether this approach has legs, but when I want to be precise, I find it sometimes pays to just write some code.
* Bite sized jobs, about what you would expect for a traditional user story.
* 10-15 minutes discussing the implementation. It's almost the exact same conversation I would have with a human engineer. I'm almost entirely asking questions - "How would you do Xyz? What about this problem? What about that problem?"
* At the end of the conversation, both the AI and I have a good understanding of the problem and the solution.
* I say "do it".
* There are occasions when I don't fully understand what it wants to do. I'll tell it to do the work, then we discuss it afterwards. Sometimes we change things, sometimes I just need to see the code to understand.
* It doesn't commit code. I do.
This has been really effective for me, easily 10X'd or more my pace of development. Sometimes I have to ask for a lot of rework and sometimes it one-shots it. But I never ship something I don't understand and it always feels like "my" code. Very satisfying. More satisfying than typing it all out; the typing was never the fun part of programming.
The best results for me have been when I write something 100% in a language and when it is done the AI takes my implementation and ports it to another language.
I’ve also tried spending an hour or two first before handing over control. That way there’s something pre-existing it can work from.
But that's at the scale of <1kloc. Might be different if you're trying to do a bigger feature all at once.
Guiding the LLM (or humans structuring/guiding juniors) to shape the abstractions help make the overall feature and reliability of the code much better.
Just study and get another job.
Comfort yourself with the fact that you did not have to sell your soul, sit through endless woke sexual harassment videos where you are taught that the white man is the source of all evil, and in endless management meetings.
They care a lot about prestige. Or at least did before AI. Don’t know what their current hiring practices look like.
I'm old enough now to be a protected class, so I don't want people accusing me of ageism, because that's not where this is coming from - but there's a type of person that clearly is on their last job of their career and knows it and I try to avoid them like the plague, I've seen it too many times.
Yup (and it's not ageism).
I'm young and stupid but forced by circumstance to practice a certain degree of respect for my own stupidity.
My philosophy is, "I write code like I expect it to outlive me".
Because sloppy work --AI generated or otherwise-- has a tendency to come back to haunt you.
By the time the system starts to experience strain under the weight of its technical debt two years later, the original tech lead/architect of the project has already added it to his resume as another bullet point, found a new job at 20% higher comp, and no longer has to ever look at that code, ever again.
Now it's the next asshole's problem. Good luck!
[0] Freeman & Pryce 2009 for the "internal quality" vs. "external quality" dichotomy
I just approve PRs, because I have to. I don't know what else to do. I can barely keep an eye on all my code.
Actively looking, but job market is rough in Northern Europe. I quit caring about quality in any capacity here, the company has clearly chosen its direction, against my very vocal complaints.
My last interview, with what I thought was a great company, failed when I didn't understand what NLP was when they asked me about it.
> NLP? > Natural Language Programming, it's the future. Claude, GPT, etc.
I laughed, he didn't, looked offended and said it was a serious question. I had thought it was a joke, said "oh you mean LLMs". I tried to answer seriously, of course, but that was my third round technical interview and I didn't get a call back.
Just getting a paycheck and producing mediocre work until I get something new. (Senior engineer, 10 YOE, patents, 3 years at a unicorn).
Also considering becoming an electrician.
Without massive scale weapon-grade NLP coming out of AI PR depts and targeting unwitting programmers, the LLM psychosis wouldn't happen.
Ahem Maybe you should write a shim for the stack you're using to broker domains.
Med school interests me, but I'm a woman in my late 20s who would like to have children, and if I started down the med school road now I wouldn't be done with training until my late 30s, where PA and nursing school don't have that problem. Medical physics is kind of in the middle.
Understand completely. I'm a little older than you but the high chance of giving up a family was the first thing on the negative column when I was deciding paths.
No one cares at best, or I'm callously told "keep up or fall behind" at worst. When that's not even the problem, it just, sucks now. There's no joy in anything I do professionally.
Can I ask where you went? I'm seriously looking for an exit, I just don't know what yet. Maybe a trade, in Scandinavia the pay is actually comparable, I just don't want to go back to school.
It can feel that way with all the hype but I very much doubt that is really true. I see an industry that is rapidly dividing into two camps - "move fast and break things" and "this actually needs to work". Writing everything with agents and merging while barely even checking what is in the PR has become standard practice in the former but it will get you laughed out of the room in the latter.
For the latter, the product looks nice and polished on the outside but makes “steaming pile of crap” a gross understatement.
I never had a better time "professionally" (albeit no income yet!) so far in my whole life! Apparently it's truly like the best of times and the worst of times to be building software, if you have the luck and the luxury to be able to work only for yourself than it's really a great time to be alive.
Often, the lack of meaningfully challenging demands being put on you is punishing on its own. It can feel like a relief for a week or month or year, but if you catch a glimpse of that being your whole future, you can come to recognize yourself as being in a mind-eroding secular purgatory.
This anguish is especially common among on highly capable people, high achievers, and those used to some kind of ambition. And by accident of its history, there's a lot of those people in this industry and some of them are going nuts now.
Pretty much this. I know it sounds melodramatic but it literally feels like rotting.
I don't solve anything anymore. I don't get any spark of fixing bugs. I don't DO ANYTHING, and it's fucking chipping away at me.
Also I'm fairly sure people are going to realise soon that if they're happy with slop, they don't actually need highly trained engineers anymore.
This whole thread actually prompted me to look into electrician work, especially in renewables that we have here in Norway.
I might fix my burnout and go to school to get my 2 year qualification. AI can't rewire a hydro dam.
?!!?
No way. Are you sure? What kind of work? Here there’s still a lot of care needed to navigate the LLM work, like knowing when to make it quit, rewrite, refactor, follow your outline.
It feels magic but when I watch people who don’t know what they are doing trying to make something new (not almost a clone of an existing product), they get pretty frustrated.
I went nuts for years, until I sold off my interest in my dream partnership.
10 years of heaven. All the benefits and none of the downsides of my internal colleagues. Then 14 years of bureaucracy aimed at predictability over progress, which was infinitely more difficult for me to dodge as an external player.
Compensation for my interest was very objectively valued, with the rate of stalled progress priced in. Which made complete sense, while also being its own madness.
I walked away from so much value, but I don't regret it for a minute.
https://en.wikipedia.org/wiki/The_Right_to_Be_Lazy
https://en.wikipedia.org/wiki/In_Praise_of_Idleness_and_Othe...
I can't believe how much mental justification needs to be made by otherwise highly intelligent people (Bertrand Russel wrote In Praise of Idleness) to defend the right to not work.
No, I fully reject any notion of life value coming from work, and those who hold your view are basically jailors to those who don't want our identity defined by work.
I would gladly not work and learn cello and physics independently if that were a real possibility, but it's not, and it won't be within my lifetime.
I had a job in a system that required jobs to survive that genuinely didn't bother me. I actually quite enjoyed it most of the time. Now it's permanently dead and I need something else, or I am homeless and starve to death.
> No, I fully reject any notion of life value coming from work, and those who hold your view are basically jailors to those who don't want our identity defined by work.
Fuck off.
Obviously there are mission critical systems where it matters, but if “your code can be A/B tested” or “slowly rolled out”, chances are, it doesn’t matter.
Tech debt is still tech debt, even if there exist models that can wade through, because it's going to require increasingly sophisticated models navigating increasingly large context windows and increasingly speculative workspace operations to keep making progress on it.
Vendors like Anthropic and OpenAI are laying a pit trap when they demonstrate how their models can independently code their whole platform as it becomes increasingly byzantine and inscrutable because not only do they benefit from internal/prerelease models but they have effectively unlimited spend because all that spending acts as a lure to draw you into their trap.
For everybody else, design still matters. People are just working really hard to make you forget.
That said, I also feel like betting on readability, maintainablility, taste, etc, being an important aspect of software, at least until code will remain some sort of human readable artifact. We'll still want to be able to understand it and perhaps occasionally add/edit by hand, make sense of it without trusting blindly what any kind of program (LLM based or not) will tell us about it, and so on.
Cost of tokens is NOT a problem unless you have a codebase with hundreds of person-years of effort. Not many people do.
There are legitimate FAANG-like companies that have general guidelines about “when to use Fable/GLM/DS” and etc. to keep the costs down. And I think it’ll continue that way.
https://www.cnbc.com/2026/10/11/ai-software-developer-jobs.h...
Doesn't matter as much. The cost of "maintenance" is going down, from what I can see internally, from other companies, and every story i'm reading from other organizations as well. Token budgets are changing, but model quality and effectiveness are going up as well.
> We've also been hearing for years that LLMs are going to automate away software development entirely, but software job postings are steadily rising.
I think this has been contested from day 1. SE jobs might be automated, but jobs might increase, if there's possibility of starting new companies / new investments / etc.
The analogy would be math people with AI vs. non-math people with AI trying to solve some problems. I cannot solve the problems because I have no idea how to prompt it to go specific way. But it doesn’t matter how dirty your scratchpad was, because you got to the solution.
> I don't see it happening on projects that have a minimum bar for security/reliability and long-term maintenance
I don’t get this, because this is currently, right now, at this second, happening across huge projects. My point was that maintainability is less of an issue than before. Also, frankly, over years, I’ve seen bad code that AI wouldn’t even write. So there can be a case that the maintainability/code quality isn’t even going down as the models get better.
You're also find that implementation matters when token spend starts facing real accounting.
I really wish people would become more coherent with their reasoning.
These things are important even in a repo with zero human code. A project that's filled with tech debt will always be hard to work with no matter what latest frontier model you're using.
I think a lot of this can be automated with slop detectors that flag messy unreadable code and make agents rewrite it, but you can't say it doesn't matter.
Sometimes, not enough to believe you that you can do it better. And even if you could, to believe that it's overall worth it to work in that way.
It seems too early to make any conclusion like that. We've still only been doing agentic coding for a year and capability significantly improved during that time. I don't think anybody on either side of this debate can have actual empirical evidence to support conclusions about where it lands longer term.
The main argument I see is that it probably pays to be slightly conservative because the cost of being wrong by maintaining too much oversight and shipping slightly less code is better than the cost of ending up with an entirely unmaintainable system. In some cases it will be more than "slightly less" but in almost all complex pre-existing systems, you are rate limited by a lot of other factors outside of code production, so the effective difference just isn't that high.
And it's debatable how much better they will get quickly. Verifiable rewards for maintainability of software is, I think, possible but very hard.
I also see devs, seniors as well, that have a different experience. At least that's what they say. I stay open to the possibility that I'm doing or seeing it 'wrong', but I'm far from convinced. I do like to experiment like you wrote in a later comment, things may change, and even now I do find it can be very powerful, for the right tasks.
It still applies to all things LLms can do from law to code to art to ideas.
They can do it - and very acceptably - but only to the untrained eye.
They are only as good as their training data.
I expect them to have badder taste for PHP or JS, than, say, Kotlin or Ruby.
That's be because I prefer the code the latter communities have produced over the former.
1. We are working on different types of application and code.
2. I am in a bubble and no-one in it has experienced the standards and productivity that SOTA AI tools can achieve.
3. The advocates are in a bubble and no-one in it has experienced the standards and productivity that expert developers can achieve.
I hope the answer is 1. I fear the answer is 2. I suspect the answer is 3.
Way to come off as incredibly insecure. There's no need to randomly put people down.
The advantage I have is that I know things that are not in the training distribution. Things about our business, our product, and its internal architecture. Without this knowledge, some new features cannot be designed successfully.
Some day I expect that will change, but all the patchwork "memory" systems, all the docs etc, will never get there because at some point they just pollute context. Knowledge has to be integrated, and that is what happens during sleep for humans, during pre-training and RL for models. LLMs are terribly inefficient at learning though and degrade generally during fine-tuning.
That's it. Maybe you should go re-read?
That certainly can be true but I would challenge "generally". If you're working on something novel then it's not exactly unusual for even SOTA models to produce a first attempt that makes some very bad assumptions but then get stuck when instructed to use correct axioms instead. This looks a lot like the suduko meme - without any real insight into the problem the models end up just iterating around in circles and never finding a solution.
You probably won't see this very often if you're working on simple CRUD web applications where there are millions if not billions of lines of code in the training data set. IME you will probably see it quite frequently if you're working on - for example - software to control some kind of equipment or analyse the results of some kind of experiment where the LLM might have little if any training data about the field you're operating in.
Not any time. Your feedback loop must provide some direction so the iteration is at least heading towards an improvement. For novel applications you might not have a convenient way to do that yet.
Even with that provision there's still a possibility of incrementally updating output converging on a local maximum. That local maximum might not be a solution at all. If it is a solution then the model might still miss much better solutions that a real developer with more insight might find.
As you said yourself the models need external feedback if they don't have sufficient information to make adjustments by themselves. In some cases that feedback might be "Start again because you're hopelessly off track". Sometimes even when they start again they just get hopelessly off-track again. That can happen even if they are explicitly prompted to do something else. So even closely supervising the LLM and not using self-iterating agents isn't guaranteed to get good results for novel applications.
For example, Li et al. (2023), Emergent World Representations, showed that a GPT model trained only to predict Othello moves developed an internal representation of the board state, without being explicitly taught the rules or board structure.
I don't really care about the code being unmaintainable. Fable 6 or whatever will take care of it, just like Opus 5.5 takes care of our Opus 4.6 slop. The code working and our feature shipping is way way more important.
> The code working and our feature shipping is way way more important.
I like that we both use AI very differently (you YOLO, I don't) but can agree that these are basically the important questions. If a company or team answers them like you do, I agree with the conclusion (YOLO). To me that's still a decision to be made though, and to be made wisely. Whether to accept the code may become unmaintainable, to forwardly rely on Fable 6 and to ship above anything (or at least a lot) else.
It can keep patching everything left and right whenever a new requirement comes up so it does not "need" to build abstractions, and it will only do it to please the human reviewer.
A year from now Fable 6 will de-slopify, so I don't really care about having pretty abstractions. That's simply not my job anymore. Sacrilege, I know.
On the other hand, I do some stuff with physics simulations and LLMs will spit out arrays like
static constexpr std::array<int, Q> cx{0, 1, 0, -1, 0, 1, -1, -1, 1};
static constexpr std::array<int, Q> cy{0, 0, 1, 0, -1, 1, 1, -1, -1};
static constexpr std::array<real_t, Q> w{ real_t(4) / 9, real_t(1) / 9, real_t(1) / 9, real_t(1) / 9, real_t(1) / 9, real_t(1) / 36, real_t(1) / 36, real_t(1) / 36, real_t(1) / 36};
static constexpr std::array<int, Q> opp{0, 3, 4, 1, 2, 7, 8, 5, 6};
and it's like ??? I know what these are for, but I have no clue if any of the numbers are right :-)
I was working on a ground-up implementation of a SPICE circuit simulator that would generate code to do realtime simulation of electrical circuits. Much of the work was derived from complex mathematics in a couple of papers that I was using as a guide, and from The Spice Manual (3rd edition) which serves as a guide/standard to the Spice language, including detailed mathematical formulae, expressed in standard mathematical notion (but no code) for most of the simulation operations.
When transcribing formulae into code by hand, I had gotten in the habit of providing references to page numbers and equation numbers in comments in the code, and "footnote" comments that provided URLs to the original material.
Imagine my surprise when Claude also provided correct comment references to pages and equation numbers when I asked it to write code to implement diode models.
This is okay as an end user who only cares about features but can be disastrous when something goes wrong and your only hope is to prompt an LLM that might or might not be (a) available or (b) not read into the context of the codebase.
The scope of the problem worsens when you consider that many teams STILL don't own what they write and have SREs serve as their first-line. These folks NEED to know how the hot paths of the services they support work without the memory or tools that your Claude account has. This is much much harder to do on a codebase that's a billion SLOC for no reason with inconsistent code style and questionable tests littered everywhere
I think that writing the code OR having written a lot of code in the past is the only way to REALLY understand what's going on.
Knowing the codebase is often the difference between proactive resolution ("This keeps happening when that log message keeps appearing; here is the PR that fixes this bug; here is the runbook on what to do if something like this class of problem appears") and reactive resolution (increasing quota, which costs money and might not always be possible; rolling back, which might introduce security concerns; etc.)
This is probably the biggest reason why SREs at big tech companies are usually also SWEs or SWE-adjacent.
Yes, agents, but given that AI-generated codebases grow and change too quickly for humans to keep up, the issue becomes over-reliance on agents over time, a Russian Roulette style of problem when the cost to access to these models keeps increasing...
I quote this just to put extra weight on this! It's surprising, the effect size of this statement.
I have very particular ideas about how everything must work. Very particular ideas about which should be the tech stack. Meaning I don't have anything that uses nodejs or Rust.
The algorithms used, the abstractions that matter to me. Almost everything is different to what the AI would do if left to its own devices.
And many times I try to patch the code myself, and succeed, before giving up to the AI.
Anything else leads to insanity.
I've been starting in the ChatGPT web UI, having long conversations, having it generate Markdown plans, and then editing those plans; I hand those off to implementation agents.
Are you working on a team? I've found that for solo projects this works but, even when you own the design of your piece, integrating with the work of several people doing this ultimately leads to even the part you initially designed no longer working exactly as you remember it.
A good chunk of my time now is spend working with an LLM to understand how other parts of the code base work and how they relate to and interact with the product features I manage. But I'm still on a small enough team that this is possible. I'm not surprised at all that an engineer at an org the size of Meta would not even be able to perform this level of work in understanding what's happening.
It still has sharp edges but IMO it’s the best we have at the moment, and it works. It also means we deliver larger volumes of code, but we mostly look for alignment with the original review — so our reviews are more focused on what tests say behavior should be
In essence you basically say that you don't read or review the code produced in detail, you just know the high level design of how the system works.
It reminds me of George Fairbanks’ Just Enough Software Architecture. He tells a story about his father, a civil engineer, who didn’t apply every engineering technique he knew when installing a mailbox. He just dug a hole, put in a post, and filled it with concrete.
The idea is simple: the effort you spend on architecture should be proportional to the risks involved. Sometimes you need careful analysis, sometimes you don’t.
Seems even more relevant now with AI coding.
But yes I agree, I don't let the llm "design" or even "ideate" with it. I have it run validation on my own assumptions and see if those hold up, but even when they don't I am the one to construct the solution.
Anyone who's ever directed AI at a very large legacy code base will have noticed that AI just gets dumber and slower the worse (larger + inconsistent) the code base gets (just like us humans do). The big difference is that with AI you can get to the point where the code base is an unmaintainable legacy mess in a fraction of the time that it used to take. Directing architecture is the only way to mitigate this to some extent.
Do you do this with every repository that's brand-new to you? You refuse to, for example, use an agent to learn where the feature should generally live? This seems absurdly slow for that purpose, because even though humans can still get a deeper understanding of a codebase after several months, an agent can get a much deeper understanding over a timeline of the initial few days. Unless everything is already meticulously documented for you, I suppose.
Yes, new codebase we take the time to learn, as every good developer should. Ramp-up doesn’t have to be that long to get one’s bearings.
Also, changes don’t have to be deadbolted into bad designs. If the code was bad before and you have a change to make, it can sometimes be faster in the long run to redesign it and rebuild it so that it can scale better with future requirements. LLMs make this part fast and easy — but again, requires design, and designs require human brain involvement and understanding of the codebase
In practical terms it probably doesn't matter. But it's still an unsettling feeling.
Sometimes you do need to see how things are implemented before you know that a design doesn’t work, I’ve never been big on large upfront design work and prefer more iterative development. This can still be perfectly done with LLMs.
I’m willing to believe that software you authored or designed yourself is better than whatever Claude spits out. I’m not as confident that the cost benefit ratio pencils out anymore. In fact, I would say most software does not need to be particularly good or well thought out. The vast majority of things I need software to do for me day-to-day at work are so niche and specialized that I tend to prefer AI slop that I can get now for a few tens of dollars at a time and something expertly crafted by a true artisan for thousands of times higher cost.
I am very much in agreement with you. What language do you do the design in?
I have hand written a bootstrap MD file in English, which guided a Greenfield implementation. Do you actually use a UML language?
I suspect there is a missing language (abstraction) here, English can be a bit unclear and ambiguous. Perhaps an AI dialect?
Soon that will be counterproductive and you'll be better off trusting whatever design it comes up with.
A question I've been struggling with, however, is what are the artifacts that live at this level? Are they design docs? Are they standardized? If I read your design docs, will I understand the code or do I need to "decompile" (use a coding agent to explain) to get back to this level of abstract for me to edit it?
Of course I only arrived at the framework after trial and error with an LLM showed me the shape of the problem and the framework presented itself to me.
I have found that making the AI look the plantuml and the rendering and reviewing before presenting it to me results in much more legible designs. To me the peak of this would be a shared interactive whiteboard w the ai during design.
Coding is however just one small aspect of bringing software into being. We generally start with an intuition. That intuition comes from context.
That context, hard won as it is, for software engineers at least, was mostly a side effect.
You had to buffer a significant portion of the system in your mind to be able to manipulate it. LLMs remove that constraint. Now you can just prompt from the upstream intuition and its passable a good portion of the time.
Notice the catch? If you don't get to the beat of the system your interaction with it will be jarring, for yourself, for the person using the software and for the agent building it.
So I made a tool to help get the context into my head faster. No its not an app (it could be but meh) Its a skill that helps the LLM make a voice narrated walkthrough of the complexity so you retain context Check a demo here if you want https://theronburger.github.io/review-story/
Just because something makes you comfortable doesn't mean it's actually good.
It still needs a lot of back and forth before I'm comfortable it's not doing slop architecture, but once it's on thesame page as me it genuinely is better at it than me.
Planning is fundamentally so critical to ensuring alignment and outcomes, that over time how one plans can be better trained into the process to help do more and more of it.
The pace of AI is literally changing so fast that we can't keep up. Your strategy here was probably true around 3 to 4 months ago. Right now it's no longer true. Not many people understand navier stokes and that's what AI can offer, it can offer some superior problem solving methodology with the level of complexity of navier stokes that you have no hope of understanding without years of study and education to get yourself up that level.
This is not a trivial question with a trivial answer.
Unfortunately your answer is outdated. I think if trendlines continue, the answer is trending violently towards putting more and more trust towards AI.
For those of us with practical budgets not measured in the millions, that bar for stupidity is pretty damn low, and I’m not convinced that there’s enough compute being built to account for every idiot in the world.
I'm saying ask the AI something beyond your intellectual capabilities. Or ask it something beyond your intrinsic knowledge base. It can produce a working machine that does the job, but you won't be able to understand it.
You can still verify the machine works by using the machine... but you won't necessarily understand it. Similar to how you own a smartphone... you know it works, but you will never fully understand everything about it.
Until it doesn’t, and then you’re too stupid to understand why not. Was it that your prompt wasn’t good enough? Does the model not have enough context? Is it even possible for your request to be satisfied?
> What does human stupidity have to do with this?
You answered this yourself:
> but you won't be able to understand it.
And:
> I'm saying ask the AI something beyond your intellectual capabilities. Or ask it something beyond your intrinsic knowledge base.
What’s the limit to this? “Claude build me a nuclear fusion reactor.” Surely we can agree that there’s a limit or we’d already be living in some post-scarcity utopia. So what’s the limit?
I’m suggesting a limit: human stupidity is limitless while compute is finite. We can use the word “ignorance” if the word “stupid” is too inflammatory.
You’re unable to comprehend the topic of the conversation.
We’re talking about querying the model with prompts and asking for solutions beyond your intelligence. You’re talking about flawed prompts.
> You answered this yourself
You’re rude af. Not understanding something is not intrinsically bad.
In fact you use things you don’t understand on a daily fucking basis. Your iPhone, your car, your computer. All of these things have aspects of it beyond your intellectual capability to fully grasp or comprehend and yet you use it. Sometimes these people who make these things produce faulty products and you ask for a refund and are more careful in the future about what you trust, sometimes the product is exemplary.
Relying on an LLM to do something for you is much of the same.
Get it? Probably not. What are you going to do? Call me stupid again because you have nothing substantial to say? lol.
Edit: you edited your response which is uncharitable. But whatever I’ll respond to your new statements below.
> What’s the limit to this? “Claude build me a nuclear fusion reactor.” Surely we can agree that there’s a limit or we’d already be living in some post-scarcity utopia. So what’s the limit?
I agree. But this is not the topic of conversation. I’m talking about querying LLM to do something beyond your ability. This is something you can do and also stay well within the obvious limit you’re implying here.
> I’m suggesting a limit: human stupidity is limitless while compute is finite. We can use the word “ignorance” if the word “stupid” is too inflammatory.
You don’t need to suggest a limit. It’s obvious ai can’t build a nuclear reactor. Nobody needs to be told this.
A clever quote doesn’t prove shit. Ok so human stupidity is limitless. Computer is also finite. I agree.
How the fuck does the above two facts which I agree with have anything to do with me asking the LLM to make something I have no knowledge about? “Hey LLM write an emulator for this Zelda n64 game.”
The prompt is well within the “limit” of what an LLM can do. Compute is still limited. And human stupidity is still boundless. Nothing has changed. Why? Because someone is ignorant and unaware they are prancing off topic onto orthogonal concepts.
But in your argument we are all essentially idiots compared to AI, and should essentially embrace that, dunno why you take offense to it. I essentially agree. Where I disagree is how far we can make it without human intelligence in the loop because of finite compute power.
I'm saying right now in your personal workflow you are giving up many useful features of AI when you only allow it to implement your design as opposed to letting it design for you.
The company got successful. At some point I had a team, and then multiple teams. Gradually, I had to let go of that level of understanding and delegate it to others. When I was overseeing 50+ engineers, I only had a high level understanding of most the systems, and only got involved in lower-level decisions rarely. All the new code was created by my team, and it varied in quality.
If I had AI tools back then I could ask it to explain things to me that my team did and have a 2nd set of eyes which had a different set of biases and abilities.
That's how I deal with it right now. I ask one AI to gen the code based on a design. I work with the AI tool and see how it builds the system out. I intervene where it starts doing things I don't want it to do. I ask another AI to review the PR after the code is written. I sometimes dig into design choices and ask it to explain them, rate them, and offer other alternatives. I sometimes change things, and occasionally scrap the whole thing and start again with better guidance.
So far I've written more code without an outage this way than ever before with a team of human developers. AI screws up too. But right now, at Opus / ChatGPT Sol and higher models, on average, it makes fewer mistakes than humans did. Any time they slip through is because I am rushing too quickly. In a pre-AI human dev team we would call this a process or management failure.
The more dangerous a decision and the harder it is to roll back, the more time I spend reviewing it. This has served me well when managing people who write code, and seems to work well with AI.
AI is a tool that needs to be managed. It won't do all the work for you, but it can speed you up greatly.
Scale that to a project with several developers and several teams, and the result is that, for the engineering manager responsible for the project, correctness and conformity to requirements need to be managed and achieved without the manager having to review and understand every line of code. That would be impractical. The manager will need to use other strategies and tools to achieve this, including but not limited to:
- Mature product development and quality assurance processes;
- Sound architecture and system design;
- Adherence to software development principles and best practices;
- Comprehensive automated testing to validate expected behavior, prevent regressions, and ensure system reliability.
So now developers are moving a bit higher up the tactical/strategic scale and facing challenges that used to be handled only by technical engineering managers. We've become coding agent managers now.
With AI, everyone now gets to act like a PE. Your job is to design, the AI builds like the junior engineers. Then you review their work. You may never understand the details, and that's ok, as long as you know the main ideas and where to ask for the details (in this case the LLM instead of a human).
Or maybe keep manual coding to build confidence, so that one can say "I've done that before..." and be able defend or take apart a particular strategy.
But you never heard someone say "you should still code in assembly so you can understand what your compiler is doing".
Is this more like case one or case two? Will it matter if you understand in a few years?
Would I routinely code in assembly today? Not for (almost) any new project.
Can I understand assembly and have I used that skill to understand performance problems or debug unexpected behaviour (including some that really did turn out to be caused by bugs in popular compilers)? Absolutely.
I'm not generally a fan of the comparison between what LLMs do and what compilers do. Compilers might vary in their output for given source code but they still follow (hopefully) well-defined specifications. Their behaviour is deterministic and the behaviour of their output is deterministic. With sufficient understanding you can reason about that output or diagnose faults in the compiler behaviour if you ever need to. None of this is true for LLMs.
I don't think I've seen a Sol 5.6 or later model fail at a task with a verifiable outcome.
It's when I try to do too little of the defining that I get LLM bugs. In which case maybe programming hasn't changed that much, LLMs really can be reliable compilers, and we're all just being lazy.
Any quality CS program will have you do that for a year or so, plus theory.
But it was a CS program, not a SWE program, the two of which the industry has confused for all of time (though there was an actual Software Engineering course offered as well).
I don't think llms are an abstraction - they're a delegation.
i spent a few weeks coding assembly at the beginning of my career. really enjoyed it and feel like it helped me understand CPUs a lot better. i'd still recommend it to early career engineers
You should only have a high-level understanding and be able to orchestrate things, plan, and make choices. AI do the details.
Stop drinking the cool aid and compare compilers with ai models.
It's also a lot of fun to do on tiny 8 bit microcontrollers like the AVR. It's really neat to be able to understand exactly how many clock cycles some code will use. This came in super handy for me once when I wrote a small bit-banging serial interface and had to debug why some characters would randomly be wrong.
It’s perhaps important to learn the consequences of certain design and impl details? Because Sonnet-class models can often miss these more easily than Fable-class.
That said, refactoring and bug-fixing is faster than ever. So I’d argue LLM coding is actually quite a bit different than having a compiler. In ways we’re still seeing ?
I'll add to this that your job is also to design tooling and feedback loops that allows the AI to improve over time.
You have policy gates: How the AI should act. (and a feedback loop to check the code is adhering to those policy gates)
You have quality gates: How the AI verifies it's done things correctly (testing, linting, PRDs, cyclomatic complexity)
Modern AI engineering is really about improving this system rather than manually reviewing AI output. The better the policy gate and quality gate enforcement, the less manual review is required and the more velocity you achieve.
Sticking with being a human-in-the-loop reviewer of AI code is where people lose all hope in still being software engineers.
1. How to develop Principal Engineer skills? Designing and communicating the design correctly is not straightforward.
2. Are we sure this approach works better? Current AI systems are very good at solving problems and SW because of the Reinforcement Learning they went through. I suspect they perform better when doing things their own way. Forcing them to change to an arbitrary workflow may confuse them and reduce performance instead of enhancing it.
Second one is a version of the "Bitter Lesson" adapted to SWE.
As engineers our job is to solve problems at various levels of abstraction. Developers that think we are just automating away our own jobs have missed the point. It's not about writing the code anymore, it's about designing solutions to problems and distilling that to agents in a way that achieves the outcome you wanted. Models are not good at coming up with this direction on their own, nor are they good at designing cohesive user experiences across a plane of complexity. These two areas require most of my time now while actually writing the code has been delegated away.
I've heard this before many times, and it's simply not true. The resemblance is there, but there's also a fundamental difference. The difference is in whose name is on the commit, who embraces responsibility for things going wrong. PE will, at most, share responsibility with the person who actually authored the code, AI user is always responsible for whatever code their agent wrote.
As a PE you may skip reading all the code written by your underlings because your underlings have signed a contract with your employer and will be legally responsible for certain things, have they decided to do those.
In the case of actual PE, there's an actual programmer who, at least, believes he or she understands the code they wrote. In the case of AI, there maybe nobody.
Here's an interesting aspect though. There are companies (I'd know, since I worked for one), who sell a product where there's a part of code no engineer working for that company understands. In my case, that piece of code was a formula obtained from some former partner (a bank) to calculate something related to credit check. Nobody could explain why the formula was the way it was, but it was extensively used in testing to validate that our code produced the intended result when it came to this part of credit check. There's a folk tale about DB2 containing a binary blob whose source was likely written in Assembly and was since lost to time, which is essentially a magic component nobody really knows the internal workings of. I'm sure there are more pieces like that around in older companies.
Since I expect the regulators will soon start tightening the nuts on programming in general, especially since AI is asking for such an action, what will happen to these "magic" programs? If I were a regulator, I would have the company, under a threat of criminal punishment in some cases, to vow that they understand the code that went into making of their product and are therefore responsible for what it does. This is not the case today and only in a very few cases were software companies found guilty of criminal negligence... but I think that the writing is on the wall: with AI being used the way it is, the legislation requiring understanding is just around the corner.
I liked working with people. It's a lonely road ahead.
That’s not to say I blindly trust an LLM - my project is an autovectorising compiler. I have it write a bazillion tests, and I have real, non-trivial programs that it compiled. I have it run a ‘gate’ before releases that takes literally hours on 5 different architectures. To say this code is tested is … masterful understatement.
I think what helps is a long planning session, going into details, working out corner cases, designing test-cases, you know work and effort. Once that’s done it’s easier to let it work through the night and do a major production feature.
I still review the code, but hand on heart I couldn’t say I’d reviewed everything, sometimes I miss something in a complicated check-in. I try to keep checkins small and contained, but sometimes that’s not easy to manage alongside the “don’t break stuff” rule as well.
So it comes down to: I’ve been working on this for 6 months or so, and so far this has worked out for me. You know, kind of like getting used to a colleague’s idiosyncrasies over a period of time.
I will say that in my experience the code quality is fairly high. Maybe that’s down to the effort put in ahead of time in the planning stages, I don’t know, but I don’t seem to suffer from the ‘slop’ that people complain of. YMMV.
That doesn't mean I don't like progress mind :) This morning I got the preliminary results of a 'C' front-end to the compiler (it uses clang to compile to LLVM IR, then translates to our IR, then feeds that IR into the autovectoriser as if it had parsed the code itself). So for the exact same source code I now have:
Those are some pretty fine speedups, from naive C, with no alterations from the standard "C" code - it automatically finds places that can be made data-parallel, tracks the data-hazards to make sure its safe to "GPU" and writes all the kernels, boilerplate and data-stream-management if necessary. Obviously the checksums have to match, so I know it did the work correctly too :)Hacker News isn't interested - any post I make linking to the webpages is immediately blocked/dead so I'll wait until the C++ front-end is ready (some time next week) and announce it somewhere else. A compiler that takes standard C/C++ code and speeds it up by automatically finding data-parallel areas and scheduling them on the GPU with no programming overhead is pretty rare.
Oh, and it manages the local network as well, so you can run xcd on machines on your lan, or via the TB4/5 bridge between Macs and it'll schedule on other GPUs, just like it manages multiple GPUs on the local machine. It characterises both latency and compute-time to figure out which devices are worth scheduling on.
For anyone wondering, the thing lives at https://compile-xc.org/ ;)
Still working on some of the data-parallel recognition in LLVM IR space. In order to support both C and C++ with the same data-parallel section extraction, I moved the detection from the ‘xc source code’ phase to the IR lowering phase, meaning that everything could ‘feed in’ to the same routines. That’s caused a little friction with the slightly different ways the LLVM IR from clang and xc’s IR encode loops. The above results are all single-loop extractions and the double-loop ones are still ongoing.
The system works in 3 stages: 1) by being greedy about which loops could possibly vectorise and marking them, 2) by analysing the IR to reject loops which do pointer-aliasing, or object manipulation, or… and then 3) by measuring the fastest way these known-safe kernels could execute.
Which brings me onto one nice thing I haven’t mentioned: that the GPU choice(s) are made at runtime, not compile time, and the system automatically measures (and then persists so it doesn’t have to measure again) the fastest way to run a kernel on the current machine. It handles MSL on Apple devices, PTX on Nvidia cards, Spir-v on Windows, Linux, and Android, and WGSL in the browser. It’ll quite happily divide up the parallel task across a 3090 and a 5080 on my Windows test box for example, with the 5080 doing ~2/3 of the work, because the scheduler is opportunistic.
All without writing anything other than plain C with nary a mention of the GPU. Give me a week or two, and I’ll probably announce it on phoronix (which might then get a mention on HN if it goes down well there, and it’ll be a different url so hopefully won’t be insta-dead).
I wouldn't be too sure about that :)
I think it’s my own fault, I’ve posted the link to the compiler pages several times in what I thought were relevant posts, and I think HN thinks I’m spamming. So I’m not posting the home page for a while and we’ll see if a future submission “takes”
Bottom line: GPL3 for the compiler, standard library exclusions for derived works, so use it for anything but feed me back improvements to the compiler itself, and you don’t get to redistribute it as your own.
The C front-end I'm currently working on is something that looks at the LLVM IR that clang produces and converts it to xc-IR, then runs the data-parallel extraction through the same routines as the original xc language. Consider it to be a 'Y' branch on the inputs - you can feed it LLVM IR or xc source code, they both get convered to xc-IR and from then onwards it's all the same.
So yes, you get tiled work-units, overlapping in time between compute/data-transport and tuned so that actually works, and an opportunistic scheduler that sends them to whatever worker can handle it at the instant of being sent. That opportunistic nature means the faster cards (eg my 5080) will naturally get more work, because the scheduler just keeps all cards busy. The divide between the 5080 and the 3090 in my Windows test box is about 2:1.
But the scheduler is also aware of networked GPUs - my M4 max has a pretty fast GPU, but it's connected to my wife's M3 Max by thunderbolt, and I measured a 37 Gbit/sec connection, so my app can send data to be processed over that very fast link and get results back. If you have a 10GBit nic, then it will work pretty well too, if your nic is "only" 1Gbit then modern GPU speed means that unless your work-units are really computationally intensive, it'll be faster to do it locally - and xcd will work that out at runtime and adopt that faster approach.
A.k.a. "The concrete didn't crumble when I hit it with a hammer."
Process quality has been a thing for ages because it is essential.
Except it’s about 1000 “hammers” (aka tests), x 9 different architectures, x 2 different (a reference and a production) variants, and x 3 different optimisation levels. Every time I find something that slipped through, it gets at least one test written to cover it in the future. My main time-sink is the hours it takes to run the ‘gate’ before a release across the test-farm. And that’s after I’ve been playing with it myself for a while, pushing corner-cases through whatever I’ve been working on.
And who decides what hammer to use, or do you even know what hammer is used? 1000 toy hammers from walmart or temu don't mean anything.
If your definition of "works" includes "doesn't have vulnerabilities", you're going to need to do more than just poke at it and add tests.
It's not perfect, but vibe coding means you don't even get that.
Jesus F Christ! No wonder most software lately (~decade) has been shit. Have y'all not heard of code review? Or did you think it just for show...you sit on a change for a week and then press "Accept" without reading it?
FWIW I was a software engineer for the last 35 years or so. Amongst other claims to fame, pretty much all Apple devices on the planet (those actively running Apple OS’s, anyway) are running my code, within AuthKit in fact, and one code review was insufficient by policy when making changes. Despite all that, I’d be lying if I told you I thought there were 0 bugs in the actually-rather-complicated repo… Maybe these days, running it by an LLM would be a good idea…
Lots and lots of testing. I usually interactively test with the LLM. Now that I'm spending less time, coding, I get to spend more time, testing.
In my experience, I test more, with an LLM, than I did, with my own code. I don't find coding errors, but I do find implementation errors.
On another note: I ran an LLM on a bunch of really mature code that I wrote, and could swear was "perfect," and it found a number of issues.
Nobody's perfect. Trust; but verify.
What have we gained aside from shifting where time and effort is spent in the design loop?
The verification is what we always wanted but never had time for. For 30 years I wrote side project games without extensive testing or a fuzzer or anything because I wanted to spend my time writing the game. Now I can do that and still have the testing I always knew I should be building.
This method also helps with the classic problem of being unable to identify essential complexity up front.
This truly is the worst thread on HN. Now we're down to explaining 102 topics. Geez guys, what did we gain from evading regression errors? Who knows!
Right now, we're in a "honeymoon" stage, where non-engineers think that they can somehow, magically produce stuff that is comparable to what experienced and disciplined engineers can do.
That won't last long.
Despite all the caterwaulin', LLMs are just another engineering tool, and good engineers will figure out how to implement them, in ways that absolutely blow away the garbage produced by non-engineers.
It's like that rich, no-talent guy that buys a PRS guitar, and thinks he'll beat the experienced musician that still uses his beat-up old strat, but consistently makes hits.
In my optimistic days, I think we'll start putting mechanisms in place that will have LLMs help develop cognition, skills and expertise. It'll usher in an era of mastery and wonder.
On my bad days, I despair that the siren song of "good enough" slop will overwhelm even the most disciplined minds, and we're facing the atrophy of civilization.
Will this just end up exaggerating the pereto distribution? Will there end up being this tiny percentage of people that are driven by natural curiosity to do the very hard work of really understanding hard things? Will society even have any sort of reward for those people?
This is why LLM coding fails.
- code? up - build budget? Up - developer output? Up - developer oprrational costs? Waaay up!
Why is this Bad? Every one involved profits somehow.
If someone finds it burdensome to review my 50-line AI-generated bug fix, adding 200 lines of tests only gives them more lines to review.
Unit testing imo is pre-LLM. It was mostly to keep the human honest because the human can't possibly read all the code to make sure nothing broke.
Are you maintaining a list of all invariants you want QA'd by the LLM after each change? How many tokens does this process take?
Claude Code (Opus 5.5 xhigh), when it makes changes, tests the relevant endpoints for behavior based on what it touched. You could also just explicitly ask it to as a follow-up prompt. You do need to have instructions on how to set up a test environment or use infra for which dev env testing is well-documented online.
And yes, it's non-deterministic. It's not gonna test everything exhaustively or systematically like a fuzzer. It's basically doing what a manual QA would, at best. It probably doesn't even cover every edge case each diff. But I'm fine with this tradeoff for not having unit tests.
Caveat emptor:
1. These are relatively small-medium programs (~10k LOC) where the frontend and backend code reside in the same repo, and the frontend is strictly a web app, and where 100% of the functionality can be tested in the dev environment (incl the database).
2. I'm the only person working on these repos right now.
3. I read every line written by the LLM and have things I don't like refactored or stashed and rewritten with a modified prompt, mostly based on intuition.
>How many tokens does this process take?
I genuinely believe anyone who exceeds usage on a $20 plan doesn't use Claude Code correctly. Always use a new context wherever possible. Always make surgical changes (or, broad changes with well-defined success criteria). For anything complex, get a plan from the LLM at least so that you know it understood your ask. Ctrl+C and `git stash` are your best friends. Keep everything clean, don't build castles on garbage. In the LLM era ironically you should never put off tech debt because it will just compound.
I take a more "hybrid" approach, myself, but I also have control issues. I am slowly moving towards all-LLM, but I'm not quite there, yet.
The more types of potential errors your compiler catches, the fewer unit tests you need. Haskell needs fewer (and potentially different) tests than Perl or Python.
The other thing where unit tests really shine is testing a complex algorithm. If you think of a problem with a very well-defined output (like sorting, searching, some types of computation, graph algorithms, geometry problems etc.), then it's pretty easy to come up with a stable API and thus stable tests. I'm pretty convinced that unit tests give you more bang for the buck than plain-English test plans that you need to spend tokens on to evaluate.
Have you ever worked on existing code base that has components that makes no sense but must be there?
What?!
Unit testing was to ensure the code operated according to an intended specification or outcome especially when it deals with a complex support matrix. It extended well beyond the codebase itself! You can have language errors where output behaviors differ between one supported version to another (ex. Java 11 vs Java 17), runtime errors that are specific to operating system quirks (ex. RHEL 8 vs RHEL 9), or workarounds added to your codebase for known bugs in different dependency libraries you support at compile/run time (ex. Net-SNMP 5.2 vs 5.5). It wouldn’t matter if someone read all your code; you would still have the potential for errors caused by the components needed to run your software.
If your project doesn’t have to worry about these types of issues then lucky you and continue on without unit tests for as long as you can.
The name of the test should indicate what it's doing.
For unit tests, these should check only one thing, and the test can be structured into "assemble, act, assert" style tests.
On the implementation side: code which touches state is tricky. -- So, minimize the amount of code which touches state (or rather, shift as much code as you can to stateless code, which is easier to bring under test).
In systems where I've taken complex inputs and transformed it into complex outputs, I'd make it as easy to bring each particular pass under test, have snapshots that the same transformations resulted in the same outputs (insofar as I did want that consistency).
I'd think with a compiler, though, it can still be practical to describe the input you depend on as a value.
In distributed systems, your service might rely on another service which might be broken, might return different values than you expect, might return slower than you expect, etc. and so testing the parts of your system which depend on other services can be tricky.
Point is, you can arrange a lot of your code into "it's easy for me to ensure this part works", and you can see wins from that, even if as a whole the full system can be difficult to bring under test.
I have no shame in accepting vast quantities of ai generated automated tests at review time without really paying attention to them. It’s better to spend more time on the core implementation.
I respectfully disagree. IME writing a good test suite is one of the areas where current LLMs perform worst. They certainly like to write plenty of tests but those tests aren't necessarily adding much value and they don't necessarily cover the important functionality.
Given how heavily LLMs and specifically agentic workflows still rely on producing something vaguely correct and then iteratively applying guard rails to reach something that actually works I humbly suggest that trusting a very substandard test suite to be one of those guard rails might be unwise.
I realise this is heresy, but my conclusion is that unit tests aren't important any more. E2E testing on the other hand...
(I'm agreeing with you about the quality of LLM tests, I'm just trying to make lemonade out of all these LLMons).
All those extra tests are churning through tokens and CI resources. These are far from free in a direct financial sense. I've also noticed some of the more recent models will try to do a lot of experimentation and ad-hoc testing by spinning up shells or running something like Python scripts direct from a command line. Every time they do that you either have to review whatever they propose to run carefully - which is slow and tedious work - or stick the tools in YOLO mode and hope that your agents don't accidentally wipe your entire filesystem, open your firewall for malware to take over your network, and perhaps launch a few nuclear missiles.
I would respectfully disagree about the value of unit tests as well. The reason unit tests appear to have less value in the LLM/agent era is because current LLMs suck at decomposition and other architecture-level work. They just see a mass of code and change it as a mass of code. This probably results in orders of magnitude more code being written/changed than would have been necessary for a skilled developer working on a well-maintained code base to solve the original problem and that automated work also takes longer in wall time. Again this gets brushed under the carpet because a lot of people trust that LLMs are so good that their costs are negligible regardless of how much work they do. People used to say that about the Cloud transition as well because not needing so many in-house staff to manage your resources was going to be a huge saving. Today we have companies bringing in consultants because they literally can't even work out how much they're spending on AWS by themselves. Instead of having a few greybeards who could make a Linux server do anything we have a few increasinglygreybeards who know which combination of 17 AWS resources they need Terraform to spin up so they can write the web server equivalent of hello-world.c. Meet the new boss - same as the old boss. :)
I have an assumption that inference tokens are only going to get cheaper, I admit. We could get at least 10x efficiency savings by burning current models to ASICs. But today they're cheap enough to let the machine churn - that's the environment I'm making decisions in.
> I would respectfully disagree about the value of unit tests as well.
I honestly don't think the LLM-generated tests are at all valuable in a human sense. I get the impression the end result is "fewer bugs" (but I've never tried to prove that). That's why I'm happy for the LLM to have a directory it can write its own tests in. It's a scratch space.
> this probably results in orders of magnitude more code being written/changed than would have been necessary for a skilled developer working on a well-maintained code base to solve the original problem
Nothing gets committed in my name that I don't understand. I'm one of the people LLMs are actually making slightly slower, because I insist on reviewing the code and working single-threaded.
I feel like your suggestion would be a nice organic extension of that same practice (at the cost of more infrastructure/tokens)
The goal is to get to a point where I can say with a good level of confidence that a change didn't break anything. It might be the wrong change, and it might be crap code, but I can ship it without anything blowing up. I've been using it for a while across a few game projects and so far it's worked quite well.
Black-box e2e testing isn't suitable, or practical, for a lot of applications. Unit and integration tests generally require you to understand the code you want tests for, to know what to actually assert
Are you just trusting the AI to do it all?
Testing is NOT verification.
EDIT: Sorry, oops, it's also called a kind of verification.
But there is the problem that tests only check or verify a small sample of possible cases or inputs. They can never show that the program is correct. (Famous Dijkstra statement.) To know something is really correct you have to understand and prove it.
Even if your test suite tested every single possible behavior and code path, tests usually can't intercept all possible unintended behavior that an LLM might have added.
In all likelihood, most test suites don't intercept any unintented behavior (such as unintended internet connections).
That's not a big problem when a honest human has written or reviewed the code, because unintended code is usually easy to spot.
But an LLM might have dropped any whatsoever code: maybe some of your project's code resembles the one of a trojan in its training, so it completes it with the trojan.
Or maybe it found that if your server is unreachable, it can upload the client's data to a public Github repo; or that if there's no space left on the device, it can delete some of the user's folders, easy!
Would your clients be understanding if that happened?
Eventually, the tools become trustworthy, and we no longer need to worry about their products.
We're just about there, with frontier LLMs, but I don't think we're all the way there, yet.
English isn't a good programming language if you want to trust that the output will be as you want it to be. High level code has no room for ambiguity - that's why we use it. It's a concise, very explicit language to tell the compiler exactly what to do.
Dealing with an LLM is a fundamentally different thing. The reason it's able to speed us up is because it makes assumptions for us and reads between the lines. The only world where we trust the output is one where it can literally read our minds. That's not coming any time soon
The human developer+compiler+linker toolchain isn't free of bugs either, some percentage it will produce code that fails loudly and sometimes it will produce code that has silent bugs, be it due to wrong source code or due to compiler or linker bugs.
LLMs instead are inscrutable, can output whenever at any time and you can't "fix their bugs"!
And don't attempt to place the probability of error of LLMs and normal software on the same plane, LLMs for the time being are wildly more unpredictable.
1. The reason OOP complained about AI code being shipped without comprehension is because they want to ship of it more and faster. If they had time, they'd review the code. Writing tests requires a lot more time than what people usually spend on code reviews (unfortunately, but that's how it is). And if you think you can generate tests, again, by using AI, then, the bad news are that now you have both the code you don't understand and the tests you don't understand. Don't forget that running tests also takes time, in some cases it takes longer than to write code. So, again, your benefits obtained from AI-generating code won't materialize.
2. Even if magically you were able to procure decent-quality tests (without understanding what you are testing!) There are still problems not addressed by testing s.a. reusability. If you don't understand what the code does, how can you possibly reuse it for something else? Perhaps a small modification or a generalization of the existing code would allow you to reuse the mostly tested codebase for the new feature, but since you don't know what it does, you've missed that opportunity, and, again, the AI-generated code came second in the race to ship the product faster (also, you've got a worse product).
* * *
The question of "how to deal with code we don't understand?" is by no means a new one. Dijkstra's structured programming was a response to precisely this question. His argument showed that programmers can't even anticipate all the potential problems to be found in a 16-bit adder and that exhaustive testing is prohibitively expensive. Therefore, he argued, we should restrict the kinds or programs we write to those that are easier to understand (and that's what he called "structured" programming).
Perhaps, we can find a similar sort of harness for AI writing code, where we can impose boundaries it cannot cross that ensure understanding. I have no idea what that can look like, but I'm absolutely sure that testing isn't it.
You must read the code before shipping it to production.
"But I don't have time!" That's not even an excuse. At least one person in your company should be able to explain the code! How would you answer when reviewer asks why did you use this very algorithm?
"We have tests." They could help. But don't rely on them entirely because there's a high possibility that the tests are wrong.
I can't believe how many people proudly admit that they don't read code. I'm not entirely against using LLMs to write or review code, but not even trying to read it should be prohibited. Someone responsible have to review it.
Edit: I know that LLMs are very good at writing code these days. My point is that this is less a matter of quality and more a matter of responsibility.
The reason is simple. It's extremely morally taxing to read the AI code and realize this is a mess that has to be scrapped.
Much, much more comfortable solution is to join the church of "I don't read code anymore":
1. You can ship fast
2. You can feel good about yourself
3. You avoid facing the uncomfortable truth
4. You get support from fellow believers
Slowing down to review the code feels like drudgery in comparison.
I'm in the review every line camp. I don't trust things that are addictive without a virtuous loop (like exercise). I have to interact with addictive things like video games, loot boxes, gambling, and alcohol in moderation. LLM use fits that perfectly. I'd rather use it throughout the day but still pause and feel the discomfort of reviewing the code, often throwing it out and having it try again when it inevitably makes a hot mess.
No you don't. You just need to be reasonably certain it works to specification. That was always the case, with or without LLM generated code. If anything I now have a lot more confidence in some of the code produced because the lower bound has become the LLM.
Releasing something to customers which hasn’t been reviewed by any human is gross negligence.
This is "Preview" — Apple's document viewer on macOS 27. Do you think the testing went well?
In my products I definitely take care of architecture, interfaces and data, as they have a high cost of failure. Anything else doesn't matter. If an user finds a rare edge condition, asking an agent to fix it costs less than 10€. Properly reviewing a big PR costs somewhere between 100-1.000€. I would be completely irresponsible to my business to review the code in-depth.
But the reason people use real code and frameworks like Django or Rails etc is because it's never just CRUD. You always end up needing some business rule somewhere, or you end with some performance problem leading you to think about async tasks and cached result layers and the like. You need to understand it because you're responsible for giving the correct results to your business and/or customers.
we don't read the assembly code produced by the compiler, nor do we read the code of all open source dependencies, yet we can successfully build software
* As long as it works
besides, there was a time before code review was the norm
When you actually have to debug, you sometimes need to.
You have to treat them like an ignorant junior engineer in nearly every respect, but trying to match with them at programming per se is a fool's errand.
The reason to read the code is not because you can catch their errors in programming. It's because they may have missed the forest for the trees at the operational or strategic level, and so that you can learn from them to understand better. It's like trying to beat the engine in chess now.
Get rid of that and you get a basically invisible productivity increase (call it 10% to be generous) but you've now lost tacit understanding. No amount of half-assed code review (or getting the LLM to generate "code quizzes" etc for you) is ever going to substitute for the tacit understanding you get from doing the 10% of coding yourself.
I really just don't get it. I mean, I can believe that using coding agents gives people a rush of power and fleeting illusions of increased productivity, but it's a mirage.
A lot of the comments here are trying to find some middle ground, i.e. trying to find some way to preserve tacit knowledge. But it seems to me that it's a waste of time. You either need to write the code yourself or completely give up on even reading or attempting to understand it. I just don't think there's any middle ground. Any well-intentioned attempt to preserve understanding of the code will fall afoul of human laziness and understanding drift
* I use LLMs to resolve the more annoying merge conflicts. It's not super hard work, but I don't like it, and I don't think I learn much from doing it.
* I implement a feature by hand, write some basic tests, and tell the AI to review it and write tests for cases that I missed (or was too lazy to do, most of the time)
* For 80% of the bugs that I fix, I have a pretty good idea going in where the bug might be. In the other cases, I ask the LLM to generate a list of hypotheses what's wrong. Often (but not always) it turns out one of the hypotheses was right.
* Sometimes it's not so easy to figure out how to trigger a bug in a test case. Haven't done it yet, but I plan to ask LLMs to experiment to try to do that for me.
* I use LLMs to generate tools that I wouldn't otherwise write (not high enough priority, too lazy, ...). For example, we had a rabbitmq connector that occasionally kept getting stuck without reconnecting in rare patterns of network interruptions. It wasn't really high priority for us (rare, easy to work around by restarting the service), so I didn't want to spend days of engineering on it. I asked Claude to write me a test harness that reproduced the issue, and it did. Having a bunch of automatically runnable scenarios made the actual fix not too hard.
* Sometimes, I have some software design problems where I feel I have a pretty good solution, but something about it feels off. Talking this through with an LLM often helps to refine or change the approach.
---
This approach to AI usage doesn't make me 10x more productive, but I do think it's a net benefit both for code quality and my enjoyment of the job. I don't know if, in a few months or years from now, I'll still be competitive like this, but right now, I'm doing fine.
Basically using it as a way to type faster.
With AI, now every software engineer gets to feel this way.
Picking battles: I lean heavily on AI for the mindless, boilerplate changes that used to take 2-4 hours; but instead AI can do it in 2-4 minutes. Then, I pay closer attention to performance critical areas, or areas that require deeper thinking. Finally, I lean on AI to review the changes. It gives me a better review than a human, and often thinks through things that require "superhuman" intelligence.
Refactoring after the fact: This is similar to working with a team of subordinates or lower-cost contractors. They (people or AI) write the first version, get it working, and then I pick the few areas that need cleanup. I work closely with AI to refactor it to be more readable; possibly to handle corner cases that weren't anticipated earlier in the design.
I'm also not afraid to make changes by hand when needed: The fact is that sometimes, changes are so minor or otherwise require a particular understanding that it's easier to do the change by hand instead of describing it to an AI. I hope where you work understands this and supports it.
If you're an LLM prompter shipping products, you're outputs are more akin to a product managers, not an software engineer's.
It's hard to hold an LLM accountable for anything.
Software engineers are always responsible for their code except now they have to be responsible for Claude's code
Which is - of course - absurd. Accepting responsibility without demanding the corresponding authority is a fool's game.
I have never been much of an advocate for unionisation in the software development industry. I didn't think it was necessary to protect the interests of the workers because for most of the industry's history the workers have largely been able to stand up for themselves anyway. Likewise I've never been a big believer in accreditation and licensing practitioners in our field - because who is qualified to judge someone else worthy in such a young industry with so little hard data about what works or doesn't work? I find my views on these subjects are evolving now.
It's not about protecting jobs if they truly aren't needed any more or protecting people working in the industry who refuse to move with the times. The Luddite philosophy isn't going to get us anywhere here. And besides - if AI is as effective as its advocates have been claiming then there should soon be more demand for capable developers to take advantage of the new opportunities. (The Jevons Paradox is relevant here.) But it is about protecting standards. If the tools aren't doing a good enough job then expecting professional developers to ship the code produced by those tools even though they know it's junk is something that needs to be challenged. And if developers are being expected to ship anything important without having the slightest idea whether it even works then that's even worse.
We had a lot of silliness going on when the very low interest rates and huge amounts of VC funding were propping up ideas and practices that would never have survived otherwise. Today we have a different kind of funny money distorting the industry but it's still propping up ideas and practices that we wouldn't normally support or perhaps even tolerate. And just like before no individual is going to be able to hold back the tide and it's going to have to be a collective effort to push back - hence my evolving views on topics like unionisation and accreditation. We've all seen the drop in quality across much of the industry and we all know why it's really happening because we see how the sausage is now being made.
Right now I have 8 agents open in my terminal and I know I'm on the more-sceptic end of AI users. Some folks here pull thousands of lines of changes, day after day.
With this many agents I don't even have time to read what they output, not talking about the code - just their output. My work today has been (sadly) degraded to: 1. Ask AI to explore the repo. 2. Pick a solution that works for the 3 solutions that AI proposed. 3. Maybe do few rounds of follow ups. 4. Create PRs. 5. Ask agent to verify if everything works post merge.
Some of my colleagues apparently skim through the code. I pretty much don't look at the code anymore.
It's just sad. I'm self taught programmer, hobbyist. I always liked working on my stuff and always were trying to deliver high quality. I no longer can't say that, despite my best efforts.
I literally have tasks right now where not only I don't understand what the agent is doing, I don't fully understand the problem either. I could invest time to understand it and agents do a pretty good job themselves, but remember, I don't read the damn output.
That said, I work in the infra team, not in the product team, so there's that.
Time will tell whether we were right to move from a system where we trusted humans to understand the low-level system, to one where we trust fancy calculators. But so far, it's working.
There seems to be disturbingly little objective evidence of that. There's certainly a lot of code being written but how much actual value that code is adding is another question. With the scale of resources that have been pumped into AI-supported software development over the past few years where are all the new applications and the quantum leaps in quality and performance in the applications we already had? If anything I seem to be seeing more anecdotes going the opposite way so far.
Disturbingly little evidence? I would hardly call that "disturbingly little"...
This does feel a lot like the "Agile era" that was the last silver bullet that was supposed to fix software development forever and the "Cloud era" that was supposed to fix infrastructure and deployments.
Agile had a few good ideas. Code reviews and automated tests are commonly used today.
It had a few plausible ideas that it took too far. We are better at recognising the value of incremental development over big up-front design today - despite the ironic clash this seems to be having with agentic workflows - but almost everyone hates stand-ups and lots of projects have gone off the rails by breaking things down far too much and never committing to anything and so never getting anywhere.
And it had plenty of ideas that almost all of us have thrown out as unpleasant and/or unworkable. Pair programming comes to mind immediately.
What didn't really change was the fundamental nature of software development. Did we adopt a few useful ideas and get a bit more efficient and improve quality a bit? Certainly. Did we get orders of magnitude better at anything and give up all the tried and tested ways of working in favour of radical new ones that never quite felt right? No.
On our current trajectory I'm expecting a broadly similar outcome for LLMs. They're now very good at some things. But they are still pretty bad at other things that also matter - and some of the reasons they are bad at them are an inherent part of how they work and therefore probably can't be overcome (at least not with incremental improvements on the technology that still basically work the same way). So I am expecting to see a period of consolidation with a lot of organisations pulling back from their dramatic leaps into the unknown and downsizing of dev teams once the big AI companies start squeezing financially and the problems of developer burnout and stagnating skills and low morale that we're already seeing after just a few months become endemic. And then as I suggested in another comment I think it's also increasingly possible that we'll see developers unionise and/or push for professional regulation and licensing so they can resist the pressure to just ship whatever their agent wrote without even bothering to check it but still being responsible when inevitably things then go wrong.
In particular I don't see why we should assume that the capabilities of AI programming tools will continue to improve at the same pace. There is no obvious source of new and high quality data for anyone to slurp up to train the next generation of models. Much of the change over the past year hasn't come from the incremental improvements in the models anyway. In fact I've even seen a few comments online arguing that some of the newer models had regressed in some respects. It has been the shift towards using agents and automating the incremental refinement of work in progress instead of waiting for human intervention that has been the big change and I don't think we really know yet whether the problems we're already seeing with that approach can be resolved effectively.
I worry about a lack of understanding creating risk, and we are creating tools and conventions to address this risk.
But I don't yet see evidence that we're losing touch with what we're building.. yes, code is shipping faster, but I'm honestly just not seeing this "sleep-walk-engineering" issue in reality.
Code isn't shipping so fast that the engineer doesn't understand the intent and general implementation details of the PR.
PRs are required to be small (400ish lines), and have tests, and good PR descriptions must be present. And two humans must approve the PR.
The time it takes to understand, verify and validate the change is fast, often as fast as or faster than the time it takes for AI to implement.
As it stands right now, our level of understanding, PLUS our new verification tools... is all we seem to need.
We're not like the hyperscalers, but we're considering how some of their approaches might fit into our workflow.
And much like Anthropic, we aren't seeing bugs increase with the rate of code increase.
Engineers are still able to understand enough of what they need to.
Well, the fancy calculators do. Yes, they still need guidance to keep from going off the rails. But, at least from personal anecdotal experience, I need to go spelunking into the codebase less and less over time. The agents and models really did just start to become that good.
HTML/JS/CSS is basically entirely vibe coded. I won't even try to reorg the 4000 line ui.js file. I can just redo the whole thing from a new prompt in a few minutes.
For things like the SQL schema I am still doing things entirely by hand. The consequences of getting this part wrong tend to be far more catastrophic than something in the UI layer.
The UI is the layer the customer is most likely to interact with and request changes over. This is why using the AI as the intermediary is so compelling. Most UI code is not long for this world if we are actually putting it in front of live customers. The less emotionally & economically attached to it we are, the better it is for everyone involved.
When my customer gives me crap about the UI, I barely even process it as a cortisol event because I spent very little time inventing the thing they are concerned with. It makes iterating faster and easier when you don't have much pride on the line.
Probably that's one of reasons why modern web experience is terrible.
A total overhaul is always but a single prompt and 5 minutes away, should the need arise...
That's how you get bloated apps that run poorly on lower/mid end consumer hardware and environments, with poor accessibility, and whatnot :p
Not that LLMs are the cause, this happened a lot before too, but they can exacerbate the issue. They can also be effectively used to minimize it, though they more often than not don't do that without explicit instructions.
There are easy ways to prevent the bloat and wasteful performance though, sth I do deeply care about.
You can get a long way staying on vanilla or picking (for your llm) one of those superfast frameworks like svelte in ny experience :)
For my part: I've put some of my tooling energy into code comprehension tools. You can get remarkably far quickly by just replacing the Github PR viewer! I started with a (native SwiftUI) read-only front end for tree-sitter and SCIP (the code-indexing standard). Then: as I reviewed PRs, I tried to catch myself doing annoying repetitive things and just building out additional features, like "show me all the knowable call chains for this function", or "show me every implementation of this trait".
You can get a fair ways just by thinking of things to hide in a PR viewer, just to clear space for the things you need to comprehend.
That's really it. I've been finding the same limitations with the default PR code review tools. We currently make do by having a section in our PRs "recommended review order" with a list of files/dirs that should be looked at first, which not only is much smaller Loc than everything else but is the main relevance of the PR.
Within a given project there are inevitably certain files/concepts that are more important, and a better review tool will help emphasize the structure of a large PR to make things easier to review.
I suppose you created a web based front end to GitHub PRs for your team to use? Or is it a local tool? Wondering how yours works.
We use gitlab and there's probably some way to hook up a custom external review tool easily (a bot that posts a link to view the current PR in the tool, for example)
Thanks for the idea!
The key thing, for me, is just having an app that is useful enough to keep running. I get ideas on how to streamline and improve review as I do it (for instance: double click on a symbol to highlight it throughout the rest of the function so I can trace assignments and expressions). Five years ago, implementing these random ideas would be too expensive; I'd lose several hours to each of them. Now it's just: have a dumb idea, share the dumb idea with the agent managing my app, check back in in 10 minutes, and now my review app is better.
It's very much like working in Emacs, but for everything.
So how do we build confidence anyhow when we can't know everything?
I think first, standard checks - does it compile? What do static analysis tools say - code quality, passes SAST analysis? Linters? Style checkers?
Do the tests run? I'm not comfortable with the tests I write let alone the AI.. by adding fault injection (fuzzing, chaos), and mutation testing, I have a better degree of confidence in the tests and code.
Spend even more time getting to know the product instead. Maybe I don't know the code as well as I used to, but I know the product backwards and forwards. That helps me have some new confidence that I didn't really have before. Knowing the greater whole.
I think the main challenge is pressure for more productivity with AI. I often go back and forth about if I am scrutinizing too much (and not being as productive as I could be) or not enough (and shipping slop). Building the additional QA harnesses and feedback loops for agents seems like the best known path forward to accelerate with some safety, but it is still pretty challenging to context-fiy all of the nuanced judgements a seasoned dev team makes.
with ai the only one that use that code is only him and the reponsibility is still on him
I think the real question here is how we rebuild some confidence and perhaps remove some fear.
Avoid known unreliable tools?
The further away I got from the implementation details, the fuzzier the semantics became. To the point that now I could not in good faith say I understand the system I have built from the ground-up and worked on the last ~4-5 years.
The solution to this, in my mind, is not to revert back to reviewing implementation details. The economics of that from a business/productivity perspective don't play out well.
The only viable solution in my mind is adapting code/languages themselves to the shift by making programs formally verifiable. That way you can be sure that the code "does what it says on the tin", and provably so.
I feel so strongly about this I've written on it:
https://gavinray97.github.io/blog/design-by-contract-and-eff...
The bridge is built. How do we test the load? Drive bigger and bigger trucks until it fails, note the weight, print the sign and rebuild the bridge the same way.
You weren't programming with LLMs in 2021 or 2022, be real. More like 2024 (that's already pushing it) or 2025.
https://i.imgur.com/CNAUczi.png
The point is that it's easy to lose understanding of the finer details, even of a codebase you lovingly created, once this switch occurs and an LLM gets its hands on it.
To me it's the memetic dynamic. There's "Old Wisdom". It's entrenched over a few generations now (and maybe some of it comes from before computers). "Old Wisdom" would have never allowed people to seriously start shipping code that they don't even understand. "Old Wisdom" has in fact prepared us for every possible "shiny new thing". That stubbornness was actually a good thing and held things together.
But now "shiny new thing" is so shiny that people convince themselves that it's actually okay. But they have to break the "Old Wisdom" in others. And this stubbornness (again, a good thing) is now rebranded as "being stuck in the old ways". You don't want to be stuck in the old ways, do you? So rather than coming up with real arguments, they're gaslighting people out of their defensive "be prepared for the shiny new thing" posture.
Once a person has been gaslit out of it, it's their turn to gaslight the next people. Pretty soon anyone who is holding back is the weird one. "I guess this is the new thing now?". This rolling effect is what I describe as a mania.
On the flip side, maybe it really is okay and it'll become the "New Wisdom" soon. This is probably also how things change. I still don't care for it because it's very disrespectful to people who have held their ground for good until the point where it's no longer useful. They've believed in something that wasn't actually true, it just temporarily served some greater purpose.
I picked up on a similar dynamic in radical politics when principled adherents are suddenly gaslit as "idealistic" by "practical" people, after they're no longer useful to them (they already won the election, etc).
Try it, you might like it. First month's free and you can still quit if it's not for you.
You trust the label. The person. The company. You hope someone did their due diligence.
Sometimes taste the shell. That is an acceptable risk.
(made a small edit)
The latter look very promising for agentic coding. The former isn't quite there yet. We'll see, but I'd suggest the prior is moderately to strongly predicting that agentic coding is not a mania, but is very much here to stay.
(Obviously still the unpopular opinion here, mind you, and I may be wrong.)
And I would put a caveat on code that could somehow be formally verified as correct, using a formal verification system that humans understand. I know people sort of do this with unit tests but I still think that's crazy.
- I wanted it to implement 5 buttons for some actions. I told Claude only about 4 of them. Then I implemented frontend/backend for the fifth one myself pretending that I just joined the project.
- I made Claude implement backend only and frontend only and then connected them myself.
I will never ship without looking. I might not look right away and ship when I am pushed. But then I will go into the code later and look anyway.
I don’t care which way the industry goes. I like looking under the hood and understanding stuff. Understanding makes you much faster because you can think inside of your head. Because I understood the code I knew just what to do when something was off. I love it when that happens.
I do feel bad when I think I don’t understand. I still go back and read high school book even though my grades were good.
Like, I'm a data scientist, and have seen my share of truly awful code, but currently I'm working on a system which has pyspark code inside javascript code.
Like, it works (for now), but my eyes, the goggles they do nothing.
Same thing is happening in academia.
Yes. We're all going to become meat proxies and we're all going to add LLM service uptime as a single point of failure to... every aspect of our technological civilization. But it's all worth it because Grok will let you force feminize Draco Malfoy into a anime girl and that's worth never understanding anything again.
Can you break down for me exactly how the unit and integration tests are made? Do you ask the AI what to assert?
There should be enough detail in the spec that it's pretty obvious. And unlike - many - human devs, agents don't shy away from writing tests. Add a coverage guardrail and tell them to use red/green TDD and you're already a good way there. The main thing is to ensure shared understanding, so they also know business expectations and write appropriate tests. And... nothing here is really specific to an agent, as a human dev also needs sufficient guidance (tailored to their experience level).
Here[0] we have benchmarks for AI writing tests for code that already has the correct behavior. Best case scenario is that they're at 67% with Fable, and that's with the highest quality specification we could possibly have (code). English is far more ambiguous
[0]: https://labs.scale.com/leaderboard/sweatlas-tw
I have never in my career been afforded the time to make a complete spec.
Noone understood the code to begin with. People understand the concepts, the primitives, the "theory" as Naur said, the basis upon which a project is built. Noone ever understood a codebase worth understanding to begin with.
Noone always gets it right, Noone went there after that horrible incident, Noone actually dances like that...
- https://dev.to/ayanozturk/dont-blame-the-developer-lessons-f...
- https://www.linkedin.com/pulse/blame-game-why-blaming-develo...
- https://thedevopsdojo.substack.com/p/stop-blaming-developers...
It's totally fair to load up some open source project into an agentic IDE and instead of asking it to write something ask questions like "how does X happen when I use feature Y?" or "can you explain how client code could accomplish Z"
All the time I see an AI use a language feature or library feature that I never used before or I see some code I don't understand and it does much better than a lot of humans do at explaining what it did.
I’ve been experimenting with writing a living spec — API endpoints, schemas, how things work at a low enough level that the LLM can fill in the details - and that’s been working well.
It also forces me to do the hammock-driven development necessary to build the right thing, and as a byproduct it keeps the design in my head longer.
Doing sports ohotography I might got to a certain indoor track meet once a year and there is a lot to know about the order of events and how to move around in the space and where the good backgrounds are that I will have to rediscover unless I write a “runbook” which again lets me share that knowledge.
A coven has a “book of shadows” and for me it is mostly a runbook, like there are a lot of ways you can cast a circle and I like some liturgy better than others so if you get consensus on things you can write it down, even the Greeks thought writing things down impaired your ability to remember things but it’s easier to stay consistent and you do memorize it over time though…. It is annoying that a lot of people are reading off their phones in ritual spaces.
What I do: I have a mental model of the system and how it works, at different levels of abstraction, in all the ways I think matter. I test that mental model against reality by making the agent proof it's real, looking at the implementation myself and seeing how it behaves in production.
Every day I'm making hundreds of mini risk assessments on what happens if I get a specific assumption wrong. If I'm not comfortable with the risk, I figure out a way to de-risk it. Maybe a slower rollout, maybe I read the code for longer, etc.
It's certainly a different job. As a startup software engineer it doesn't feel against my instincts though
Slowly build understanding of what is possible, push back on engineers suggestions and don’t approve stuff blindly, but relying on results to guide them not the implementation.
It is very very similar feeling. -you need to know your models and harnesses (e.g. your … team). You don’t need to know the specifics of the code but doublecheck with different personalities if the assumptions are correct, test them.
And ultimately shipping bugs is a given , human engineers do it _constantly_ it’s the resilience systems you build around that which saves you, tests, canaries, rollbacks, feature flags, everything we already know how to do - just use that and imagine yourself the product owner.
The codebase I work on was designed to be split by features. Architecture is well thought and each feature has the same high level abstractions, all code is in one folder. This bring several interesting guarantees: code is scoped to this folder, each feature is "not too big" and fits in the llm context easily, and a design document explains architecture choices, that is well respected by the llm.
When the llm works on a feature, it is instructed to do several things to help me catch errors before shipping. Write a templated description of the change: interactions with external systems, sequence diagram, happy path, corner cases, deploy checklist, settings change, etc. I review high level choices, and not the code.
Finally, the llm add review threads to the code everywhere it made suppositions and related to untested assumptions. Each of this comment says: what could not be teste, what the llm thinks, what happens if I don't change anything and release the code.
Finally, on parts where I want to be sure, I do llm-instructed testing. It can build complex test benchs in no time, fetch data from production "provide me the command to fetch data, and a script to anonymise it", do a before/after comparison, ... It costs few tokens but it buys the peace of mind.
This way I have a workflow where the code is fully written by the llm and I don't go crazy reading its code.
2) Ask agents to do more than just code.
They need to fully demonstrate and prove that their PRs are rock solid. Ask for screenshots, videos, code reviews, detailed architecture diagrams, etc.
It would be an insane amount of work for humans, but AI can do it easily for each PR.
Merging any PR from an agent should be a no-brainer if there's real proof that the feature is built correctly and working.
If there's still doubt, then the agent needs to do a better job of proving that the feature is built correctly, doesn't introduce regressions, etc.
So I suppose we just have to care less and let it get on with it. Not easy tho, for sure.
Personally, with side projects and tooling, go nuts. There's no reason to read all of that code.
I feel like an increasing amount of orgs out there have long abandoned this thinking.
If I'm working with something I don't understand, I ask the LLM to teach me first, with as many references and correlations as I need to fully know what's going on.
I also do the architecture myself, but ask for periodic suggestions for improvements and checks on how other systems are doing it. If I see a newer system I think makes sense, again I ask for explanations until I understand it.
Idea is always that the agents assist me in doing something I could have done myself if I didn't happen to only have 24 hours in a day and other parts of my life to live.
In the beginning your had your hands deep in the code, knew every corner and every pattern. Then you became teamlead, coaching other devs and only reviewing their code. Then you became managaing lead taking over more projects - now you don't look at code anymore, but you still manage the people that write the code and make sure the code fulfills its requirements and delivers on product roadmap. You develop trust in your team, you barely every read their code. If individual teams or devs ship bad code, you will only learn from prod defects, but the measures are mostly the same - fix the team.
What matters are the high level decisions in the code such as "Deleting an account removes the row for good", "Email becomes the user’s permanent ID", etc.
We imagined a completely new concept for code reviews where you review the decisions not the code diffs. Also if you want to review the code it is grouped by concept not by random file diffs.
This is coming from someone that likes manually coding btw but our PR size and velocity is increasing too fast to meaningfully review all the diffs forever.
How many more lines of code are Meta engineers shipping that they don't have the time to check the ones they _didn't_ write?
This makes no sense to me. So I spend vibe coding some app. I barely know code. It's 40 000 lines without comments. Say it's my job, and pays the bill at the end of the month. Say the LLM and I would have done the vibe coding within a week. I'd need one more week to check every line and understand everything. I'm a noob, this dude's a pro, so complexity is irrelevant. What the hell are they doing the rest of their time? Shipping more code?
And if that's true, and again, I'm a noob: shouldn't their "instincts" tell them to write tools to increase comprehension speed? Better interfaces? I hate everyone single one out there and designed a better one. Now I have to port to Rust/Actix/GPUI and some vibe coded redis because that thing is laggy as hell ... but I HAVE TO UNDERSTAND HOW it works because otherwise I cannot improve/customize the stuff/frameworks already out there and that people build with ...
What are they doing with their time? There are all these posts out there of programmers and systems people not doing anything 90% of their time? If it's already shipped, don't you try to understand the details afterwards?
I agree with the advice to design your system before implementing it. It is pretty easy to iterate on a design with your favorite coding assistant, and this is your chance to be ultra-picky and make sure you get exactly what you want. This was always where the "software engineering" was done, but in practice it's easy to have a vague idea and refine your (internal) list of requirements / tradeoffs as you type in the code. I find it harder to do that when the LLM is generating the code, just because one turn can change so much.
Keeping it under control requires discipline, just like before LLMs were a thing. You set a bunch of engineers loose on your codebase with no vision or supervision, you get spaghetti. You just get a little bit more control now. Agents don't feel shitty for the rest of the day when you tell them to do it some other way, so you are free to review as critically as you want without hurting anyone's feelings. I think it's a good thing.
You'll also discover over time which systems the AI can handle fine on its own and which abstractions and systems it just has no ideas on how to design. Often I find I have no idea how to design those too. It's then useful to try different designs, test tradeoffs and refactor aggressively if needed.
If you continue down this path, your golden goose is COOKED
Even if you have a 20 file 5k line change set, it's still not impossible to just grok what's being done, what the purpose is, and what it works like on a high level.
LLMs don't change the fact that such a review is difficult, but I'm more confident that there are no hidden off-by-one issues in 5k lines of LLM code than in 5k lines my colleague (or myself) wrote. So it's a welcome change in reviewing to do it on a higher level.
A manager never read all the code, you just need to find the level at which you are getting good results. For some tasks you need to read every line, even write every line, for some tasks you let it do everything and just test the feature.
If the agent has written tests and crucially - witnessed them fail before starting the work to make them pass - you have a lot of confidence.
Review of your own work's easier too - test and production code tend to mirror each other, giving you a much more complete picture. Confused by the production code? Go read its tests for a bit, then come back.
It's also easier to understand the change if you were there for the build, watching the transcript, checking in every so often. Doesn't have to be your primary task - leave it on a second monitor, it'll catch your eye naturally.
And finally, on top of test-driven development, have the agent first (before the first test driven development cycle) build out an end-to-end test covering the work it'll do. Once that test is green (test driven word for "is passing") you're done with your work and can clean up at will.
Play with it before shipping, get it real nice. It's never been easier to clean the tech debt up and make the UI perfect.
Eventually you'll be ready to package it up into a PR!
Now's you can leverage agents and get creative. Make your colleagues froth to review your work. Consider making a Loom video to walk them through it. Consider reworking it into a commit series; it only takes a few second for your agent and you can tell a story for humans. Maybe you want to sequence it according to your struggles; maybe it's better to show the different layers emerge with each commit - it's up to you. Consider also that your agent can easily do one-off visualizations of the change for your colleagues if you think it'll help them, and you can attach those too - mermaid charts or even whimsical AI generated images. All this is because motivation is driven entirely by other humans' motivation and you want them frothing with excitement every time you drop a review. Make it fun, it takes seconds and gets your stuff merged fast while making your teammates smile.
This is a brand new job and we will work out how to do it together :)
There have been multiple times where my software dev team has gone back to prior implemented tests they had the model generate and found that after some time eventually the model got frustrated with the test no longer passing eventually modifying it to fit current needs around a feature push. You can probably anticipate how we came to discover this, as it ended up introducing a regression around multiple edge cases which the test was meant to control for.
The only answer is to inject actual engineering into the process as much as possible without giving it all up to the models to generate more slop in response to slop. tests are a great answer, but only if you treat them with serious intentionality.
There are multiple places in my post where I explicitly describe the process by which you intimately:
- make the agent do verified work (the e2e tests and test driven thing)
- watch the agent work (the second monitor thing)
- read your own code (the tests/prod mirror thing
- play with the work and perfect it (the tech debt and UI perfect thing)
Plus a ton of other places in my post I very clearly describe being intimately involved in building and selling the work.
That to me is basic god damn competency. What your engineers did is a fad, and it won't last, and I'm sorry they abdicated their professional duty.
If you wish to screen for that sort of shit in future/current hires, show them my post and see if they turn up their noses or if they get excited and creative. You want only the latter.
You always need to understand a bit of the past technological layer before saying your goodbyes to it and moving on to the next progress phase. That's life for a programmer.
Actually, it is quite similar to a /compact with a harness: you need to "sum up" you current knowledge (aka abstractions, design patterns, ...) from time to time to carry it along with you in your journey.
Like, it is not that I hate AI tools, I just hate how most people are using them...
I can't imagine going to punch cards to, well, anything would feel anything but great. But going from assembly to C I can see how a programmer could struggle with their coworkers not checking the outputted CPU instructions for performance problems and compiler optimizations that generate unintelligible assembly.
But even then all those changes were deterministic (at least to the compiler version), unlike AI.
- Ask questions, clarify and ask "Do you have any questions".
- Build systems for robustness, like you would build when delegating to human engineers. Tests are just an important but small part of it. (Take ownership)
- Lead with examples (not just skills.md or what-i-want.md). It cannot understand your abstractions.
- Peer reviews (Across agents or across models)
- Clear division of responsibilities (Context engineering - Agents/SubAgents)
"This new way of piling bricks is so fast that I can no longer afford to check the buildings integrity".
I just don't understand how the fuck we ended up with this narrative. Somehow the machine making things twice as fast forbids us from spending time ensuring quality? Surely if it does things faster you have more time and tools to check!
I'm half convinced this frenzy is the marketing work of AI companies at this point.
Worse. "AI" is a self-marketing contagious delusion, driven by an inexhaustible power supply. Human gullibility.
I also have a /simplification-check skill I run after pretty much every large change, and that already helps surface the murky areas of the code and sometimes remove complexity before I even start reviewing, thus accelerating the review.
Phase 1 (mid 2025): Have AI write the simple stuff if you want, but hand-write the important core stuff.
Phase 2 (late 2025): OK, let AI write the important core stuff, too, but review every line of code.
Phase 3 (early 2026): Wow, AI is making virtually no mistakes, so only review the important core stuff that it writes.
Now (since mid 2026): If using newer models like Opus 5.5 or Astra, no need to review its code as long as the behavior is as expected. I'm no longer a software developer or coder, I'm a software creator.
Are you? Seems like the model was the thing doing the creating.
In the last few years we've shifted into building more things in-house. It's awesome and we can solve even more specific needs for the organization. But the situation with understanding just enough does not really change. We just don't have time to dig into every line of code like you say.
I do get the sentiment but the conclusion doesn't make sense to me. We have been solving problems as engineers at different layers of abstraction for a long time. Pre-AI, I used frameworks that abstracted lower level concepts away which made it faster to develop the solutions that mattered (aka solving problems for the end user). I could, given enough time, write the underlying database adapter to convert my business logic form into a database query and back out but I didn't need to because someone else abstracted that part away. Did I know how well that adapter was written? No. Were there bugs in it? Occasionally you run into a limitation or bug the author hadn't considered that required a patch or fix. Going a step deeper, the programming language itself is an abstraction that compiles into machine code or bytecode. Do I understand machine code or kernels? Definitely not. Some programmers do, especially if you have a compsci background, but I'd wager the number has been decreasing over the years. The point is, now agents are writing code and they are now pretty damn good at it. Better than the "average" programmer out there. Now we get to "program" in natural language by telling the agents what we want and they get to worry about nailing the syntax and all those details while we focus on the higher level decision and direction of what's happening. If your agent written code is sloppy and terrible, that's a skill issue not an agents write bad code issue. You need to setup the systems that result in code to your standards with the checks and balances to test and QA appropriately before it hits production.
Complaining about the state of things while sitting back with your arms crossed refusing to adapt to this bold new world is a risky strategy. People saying just don't use AI and do your job as normal are also going to have a difficult time. The devs that can leverage AI effectively and get a lot done will out leverage you to the point where it doesn't make economical sense to keep paying you to not use AI (unless you are in one of those super critical fields I mentioned earlier and even then I'm not sure those are safe). Isn't it more fun to embrace this new technology and figure out how to use it effectively vs complaining and refusing to acknowledge it? Help shape the future instead.
That's not true at all. LLMs are still noticeably worse than a typical (competent) human programmer.
> Isn't it more fun to embrace this new technology and figure out how to use it effectively vs complaining and refusing to acknowledge it?
I care about results. If a tool is effective, I use it. If a tool gives bad results, as LLMs do, I don't use it. It's pretty straightforward.
https://marketplace.visualstudio.com/items?itemName=novian.g...
What I did was build my own process for this: https://github.com/mhalstrom/maximizepm
The main take away is: 1. You need an agent to manage your agents that you mainly interact with. 2. You need a durable way to manage tasks and the dependencies between them. 3. You need agents with multiple roles ie manage, build, review etc 4. You need to manage yourself across projects so any system you build needs to take that into account
What I do is ask Opus/Gemini to explain the parts I don't understand. And I do some of my own research to verify accuracy of the explanations.
In addition:
* Every new feature has a Feature Design document which I review, modify, and approve. And a corresponding Implementation Plan which I also review and approve.
* Every fix has a RCA document and fix Implementation Doc which I review, modify, and approve.
* There's a global high level Architecture and Design document with additional sub component design documents which are always kept in synch after a new feature or bug fix is done. Synching it is always a step in the Implementation plan.
This does adds some overhead but it allows me to have have check point gates. This has often allowed me to catch misunderstandings and align properly before any code gets edited by the AI agent.
Having the above docs also helps when it comes to referencing them as context to properly ground new features or bug fixes.
So, for me, end of the day, it's all about testing, monitoring, soaking in staging and promoting to production.
This might be why I'm fairly comfortable shipping AI generated code.
For my own projects I’ve limited my exposure points by picking which projects and what scope I ship. I have to iOS apps in the App Store and I simply removed high exposure surfaces - right now neither use accounts because I don’t want to manage account systems, and Apple prefers it anyway. I downrisked my projects from what I could ship and I’m perfectly happy with my production projects.
Most of my other AI written projects are internal apps and systems. I have another project going public soon where I ship the output of LLM written code, without running the code in any user accessible area.
And tests, of course.
In time things will mature, tooling will be better, and we’ll have a different risk quotient.
To me, "It works" is pretty much the lowest possible quality bar for an acceptable creative work product. We should not aspire to "it works." We should not hold ourselves only to the "it works" standard. And, yes, I know that many traditional software companies fail to even meet "it works" for their shipping products.
I don't even know what to say anymore. This has become a deeply unserious industry putting out mountains of junk that barely even meets the "it works" bar, and LLMs are just accelerating this.
It's not perfect, but it's helped so far.
It then gives me a hint on the area or a typical code flow that would hit the bug - whichever gives me less info. I can obviously ask for more hints as I read along.
Obviously all honour-code based since I could just ask it to fix the bugs itself. I think this is something that can't be fixed systemically - either you're going to bother reading and thinking through the code or not.
Other than that I guess reading the tests - to make sure they fail without the code / fix, to make sure the setup makes sense etc.
Written code is the new compiled code. You never look at it, not even once. Documentation is the new written code.
Every repo has a /docs folder that holds the full specification of the code. Full architectural block diagrams, edge case behavior, style, problem statement, etc. I closely review these docs and make sure that the agent follows these specs and ensures the specs are updated after every code change. In theory an agent could fully rebuild the system from scratch using this documentation.
Maybe this is just pearl clutching, but it helps for when people ask architecture questions and I can answer and pretend I know what's going on.
Basically, don't let the AI coding era peer-pressure you into being a bad engineer.
I then make sure they write a lot of tests and that if functionality is changed or added/removed then tests are updated alongside.
Generally I try to review changes myself and ask it to explain the changes.
It's not perfect but I feel like the planning step being iterative isn't all too different from my old research -> think about implementation -> implement loop.
At that point, reviewing any code changes goes hand in hand with how I coded before LLMs.
The major difference is I usually have it make any changes for things I don't like.
It also took the industry quite a while to accept that server code needs to be secure even if that requires a lot of extra effort. AI will probably be the same.
For me I dont look too closely at the implementation but I look at the api where if a human was using my framework how is the code they'd have to write and engage look at. Then I also do plenty of performance against a comparable target in my case slate (default unreal) and keep overhead minimal.
So good performance and reasonable public facing api are the 2 main pieces im focused on. Less with the internals.
Care about the seams and interaction and the things you can measure or count.
If Facebook is hiring and looking to replace the idiot who made that linked in post, I'll be honest, my ethics are at an all time low, I would work for you. Plus, I can guarantee I am capable of doing BOTH no-shits-given programming and FULL-shits-given, so I'd be perfect for your payroll where a straight upgrade is warranted.
This will not only not prevent CVEs, it will also make everything a giant pain in the ass
And if you don't think the firings and reduced pay are right around the corner, I have a bridge to sell you.
Yes, there are more CVEs discovered every day, but, if you don't patch, the hacker doesn't have to try.
LLMs cause errors at a noticeably higher rate than human devs do. If you lean on them without actually checking what they wrote, the error rate in your software is going to go up.
Does the LLM understand it, in detail? Can you trust its interpretation of the information and instructions you provided? Did it independently make an adverse decision you haven't noticed, because you didn't read the code? Was the model missing any information it required -- and would you know if it was?
LLMs depend on humans and human oversight to ensure they have the information required to make the right decisions, to catch when they don't, and to steer them based on your much more complete understanding of the broader context and requirements. You can't rely on them to do that thinking for you (yet?).
You said LLM need human oversight and it’s true, but it does not imply you have to read the code.
I think we are going towards a place where code no longer makes sense, you just describe what you want in English, and the LLM just builds directly in machine code. You then build different verification tests to make sure the program is doing what it’s suppose to do.
That sucks. Ship it.
What the worst case penalty of the bad code/app?
My dumb little utils in ~/.local/bin? Those get merged after a quick functional test without my having read a single line of it. Still, the skill I maintain for such a job directs agents to write such throwaway scripts in anything but shellscript because I might want to extend some day and fuck shellscript
Just kidding, it's de same as with any other Open source plataform You install from their repo. It will be hard at the start but then You Will be very aware where it starts to fail and how to fix it.
It's a problem that requires a united approach from both sides.
A need to pace oneself, as it were.
* Review any code that modify the data (when possible, avoid migrations that can't be rolled back)
* Staging/PR deployments/feature flags
* Observability
* End-to-end black box integration tests for the main features (unit tests are mostly useless)
your coding agents can draw interactive architecture diagrams with it that reference the underlying code so you can get familiar with a diff from a high level and drill down if you need to. we've found it super useful to quickly get a sense of your own changes before sending to colleagues or shipping.
Recommend reading (All though a little self serving): https://www.linkedin.com/pulse/ai-code-quality-jorge-varas-a...
This is a bold assumption and inconsistent with my knowledge of history. High level languages allowed better management of complexity and reduction of tedium. The common complaint was that compilers produced less efficient (slower and/or larger) code, not that the output was more complex. It was[1] normal to read the compiler's output, which was generally assembly or another intermediate representation. After all, a compiler is merely a tool for translation from one language to another. Additionally, early compilers often were not optimizing compilers; the code they generated was simpler than handwritten programs.
1: And still is, for some of us!
The problem assembly programmers had with compilers wasn't that they wouldn't understand the generated machine code - it was that the generated code was often bad (as in unoptimized and bloated) and didn't justify the resources required to run the compiler. As hardware got more complex and powerful, compiler got much better, and operating systems became the primary layer between hardware and applications, assembly became more niche. Nowadays it's pretty challenging to beat the compiler at generating efficient assembly and writing a program in assembly would consist primarily of OS and library API calls anyway.
There are definitely parallels there. The only obvious difference I can see is that both high level language code and assembly are unequivocal in their meaning (as in you can determine what gets executed), whereas natural language is not.
Each step in abstraction we've made had people who felt uncomfortable at the level of magic they were accepting. At one point in the 70s/80s, traditionalists were arguing that C itself was too high level. Then in the 90s and early 00s, that languages like Python and garbage collection were absurd: a "good" programmer doesn't need them and they give up too much control. Then as web development ramped up, people entered the "Javascript is slop" phase into "React is slop" phase.
Anytime the barrier to entry is reduced and some skill or profession becomes more focused on output than the ego of the professionals, you can bet a counter-culture of people will rise up to oppose it. It's a threat to their sense of "betterness" and/or the (perceived) value of their career.
Looklike they are correct because now we have web people making 1GB-RAM Weather app.
No I only do that for unit tests so far
I went through like a mid-life crisis when the thing I learned/spent last 13 years on became meaningless.
Stop shipping code you don't understand.
Is there really any other solution? You can either ignore it and pretend like it's the new era of software like half the other commenters are, or you can sit down and understand the code and what you're shipping.
Brain AI Brain
You start the thinking, you tell Claude what to do, you think about what it built. It's that simple.
Or at least, I don't think people should.
To me this completely goes against the whole point of engineering.
IMO If you don't understand the code, then you're just a manager now. Soon your salary may also reflect as such, assuming you get to keep your job.
have an architecture
Be consistent. All parts of the app should look like all other parts of the app.
Dont allow clever code
break things into modules
do true unit testing, meaning test the smallest unit of code possible.
AI still does not know how to even write code that makes impossible state unpresentable so testing won’t help.
In any case at the end you're the owner/accountable so you need to justify your and your agent choices.
How do you keep the IEC 62443-4-1 auditors happy?
During my 30+ years career I have never worked in a project where shipping slop (AI or pre AI, doesn't matter) was an acceptable solution. That's my choice and I'm pretty happy I have always had the opportunity to focus on quality of the product.
PS: LLMs are wonderful. We should learn to differentiate between AI-generated slop and AI-generated code.
That's essential as you should really "own" the solution you sell and take money for, as you're responsible for what it does on your customers' hardware.
easy, don't write code with LLMs. If my job forced me to do so I would quit.
The mental gymnastics developers are willing to go through just so they can avoid standing up for something is quite perplexing.
My conclusion was I had to elevate myself and make sure another agent reviews and tests the code (QA) under the supervision of a third agent (CTO) that defends my vision.
The ones who get to keep their jobs will be rebranded as Maintenance engineers with a lower salary. VPs and Heads of will keep their cozy job (bc there are not so many of them), and they are not that dumb to fire themselves.
Code understanding doesn’t matter anymore (or won’t matter soon)
Is it a company culture thing or a domain specific thing?
I am perfectly comfortable prompting AI and then seeing the end result and not questioning any of the code. I've looked at code for nearly 20 years or longer, I can't even keep track how long anymore. Started with QBasic and DOS scripts. I just always accepted it as an annoying necessity, and then the world changed two times. Once when they started paying us good of money for it.
A second time when programmers became an insufferable bunch. It wasn't about the art of the product anymore. It wasn't about working with the other parts of business to understand the customer's needs and building to that. It became this weird gatekeeping bullshit where we turned on each other and started asking people to reverse binary trees and implementing search on a white board after 2 weeks of interviewing. And then rejecting them because "culture fit". Add in all the other misogynistic racist stuff, and yeah, this industry is messed up and it's rotten from the inside.
Ever software is a "I don't know what those previous engineers were doing, it's poop, so we have to refactor and rebuild with this new tech", and repeat endlessly. And quite honestly, every piece of code (mine especially) is garbage.
So I think this is great. Programming was a means to an end. It became over glorified. Now it's dying. I'm here for it and I support it. Now anyone can build an application no matter how complex, and the only thing that really decides success from failure is the actual business around it.
No we can't call the CRM from our client side using a static bearer token even though their docs say we can. No we can't simply switch to Postgres even though it supports 99% of the same SQL we currently use. No we can't simply block usernames which are "clearly" profane.
But if we can chat about it, then we can see what the REAL problem is, and then we can come up with a fitting and simple solution.
The problem is that I have an AI colleague who is way more energetic and willing than I am. They love to talk, they seem to ramble on endlessly about any subject. And its the kind of solution oriented talk my PM loves to hear. And indeed my <insert senior role here> talks to them (or really, lets them talk), and draws up a comprehensive and confident plan to do X. I pick up the plan and discover problems with X, let alone the plan to do it.
I feel nervous about raising issues with X or going through the plan again though, because my other human colleagues are already speeding through the work for Y and Z that my AI colleague is aiding with.
And so maybe I'll raise my biggest concerns with X and not sweat the small stuff. Maybe next time I won't raise my neck at all, noting the gleaming axe sitting in the corner.
my only response to those who remain and try to control the slop is
and really, you should have an Imperium/Mechanicus-level devotion (whether it's to money or to AI itself) to survive this and not burn out. in my region, though, you are not getting money - salaries were slashed in half across the board over last two years.https://t.me/djinni_official/2163 (go in with translator; infographics are in English but explanations are not)
at this point, for a junior, it's easier to find a well-paying blue-collar job than to get into tech, and the ceiling is falling at the speed of sound.
On the other hand, not even surprised anymore most people even try to provide serious answers to this. Welcome to the pure insanity.
not understanding the detail is quite similar to a coworker writing the code and another coworker approving and merging the changes.
That and "oh, it's just software for a website that doesn't do anything important that couldn't actually damage anything".
With zork I can also just start typing in a code file and leave instructions for the agent to fill in or fix parts of the code while I'm editing it. So I can lay out what I want the class shape to be and don't have to implement all the annoying little parts. This feels more like the GitHub Copilot days.
I honestly believe more of us will pair program with agents, not just yelling at a chat window.
My point is if you don't understand the fundamentals of the underlying code (SQL in the example above), you're more likely to have issues in the future. Or maybe not if that app doesn't reach a usage pattern or complexity where the nuances under the hood matter.
It really depends on what you're shipping. Meta ships social media apps where in the grand scheme of things if something is broken software wise it largely doesn't matter. What's the worst case? lost ad revenue? Users can't do things? If that were software for MRI machines or aerospace where software issues/bugs can result in lives lost then it matters much more in understanding what is going on at all levels of the software and potentially hardware.
That is the road to hell. Even if your higher ups think it isn't, this strategy will for sure blow up in their faces soon.
I predict that Meta is soon going to be going on a hiring spree to fix all of the issues this strategy is going to create.
At least someone in the chain has to take over the responsibility in respect to litigations. That one has an interest to understand the inner workings of AI generated artefacts.
You will know intuitively if it's worth taking a closer look or if u can safely close your eyes and just commit it right away.
I usually know which meta questions are crucial to clarify with colleagues and where to check the code punctually.
It's at least nice to be able to prove something about the code, rather than blasting it with tests which only prove that the tests that are written do not fail (and perhaps get you some amount of code coverage — necessary but insufficient for anything like quality).
How to do all of that in your environment? That can vary greatly!
An experienced senior engineer with access to a coding agent is the most powerful code-understanding system that has ever existed. There's no excuse for shipping or approving anything you don't fully grok.
All vibe coded systems are balls of mud by definition if you use the conventional definition. If you allow "understandable by an AI" then right now not much changes because AI aren't so hot at designing systems and even if they could, they make poor use of their limited context. Whether that changes in future is the big unknown.
We already know how balls of mud evolve. New features are "bolted on the side", making the system more inscrutable. These bolted on systems often grow interdependencies on each other, creating a spaghetti of relationships no one is aware of. This means every change could introduce a bug whose blast radius is the entire system. Changes get harder and harder. A death of sorts, or at least a growth limit is reached when on average every bug fixes introduces more bugs than it fixed.
Google Chrome is a fine example of this happening right now. It used to be a highly modular combination of mostly independent systems that had their own champion who understood them intimately. They left, Google management insisted the client platform that supports most of eco-system continued to evolve and grow. The ball of mud was formed. It's been evident for years chrome has a higher bug injection rate than Firefox. As complexity grew it became too hard to move to new tool versions, so compilers and libraries got pinned to known working version. That made it too hard to use as a component, so projects like ChromeOS are being replaced with Aluminium OS. Now the that Matt Jolly has thrown in the towel [1] it's hard to see how distributions like Gentoo and Debian that used his work can continue to ship chromium. Debian can use ungoogled chromium, but vendored libraries, obsolete tool chains, inability to do forky mandated reproducible builds make it look like it's a foreign object bolted into a true open source project. This is what software death looks like. It's not a bang, projects cease to evolve, then can't meet new requirements, and then are replaced.
It doesn't have to be that way. Projects like the open source kernels are decades old, but their strong stewardship has ensured they remained decomposable, so people can work independently on different sections without too much fear of stomping on each other's toes. Open source stewardship seems particularly good at this, possibly because open source projects can take a very long term view. A manager in a large organisation on the other hand is usually around for only a few years. They take the short term wins that come from features bolted on the side safe in the knowledge the long term death of the software will be somebody else's problem.
[0] https://www.researchgate.net/profile/Brian-Foote-2/publicati...
[1] https://lwn.net/Articles/1097760/
And before agents, we often built this working theory of the system by writing the code by hand. It helps us figure out the "grain" of the codebase so we have a better idea of how to extend it.
So what happens when agents are writing the code? The main avenue with which we built the working theory of the system in our mind is now taken away, and we're left feeling like our understanding of the system is blinded.
I ended up more or less changing my entire workflow. Four main principles:
1. Interrogation/interview-based design and specification
2. Make the decisions that matter and leave the rest to the agent.
3. Use agent to generate explainers, quizzes, and simulations.
4. Use verifiers and guardrails in the coding loop.
Interview-based design and spec
When I tell the agent what I want to build or extend, there's naturally ambiguity. But the agent knows what is ambiguous to it, so it can ask me. /grill-me is one way to do this. You'd think this would be commonplace now, but it's actually not. Some people are still wondering why they're getting bad results. It's because they're not resolving the ambiguity.
Making the decisions that matter
Not every decision matters in a product. Agents are fast at writing code and pretty good at some of the nitty gritty details, so I let it make those decisions. Also, if a particular subsystem is commonplace and undifferentiating, such as login, then I also let it make architectural decisions for that subsystem.
For decisions that are product differentiators, where that differentiation is supported by technology, I make decisions on the stack, the architecture, and the approach. This is the design phase. Because agents tend to make the mainstream decisions, they almost always take a midwit approach which doesn't leverage the technology in a way that makes a difference for the customer.
Some try to build this understanding from code reviews. I personally don't think it's the way to go, because it's not motivating. I find that I'm more motivated to learn how the system works today, because I want to change how it works tomorrow--and it sticks in my head better too (no different than pre-agents).
Generating explainers, quizzes, and simulations
As I'm doing design for an improvement and I don't know how a part of the system works, I can ask questions of the agent in chat. If I still don't get it, I can ask for diagrams (either ASCII or mermaid). But if it's an entire subsystem, I have a skill in which I can ask for visual and interactive explainers. It'll generate a markdown page (or html) that has these interactive components that help demonstrate a concept.
To help make the concept stick, there are quizzes after each section and at the end.
With the computational simulations, I generate a throwaway tool in which I can explore the state space. This doesn't happen as often, as I try to keep state spaces small, and for those explainers tend to work well.
With the design simulations, a same-model agent and a cross-model agent and I will talk about an explainer, and we'll name things that we understand or notice about the design and the intended direction we want to extend it. Then, we'll use subagents to generate options and then discuss the pros and cons. I find this is a good way for me to understand the dead-ends of a design.
Using Verifiers and Guardrails
I define guardrails as mechanistic checks that doesn't change with the feature. such as linters, typecheckers, and pre-commit hooks are all guardrails. Verifiers are mechanistic checks that will be different with each feature, such as unit/integration tests.
For more complicated states or concurrent parts, I'll ask the agent to use formal methods to write a proof. Then use the proof to generate a trace (a series of steps that demonstrate a happy or unhappy path). Then I run that trace in unit tests (kinda like property-based testing--so I guess this can be called trace-based testing), and the code has to adhere to the trace.
I also wrote a design verifier, where the requirements in the spec can be converted into a series of mechanically checkable rules. Then during testing, the verifier fires up the headless browser and uses the DOM measurements of the page to see if it matches up to the rules. This was completely impractical before agents.
All of this so a) I can be sure that the agent is doing what it's supposed to with the decisions that it's making b) I can be out of the coding loop, so that I'm not up at 2am begging, "you fixed this, but you broke that. pls fix." The agent can check for itself.
---
If any of this sounds helpful, lemme know. I've been wondering if other engineers care about understanding the code that's being written, or if people just don't care anymore. I've spoken to a few, and it seems to be split. And it may just be a matter of degree and detail. Some like it at a higher level. Some like it nitty gritty.
But if you're trying to gain the same level of tacit understanding by writing quizzes and simulations, are you actually gaining anything from using a LLM? Isn't it just better, faster and quicker to write the code yourself? Not to mention more enjoyable?
By doing so, you debase your own profession.
You're going to have to understand it one way or another, either at writing or at debugging. Even if you delegate all debugging to AI, you'll still need to determine what is "right".
If you don't understand the code at writing or debugging, you'll pay the architectural price (where it degrades). That price may be worth paying, but that depends on the system you are building.
You wont be able to review or understand code for much longer.
There will be just too much of it. The vastness of code libraries in 5 years are hard to imagine by today's standards.
We need to develop tools that help us understand.
I think a useful perspective is "How does engineering management control software quality?" You will know the answers to this question: testing practices, design documents, SLAs, release management. You will know this because you almost certainly have to comply with some of these as a SWE. All of those practices exist because code produced by humans is not perfectly reliable and thus there need to be checks and risk reduction steps to manage the software that is shipped.
The way I view agents is that all of us have become engineering managers now. So those tools are now your tools. You set up the practices and frameworks and make the agents use them. If you set them up right you can be very confident in what the agents wrote without ever reading it.
Now you're not going to go from nothing to "Hey I ship code I've never read" overnight. It takes work. You have to set up your project carefully so that the agents can’t make mistakes. But all you have to do is be systematic about it. Every time the agents do something stupid or they ship a bug, you think, "How do I put something in place to prevent this entire class of bugs from happening again?" As you iterate this process, you will continuously be ruling out entire classes of bugs and eventually you'll be able to trust the agents.
Example: I had a bug, or a class of bugs, where agents would write a database migration and it would be wrong. It would get rolled out and the database would be broken. I fixed this by putting in a migration testing framework where there was data written in an old schema, then the migrations would run on that data. Then you could write tests on the expected result at any point after any migrations. Then, I put in place a rule: if you write a database migration you have to write a migration test. That fixed the problem. I have never had a migration failure since.
So this is not some magical process. It's going to take you hard work to set up your system with all of this infrastructure to make the agents maximally productive. But as you iteratively improve your system, you can reach a place where you can have agents ship code without reading it. It's not just about making the system work but it's also about building your trust in the system that you have. I routinely ship code without reading it now and I think I've shipped about two bugs in the past two months (and I’ve written ~500k LOC in that time). And one of those bugs was very explicitly my fault for telling the agents they could violate one of the frameworks in place (oops).
1. If you keep shipping AI-written code you don’t understand, you become entirely dependent on the AI to maintain it, because you lack the mental model you’d need to jump into it quickly.
2. You can afford to do this as a senior, because you can tell when the AI does something suboptimal. But how is a junior supposed to develop that intuition when all they start out with is AI-generated code?
I have some work in Lean4 that I'm certainly unqualified to validate; Layer Coding QEC derivation, Positive Grassmanians and the Fresnel-Kerchhoff correction to the Parallel-Axis theorem, Huygens intensity as an alternate model for simulating photonic and electronic interference in a double slit experiment, for example.
It was a black box but I'm letting it tutor me about it so
Visual proofs, Tools for logic and formal methods in distributed systems
Things that compile logic!
> That means you can end up with systems you've built where you don't truly understand how all the code works
Though AI coding and system development can be overwhelming due to information overload and rapid growth in complexity (and concern for unwittingly embedded mistakes, the unsecurability of agent-based development, and number of LOC of code per day), not understanding the whole system sufficiently is normal and expected in engineering.
This week I've actually learnt something at work. This is what feels good. Building yourself. There's no long term joy in building things you don't understand, especially if you're building them for someone else.
The problem here is that you're trying to do a job that is already being done for you. Maybe you don't yet trust it to do a good job, but that is quickly changing.
Models and harnesses have gotten really good, but they still are hard-capped by the operators that use them. The sycophancy of models is perhaps their greatest weakness, as when a user who might be experienced in one area attempts to use AI to build within an area they are less familiar with, the model does not take a step back and examine the "why" of what is being built from an architectural standpoint, it simply performs the expected task in a direct manner, ultimately creating sloppy work that may be easy to ship but difficult to maintain.
A good example of this was a recent interaction I had with a Dev who was very familiar with how to develop embedded and system level applications. He and every other dev are being asked for increasingly more due to AI, and he had to bring something he had built as a single-user desktop application into a distributed cloud environment. He ended up rsyncing a copy of his working directories including multiple credentials and sensitive internal project documents to a virtual machine binding it to a public ip address. Thankfully our tooling detected this before he could get any farther but it illustrates how the AI model isn't doing any real thinking or engineering at the end of the day. Unless the operator knew to instruct the model about proper cloud architecture and understood best practices around deployment of public systems, the model simply did what it was being told to do by a user who didn't know any better.
Another example of this is the sheer amount of unsophisticated users we've been detecting using Javascript injection into their web browser sessions as a form of automation for web services they may use. These users would never know to ask how to request an API token which may allow them to accomplish things in a much easier and approved way, and instead are relying on the frailest possible automations that expose them to risks of credential exposure.
We've got to be careful with how we engineer things with AI models. Just because we've made boilerplate easier to generate doesn't mean that we can avoid the actual engineering of what we do. And I can't help but think that firms which are fully committed to AI code generation, won't just be introducing an avalanche of slop into their product which will explode the costs of maintenance over time.
Which is fine, usually. Depends on what you are doing.
The things that always mattered were the core system, the trunk of a tree if you will. The branches less so, and the leaves not at all (though that is what people see and appreciate! The irony!).
Same method.
https://theseus.dev
Basically, Theseus reads your pull request and your codebase, categorizes the kinds of edits that were made, and writes human-readable documentation that describes what changed. Then, it shows the explanation side-by-side with the code, but presented in a way where you can decide what you want to skim and what you want to deeply review.
I don't think engineers should have to choose between wading through thousands of lines of slop and just smashing LGTM on every pull request. The problem is that the tools for code review are built for an age where code was expensive to write. Now that code is cheap, we need to start experimenting with tools that allow us to stay in sync with what's going on, while also not needing to review every single line individually.
don’t wash your hands, or at least don’t expect us to wash them for you
Recognise you aren't the one shipping it.
I've started using Stryker. We have a CI job that mutates the code before running the tests. In the first run, it has returned hundreds of code changes confirming that the tests are biased. I haven't yet fixed those issues, but for now i am banking on this approach.
Occasionally, Claude may not be able to fix the issue – and it may turn out that Codex can, because it has a different approach.
Either way, at least two LLMs is the only way to get (mostly) reliable code.
quality is all we have, it’s what makes us different from these agents.
quality is perplexing to agents because it requires a strange loop into asking why. there’s not always a clear answer. but i think the power is in the ambiguity.
if we don’t know why (or don’t care) then what happens if we all adopt this attitude at scale?
When you write C++ or Rust do you try to understand the generated assembly code?
Imagine the carnage if real engineers started vibe-designing bridges.
I think it's silly to make strong statements like this. _Most_ professional software engineers (women too, not just him!) are using LLMs and are moving some amount of faster as a result.
The code was always a black box. Always.
Specs come in, passing tests come out. If you find an issue, your specs were shit.
Anyone that has ever had to outsource work to someone else knows this. If you've ever worked with another team whose codebase you didn't touch - you know this.
You write your specs, you launch an agent to implement them. You launch a 2nd agent to keep the first one honest. When done, you launch as many agents as you have budget to try and poke holes in the implementation. As it always was the case.
You can thrash around (and you will), you can fight it (and you will). Like it or not, the genie is out of the box, and the realization you were never really good at your job to begin with is gonna set in eventually.
I was with you until this sentence. You know you can have an opinion without having to put anyone down?