Learning Rust while the agents write it
I picked Rust when I knew only the fundamentals.
Task 3 broke the app. App runs: 3. Suspects: 1.
I took on two problems at once
When I chose Rust for the whole app, I knew the basics. That is very far from image code that keeps up when you drag a slider. The second problem is the goal: the image must change as soon as you move a slider, even in a folder with thousands of RAW files. That is a speed target, and in my experience speed is very hard to add at the end. Many side projects stall when they take on both problems at once. I think that mine did not stall because I do not type the code.
Where my time goes
Planning is the part that I do not hand over
Every change starts as a paragraph and a list that I write myself. It says what the change is, which parts of the app it touches, what I leave out on purpose, and how I will know that it worked.
That last item does most of the work. Without it, the agent picks its own idea of "done", and that idea is usually generous to itself. "Make thumbnails faster" is too vague to check. This target is not: "A folder of 3,000 photos fills the first two screens, the scroll does not stutter, and memory does not climb while I scroll."
A plan is also the cheapest place to be wrong. Sometimes an agent confidently builds the wrong thing. If I find that in a paragraph, it costs me a minute. In finished code, it costs an afternoon, and I probably will not throw that code away, because by then it compiles and I am attached to it. This is the habit of the seven-item list in the first post.
Building one thing at a time
Every change must leave the app in a working state. I build and run the app after each task. When two changes land together and the app misbehaves, I cannot tell which one caused it, and that is what I most need to know. Agents write a lot of reasonable-looking code very quickly, and if I let them bundle changes, I lose that clue.
I do not take the agent's word for it
Agents are quick to announce success: "The thumbnail loading should now be much faster." That word "should" cost me more time than any bug in this codebase. I do not count a number until I see it myself.
The pixel maths has real tests. Exposure, the tone curve, white balance, and the other adjustments are pure functions: the same input always gives the same output. So I check them against fixed numbers.
I check scrolling, decoding, panning, and memory when I run the app. This is my weakest area. I can see if something is broken, but not if a change made the app faster or slower, because I never click the same way twice. I want to fix that next.
Getting a second agent to review
When a change is done, a fresh agent with no memory of the conversation that produced the code reviews the diff, the list of lines that the change adds and removes. This helped more than I expected. The agent that just wrote four hundred lines is a poor judge of them, and it will happily agree with whatever I suggest.
Rust compiles all five without a warning. The compiler has a set of rules about who owns each piece of memory, and a clone (a copy) is the easiest way to make it stop complaining. On a 60 MB image in a function that runs every frame, that copy is a real problem. A request for new memory inside a loop that runs once per pixel looks reasonable until it runs twenty million times. A lock stops other threads from touching some data, so holding one during slow work is how a background task ends up freezing the interface.
Then I read the diff myself. I rarely catch what the reviewer missed, but reading diffs is how I learned Rust.
Keeping releases dull on purpose
A script bumps the version, builds each platform, and updates the site with the new build. It is safe to run twice by accident. If a release takes concentration, I put it off, and then nobody can use a month of work.
| Stage | What I hand over | Done when |
|---|---|---|
| Plan | A paragraph and a list | I can state what would prove it |
| Build | One task, with its scope | It compiles and the app still runs |
| Test | The proof from the plan | I saw it, on a real folder |
| Review | The diff, to an agent that did not write it | Findings are fixed or consciously declined |
| Release | A version number | The build on the site is the build I tested |
Four ways this goes wrong
Fixing symptoms. The agent patches one symptom, then the next one. After four rounds, the code is full of special-case checks, and nobody has said what the real problem is. Now I stop and reproduce the problem, and the agent names the cause before it changes any code. Then it makes one fix.
Confident numbers that nobody measured. An agent once told me that a change made something "roughly three times faster", when it had not run anything at all. If I do not see a number get measured, I treat it as a guess.
Code that is correct but wasteful. Rust's compiler checks that your code does nothing unsafe with memory. It does not care if you copied fifty megabytes for no reason. Speed is the point of this app, so a change must pass a speed check before I accept it.
The change keeps growing. A change picks up work that nobody asked for. The out-of-scope list in the plan stops most of it, and I catch the rest in the diff.
How I learned Rust
Four things taught me, in approximate order of value.
- Reading diffs and asking why. I asked "Why is this line here?" a few hundred times, about real code that does something I care about.
- Asking the agent to explain its choices. Why a
clone here? Why a reference there? Why
Arc(a way to share one value between threads) and not a plain value? The answers made the ownership rules predictable. - Compiler errors. The borrow checker, the part of the compiler that enforces the ownership rules, rejects mistakes bluntly and tells you exactly where they are.
- Writing some of it by hand anyway. I write one function per session, with no agent. It is slow, but it shows what I understand and what I only recognise.
A few weeks in, I can read all of the Rust in LightPhotos and write most of it. Reading matters most. An agent can always write more code than I can, but it cannot tell me if that code is good. If I cannot tell good code from code that only looks right, I will get an app that only works, not the app that I want.