• 0 Posts
  • 12 Comments
Joined 2 months ago
cake
Cake day: August 10th, 2026

help-circle


  • The future software engineer looks more like a lawyer to me: much fewer of us, each serving multiple clients, billing insane amounts in 6 minute increments.

    Billing $375/hour for 3 hours work is a fast track to defaulting on your mortgage. You spend too much time finding clients and billing clients. Plus it’s hard to keep a small-job pipeline full, so you spend a bunch of time sitting around with no clients. This is fine when you’re 25 and you are fine spending a lot of time hiking instead of working and have reasonable tuition loans. It’s less fun at 40 with a mortgage.

    Furthermore, at least in major US cities, typical lawyers have been worse off than typical programmers for a while now. And lawyers are increasingly terrified of AI, too.

    So don’t automatically assume that “programmers becoming more like lawyers” is some magical land of happiness, is all I’m suggesting.

    Source: Have worked as a freelance software consultant, and once did a legal industry startup.


    score 2 · canonical



  • Often I am 80% confident that something is wrong, to me the appeal of Claude is that I can quickly check, and be certain that it is wrong and how it is wrong.

    I find that this is most appealing when it’s my own idea that might be wrong. My local LLM is a surprisingly capable critic of my ideas. And it is very good at finding the one nasty corner case that will send my entire design back the drawing board. Of course, I’d find that corner case eventually, but it might be 12 hours of work later, or perhaps when I’m working on version 2 and I find out I committed to a bad API. Like any outside technical criticism, I’ll decide that half of it is probably wrong, and filter the other half through my own knowledge.

    But none of this excuses either outsourcing my own thinking, or just parroting Claude at my coworkers.


    score 1 · canonical


  • I haven’t considered simpler models but might try - I use Opus 5.5 and Fable and often find it way oversteps and drowns me in code.

    Using simpler models is partly about keeping me honest. Fable actually has pretty good taste about certain domains. Which means that by the time it does mess up, I have 2,000 lines of code to deal with.

    For the “not getting drowned in code” part, I’m relying more on the other parts:

    • Using jj, which gives me patch-stack superpowers (at a price, though jjui and stakk help).
    • Asking the LLM for “Linux style patches” (one logical change per patch). This forces the LLM to think in small chunks.
    • Providing a custom skill about how to write a clear commit message.

    Taken together, this can really simplify deep review.

    One thing I would add - it may or may not be in your workflow - is the use of agents to understand ideas.

    Yes. Agents are excellent at this. I often say, “So here’s how I’d like to handle this. Can you see a reason why this won’t work?” Then the agent points out 5 problems. Three of them are silly, but one of them means I need to to do a major rethink.

    I also have a specialized skill which tells the model that the human is an experienced software designer with the magical ability to change the requirements. So if the model gets stuck on a messy design challenge, it gets maximum credit for flagging that to me. This actually gets many overenthusiastic models to stop more often and to explain why my plan was far too simple. That’s one of the points where I tend to start refactoring and typing.


    score 1 · canonical


  • Sure, I should definitely write this up properly at some point!

    The basic idea behind my workflow is that writing code isn’t the real bottleneck. The real bottleneck is understanding the problem and the code. So anything that generates code faster than I can understand it is a problem. So agent swarms are mostly useless, because they speed up code generation without improving understanding. Asking an agent to look at my code and see if I’ve overlooked anything, on the other hand, increases my understanding. Especially if I do the fixes myself.

    Now, it’s also important that not all code requires equal understanding. Just like on human projects, there are parts I don’t care too much about, and parts that I watch like a hawk. Knowing which part is which is where experience helps. The core Rust traits in a system are very important. The 9th implementation of some trait may not be.

    Similarly, I choose my models carefully. My model needs to be smart enough to debug a broken library, or to help me figure out mathematical invariants for a prop test. But it shouldn’t be so smart that I’m tempted to let it do a lot of work unsupervised. Somewhere between Sonnet 4.5 and Opus 4.6 is ideal. Fable is too smart. In practice, this means running open models like Qwen3.8 27B, Qwen3.8 Flash Next, or DeepSeek V4 Flash 0731. These are all smart enough to walk me through the interaction of two monads (say, flatMap and propFlatMap) if I ask. But they’re not smart enough to encourage true vibecoding and “not even reading the code.” Probably the largest I would consider for my own work is GLM 5.3 Flash, but you have to be really rich to run it locally.

    My favorite agent harness is Pi. One session, no background agents, no TODO list, no plan mode, no fancy workflows. Maybe 1,500 tokens in my AGENTS.md, plus 2 or 3 tiny skills that tell the agent how I like to break down problems. (Which I do not share publicly, because I don’t want future agents to learn how I break down problems.)

    For version control, I use jj. Using it really well isn’t any simpler than learning git. But it makes it easy to break up changes into multiple tiny pieces, and then interactively edit any of those pieces. I have some skills telling the agent to “break changes up Linux kernel style,” and how to write a good commit message. This allows me to review tiny, standalone pieces right in Zed and edit patch 3 of 4 all that I want.

    I still invest time in my editor. I’m trying Zed these days, with Zeta2 as a code completion model. Zeta2 is nice, because it only completes obvious things, and it rarely completes more than 3 lines. If it has any doubt, it moves my cursor to the part I need to fill in. I work in Rust whenever I can, because I like Rust. And I very regularly drop into my editor and rough out traits, refactor things, and think with my fingers.

    Overall, this process is good because it maximizes my understanding. And once a project passes 3,000 lines, a deeper understanding helps me move faster. Like, I started programming on the Apple IIe, and I like to imagine I’ve gotten pretty decent at it. Plus, it’s fun. Letting an agent write all the code would be like letting an agent go watch a fun movie for me.

    But the north star of this process is that I’m optimizing for understanding, not speed. And from there, everything else is just making deliberate choices. My process has actually gotten more minimalist with time. And as agents have gotten smarter, I’ve become increasingly alienated from frontier models. I want a model that will punish me swiftly for trying to delegate too much understanding. It’s sort of like my kayak: It’s quite maneuverable, but it punishes sloppy technique by making me swim.

    This is not the only process I use. Sometimes I just turn the agents off entirely, or limit them to research. But at its best, this understanding-focused process is like having a smart pair programmer or a faculty advisor. I can ask it for tips on what I’m overlooking, or I can say, “I don’t really feel like converting from the old Rust error handling library to the new one, so please do that for me.” But at each step, I focus on maintaining understanding. And if I do lose understanding, I also lose productivity.


    score 2 · canonical



  • There is something quite odd about these comments.

    I think there’s a disconnect which might explain some of the oddness. Let me try to explain. I have seen two types of attitudes towards AI, even here on lobste.rs:

    1. “Our project does not want slop PRs.” This is an entirely rational policy for many projects. I run many of my projects this way! If I really want slop PRs, I can always ask an agent myself. Slop PRs are dirt cheap, and I can get more than I want to read in an hour or two.
    2. “AI is morally wrong, and I don’t want it involved in my project.”

    When I say “strict no AI policies”, I’m thinking about group (2).

    if one were to find some mistake, one could fix it in a fork, one could write a blog post about it,

    As I suggested in my original post, yes, I might quietly fork someone’s project. Or If I believed a data corruption bug affected lots of users, I might write a blog post to get the word out. Or sometimes it’s easier to switch to something else.


    score 1 · canonical


  • When you think about it, contributors who are completely turned away by the fact that they can’t use LLMs are probably contributors you don’t want in your project, even if you have a Debian-like “moderate use of AI” position.

    If a project says, “No AI,” then I absolutely respect that. Nothing which has been touched by AI in any way will be sent to that project. This is pretty clearly what a lot of people with that policy want. They ethically object to any use of LLMs. It’s a matter of moral purity, not a practical question. I wouldn’t offer them something that involved AI at any point any more than I would offer a vegan something that had touched butter.

    Let me give you an example: I was working on a distributed word frequency estimator. Before I left for a meeting, I asked Claude to check the distributed counts and see if it could find anything interesting. Claude found some weirdness in the numbers, and—completely unasked—it started digging around in one of the open source libraries I was using. It found a nasty and very subtle bug in the open source library.

    So now I have a problem: I know about a serious bug. But it was found entirely using AI. As lawyers like to say, it was “fruit of the poisoned tree.” There is no way that I can retroactively remove AI from the process.

    Now, that author had no policy against AI contributions. So I hand-wrote a nice bug report and offered to fix it. Fascinatingly, Claude made a subtle mistake in its proposed fix to the algorithm. But both I and the upstream author made exactly the same mistake Claude did the first times we tried to fix it. The only thing that actually fixed the bug was when I (or maybe Claude at my request?) wrote some proptests, which found a super tricky edge case. (I actually think the incomplete fix is present in some academic papers about the algorithm, IIRC. I’m still not even 100% sure that the final fix was correct, but it holds up under a quarter million randomly generated test cases checking various mathematical properties, which is a good start.)

    So that’s my dilemma: Sometimes a coding agent will find bugs in someone else’s code. These may be subtle correctness bugs, or data corruption bugs, or serious security vulnerabilities. And if I discover one of these bugs, there’s a decent chance AI will be involved in some way. This is because my first reflex when someone else’s library does something weird is to ask the nearest coding agent to take a look. Life’s too short to debug things like treesitter myself when an agent will almost always find the problem in 2-5 minutes. I may have to fix the problem myself, and of course I’ll write my own commit messages and PRs.

    But if you have a strict zero AI policy, then I’m going to assume that you’re doing it on moral grounds. Which puts me in the awkward position of knowing that your code is computing incorrect answers, or corrupting user data, or leaving your users vulnerable to attack. And then I have to guess whether you want to know about a bug discovered using AI. If you have a strict no-AI policy, I’m not going to hide the AI use.

    In practice, if it’s a security bug, I’ll probably verify the bug carefully by hand and disclose it to you via a private channel, if I can find one. For other bugs, I may switch to another library, or just quietly fork yours. Which feels antisocial. But given how many “no AI” policies feel like ritual purity laws (nothing wrong with ritual purity laws!), I don’t want to inflict AI-discovered bugs on people who are strongly anti-AI.

    On my personal projects, I actually have a mix of AI policies. I will normally close slop feature PRs without reading. Slop bug-fix PRs, well, I’ll either read them myself, or if the PR is too annoying, and confused, I’ll ask my own agent whether the “bug” is real. A few of my projects are “learning” projects, and these will forbid all AI-written code, because the point is learning. Or sometimes my policy is “trusted maintainers can use AI if they want, but we’ll reject slop feature PRs from other people.”


    score 2 · canonical


  • The way Singeli design differs is that it embraces compile-time programming being a different kind of language than runtime, where other frameworks treat the differences as dirty secrets.

    Interesting! Mojo runs the same language at compile-time and run-time, except that:

    1. The compile-time language can manipulate types as values.
    2. You can do things like comptime if (compile time conditionals) and comptime for (compile time loop unrolling).

    It’s actually a pretty slick system: Quite a bit of power, and vastly simpler than C++ template tricks.

    but I’m always amazed at how many hurdles these languages put in front of what in Singeli is just normal array programming.

    To be fair, Mojo is less interested in arrays (though it does support them) and more interested in tiled tensors. This is the kind of memory-layout optimization you normally expect to see in Fortran and on GPUs, but not in too many other places.


    score 1 · canonical