harlley.dev
post

What an agentic product engineer actually does all day

Less typing. More wiring up agents that can. A field note from the messy middle.

  • #agents
  • #product-engineering

I used to ship features by typing them. Now I ship features by typing the spec, watching an agent type the feature, reading the diff, and pressing approve.

That sounds glib. It isn’t.

The shape of the day shifted

A year ago, my unit of work was a function. I’d open a file, hold a small mental model of “what calls this,” and edit. The output was a commit.

Today the unit of work is a task that an agent can run. The hard part isn’t the implementation - the agent can do that. The hard part is writing a task the agent can actually execute without me babysitting it. Names. Shapes. The weird invariant that lives in the head of a senior engineer and nowhere in the code. If I leave any of that out, the agent fills it in by guessing, and the guess is plausible, fluent, and wrong.

So my morning looks like this:

  1. Open a TODO.md.
  2. Write a task that - if a stranger picked it up cold - they could finish.
  3. Dispatch an agent to a fresh git worktree.
  4. Move on to the next task. Come back when it’s [?].

Why this actually works (when it works)

Three things make it not-snake-oil:

Worktrees. Every agent gets its own checkout. Two tasks running at once can’t step on each other; if one of them ruins the tree, I delete a directory. This is unglamorous infrastructure that turns “AI did something to my repo” from a disaster into a yawn.

Squash merges. Nothing reaches main until I read the diff. The agent can fail loudly, fail silently, or hallucinate a beautiful new architecture - none of it matters until I press approve. The cost of a bad agent run is the time to read a diff and type discard.

Reviewable spec. This is the new craft. A task description is now production code: ambiguous prose ships ambiguous features. I find myself writing TODOs the way I used to write integration tests - adversarial, listing the edge cases out loud, naming the thing that should not happen.

What I miss

The tactile pleasure of typing a function from scratch is largely gone, and I think that’s actually the loss people are feeling and not naming. There’s a real grief in trading “I made this with my hands” for “I directed an agent to make this.” Some days I write a small utility by hand just to keep the muscle warm.

The other thing I miss: the false confidence of “I read the code, therefore I understand it.” With agents, I read more code than I write, and I’m constantly reminded that “I read it” and “I understand it” are different states. Pair-debugging with an agent that’s confidently wrong is a humbling teacher.

What I’d tell someone starting

  • Optimize for the spec, not the prompt. A good prompt with a vague spec is a faster way to ship a worse feature.
  • Treat agent runs as cheap and disposable. The whole point of the worktree is that you can throw it away. Throw it away.
  • Keep one tool you can build by hand without an agent. It keeps your taste calibrated.

The job hasn’t really changed shape - it’s still about shipping things people can use. What changed is the throat I’m grabbing when something breaks.