Ramblings on Effective Development with LLMs, as of 2026

Posted on Oct 29, 2026

About a year and a half ago, I wrote a post about how relying on LLMs removed meaningful signals we derived from development friction. Since then (say the line, Bart), the models have advanced significantly and the way I’ve thought about them has changed. I felt like it was time to write more and share where I’ve landed.

In short: LLMs are incredibly powerful and are becoming a necessary tool as part of development. We can use them to move fast, and answer questions we wouldn’t have spent time on before. At the same time, I don’t think we should throw away what we’ve learned as an industry over the last several decades.

Stay Grounded with the Realities

LLMs are fantastic at solving problems, especially with a clear goal. When they run into small obstacles, they are very capable of finding workarounds in service of that goal. It’s very easy to ask an LLM to solve a problem and show you the quantitative benefits. It’s also quite easy to lose sight of what’s actually happening.

A common trap I see is folks asking an agent1 to optimize a piece of code, write benchmarks, and then trusting the results directly. Honestly, in a lot of cases, this might be fine, but there are other cases where LLMs’ craftiness will outpace their “thinking” about the problem. In a recent example at my day job, I ran into a case where Codex had decided to go to great lengths to isolate garbage collection effects of the BEAM runtime between runs. These choices didn’t make sense to me, and the LLM could not justify them coherently2. I regenerated the test cases with a popular benchmarking tool (Benchee), and the LLM’s pretty picture faded away; its optimized code performed quite similarly to the existing code.

I share this example because the siren song of having your problems solved for you is often too easy to give in to. It is often valuable to punch through the abstractions and see what is actually happening3.

Stay Repeatable

Several years ago, I watched the talk Curiously Recurring C++ Bugs at Facebook from Louis Brandy; one part has stuck with me when I think about codebase health: “tools are our best teaching weapons.” Louis suggests that the sooner you can tell people they’ve written bugs or violated a norm, the better; there are diminishing returns the later they receive the feedback. I go a step further: if the rules are not applied consistently, defects and learnings get missed or drowned out by noise.

In the “before times,” we would use tools like linters or runtime sanitizers to catch bugs or violations of norms. These tools were by no means perfect, but they followed a clear ruleset. However, building them often had a high barrier to entry, as you would need to also build parsers/AST walkers or runtime instrumentation to achieve the desired effects. This was workable, but often too high of a cost for custom rules.

When we couldn’t write lint rules for a norm or a footgun, we’d defer to humans to catch violations in review. LLMs have made it extremely easy to simulate code review before submission. It’s tempting to simply write more Markdown files and ask LLMs to apply them; it’s a low-cost way to get some decent results. I think, however, it’s wasteful and not consistent enough to be the preferred option.

Given we already have this magic code-writing box, why not use it to build more tools? One way I’ve applied this is to write lint rules that apply mostly to my company’s codebase, such as rules to prevent common misuses of library functions. These tools can be reused without burning tokens, run way faster than any LLM, and be adapted to new needs as they arise. I never would have been able to justify spending time on this a couple of years ago, and these lints cut down on code review churn significantly. Plus, it keeps the robots honest.

Stay Curious

I’ve always thought of myself as someone on the “craft” side of software engineering; I want to know how things work and produce new things that solve real problems elegantly4. I consider it a strength of mine that when I have questions about how something works, my instinct is to crack open the library and see where certain documentation claims or emergent behaviors come from.

I think this is something we need more than ever. It’s easier than ever to produce something. There are many discussions on the web about taste and building the right things, but more than that, I think we should make sure the things we build do the things we think they’re doing, the way we think they’re doing it. Do the tests assert what we think? Are those things meaningful?

If friction is no longer the tool we need to use to build new abstractions or tools, we can start applying our curiosity and desire for betterment to all of the output we produce, not just when we get stuck.

Stay Human

Above all else, I think we need to remind ourselves that we’re humans working towards shared goals. We often all want the same things; let’s deliver empathy where we can, even if it’s not the most efficient way of solving problems. A code review, for example, is a wonderful place for knowledge sharing, not just catching bugs. We don’t necessarily need to replace it with LLMs and remove the human aspect of the process, but we can augment it to make it more effective.

…and of course, I’m prepared to be humbled by the models in three months, and completely change my opinion. Exciting times.


  1. Yes, yes, an agent is not an LLM; it is a tool built on an LLM. I use them somewhat interchangeably here, since the distinction isn’t meaningful. ↩︎

  2. To be fair, its reasoning may have had some merit, and I don’t remember the details clearly enough to refute it. That’s part of what shapes my thinking, though; in the moment, I couldn’t verify it and felt it more practical to lean on already-proven tools. ↩︎

  3. As an aside, I’m not super swayed by the “LLMs are like compilers” argument. It’s true that they can abstract details from us if we don’t want to look at them, but I’d argue the key difference is the artifacts we keep around. We don’t usually commit the generated assembly or emitted binaries to version control, because we modify the source code when we want to make changes. With LLMs, we do commit the output, and throw away the input. There’s been some debate about whether we can simply commit specs in human language, and then regenerate the code when we want to make changes. Maybe some day that will be true, but it’s not the way we commonly use LLMs day-to-day. ↩︎

  4. I still write code by hand at home! It gives me joy. ↩︎