“String or &str?”
A function formats a greeting from a name and returns the text. It only reads the name. Your agent asks which parameter type to take. Choose.
“Do you want this async?”
A CLI tool: read a config file, call one HTTP API, print the result. Your agent proposes the shape of the code. Judge it.
I’ll make it async with tokio and reqwest. It’s the modern default, and it lets us add concurrency later without a rewrite.
The convenience that is a commitment
Your agent is building the ingest path for a service reading 40,000 sensor frames a second. Frames get parsed, filtered, and dropped: nothing downstream keeps them. It has made one call and explained itself.
I’ll take the frame by value, fn parse(frame: Frame), rather than by reference. It keeps lifetime annotations out of the public API, and Frame is only 96 bytes, so at 40k/s the copy is around 3.8 MB/s. Negligible. I’ll also derive Clone on Frame so callers can keep their own copy if they need one.
“Box or generics?”
Deploy notifications go to email, Telegram, or both, chosen from a config file at startup. Your agent asks how to type the notifier. Choose.
“Channel or shared state?”
Webhook workers, like the HelloHarel sync, process events in parallel and update one job ledger. Your agent offers two shapes. Choose.
“One crate or a workspace?”
A new sync service, about 3,000 lines, written by your agents. Your agent proposes the repository layout. Judge it.
I’ll set up a workspace with seven crates: domain, ports, adapters-http, adapters-db, config, errors, and app. Clean architecture from day one; it will scale as the team grows.
“Is this error recoverable?”
The sync cannot reach HelloHarel for one order in a batch of a thousand. Your agent proposes what the HTTP adapter should do. Judge it.
I’ll retry three times with backoff inside the HTTP adapter, then panic. This should never happen in production, and a panic makes it loud.
What survives this level
| Owned type vs borrowed view (String / &str) | Take a borrowed view when you only read; take ownership only when you store it. |
| Trait objects vs generics | Chosen at runtime or mixed in one collection: Box<dyn>. Known at compile time and hot: generics. |
| Send and Sync | A Send/Sync error names what may not cross threads; fix the design, never the marker. |
| Arc<Mutex<_>> vs channels vs actors | Default to one owner behind a channel; a shared lock is for small, rarely contended, read-mostly state. |
| Sync vs async and the colour problem | async only when many things wait at once; it colours every caller and commits you to a runtime. |
| "Fearless concurrency" is a compile-time claim | The compiler proves the absence of data races, not of deadlocks, contention or a bad design. |
| Crates and workspaces as module boundaries with teeth | Split a crate where a second agent needs to own something; not before. |
| Zero-cost abstraction — what it promises | Zero-cost means no runtime cost; you pay in compile time, binary size and reading generic signatures. |
| Compile time as a project-management cost | Every generic and every crate split adds to the loop your agents run all day; budget it like CI time. |
| Workspace boundaries as the unit of delegation | One agent, one crate, one public surface; agents meet at crate edges, not inside modules. |