The Articulation Workflow

Most senior engineers I talk to are either not using AI at their desk, or using it badly. The ones who aren't using it usually cite reasons that boil down to "the tool doesn't understand what I want." The ones who are using it badly typically use it as autocomplete, or as a rubber duck. Both of those miss the point.

There's a specific way to work with AI that turns it into the highest-leverage tool a senior engineer has had in a decade. It doesn't look like pair programming. It doesn't look like autocomplete. It looks like a fast articulation loop where the human contributes intuition and the AI contributes throughput. Once you see the shape of it, it's easy to run. But getting there means understanding what you're actually good at as a senior, and using the AI to amplify that specifically.

Here's what the loop looks like, illustrated by a real refactor I ran recently, and what I'd tell you about each stage.

The starting point

I'd just landed a feature. Tests passing. Lint clean. Feature-complete. Read the code again and didn't like it. Not because anything was broken, but because the shape was wrong.

That "shape is wrong" signal is the senior thing. Pattern-matching on past code that grew badly. Often pre-verbal. I can't yet say why the code is wrong. But I can feel it.

That feeling is the input to the loop. Everything else is what you do with it.

Step 1: name the smell

The first move is to articulate what I'm sensing, to Claude, in plain language. Not as a formal review comment. Not as a specification of what the fix should be. Just: "This whole class seems to be merging arrays upward. Every method builds a list and returns it, then the caller concats them."

The act of putting it into words is doing work. Even for me. The pre-verbal sense sharpens as I try to name it. Sometimes the naming reveals that what I sensed was actually two problems, or that it was less serious than I thought, or that it was a symptom of something bigger.

The naming also gives Claude something concrete to work with. "This code is bad" doesn't help. "This class is building lists by mutation instead of by declaration" is a specific claim Claude can react to.

A useful test I've adopted: can I articulate the smell in one sentence? If yes, I'm ready to hand it to Claude. If no, I'm still figuring out what I sense, and I need another pass of reading the code before I can articulate. Trying to hand vague dissatisfaction to an AI just produces vague suggestions in response.

The naming step is also where I catch myself when the dissatisfaction is wrong. Some percentage of the time, articulating the smell reveals that I was wrong to be dissatisfied. The code is actually fine; I was pattern-matching against a superficial resemblance to something I'd disliked before. When this happens, I stop the loop. The dissatisfaction signal is high-value but not infallible. Being willing to abandon the refactor when the articulation reveals the code is actually fine is part of running the loop well.

Step 2: get options, not answers

The next move is a request I make almost every time: "Show me two or three alternatives with tradeoffs."

Not "fix it." Not "tell me the right way." Alternatives.

Claude's default, unprompted, is to give one answer. That answer is usually competent but not great. It's pattern-matched from training data, which means it's the most typical way to solve the problem, which means it's plausible-but-generic. If you accept the first pass, you get a codebase full of plausible-but-generic code. That's the mid-level failure mode expressed through AI.

Requiring 2-3 alternatives fixes this. Claude has to think about the space of solutions instead of picking one from the middle of the distribution. It sketches Option A (surface fix), Option B (the pattern I probably wanted), Option C (the ambitious version I hadn't considered). Some of them are wrong. That's fine. Seeing them side by side is what lets me pick, and often the picking includes a hybrid that combines elements from B and C.

For the refactor I keep coming back to as an example, the three options were: convert the imperative shapes to declarative (surface fix), extract rule classes with a light factory (medium), and pull "what state is this domain in" into actual domain models (structural). I saw the tradeoffs. B was clearly best for the current situation. C was the version to remember if the codebase kept growing.

What to ask for in the alternatives request:

  • "Give me 2-3 alternatives with tradeoffs, ranging from minimum-churn to most-ambitious."
  • "For each option, sketch enough code that I can see what the shape looks like."
  • "For each option, tell me what evidence would justify picking it."

The last one is the most important. Asking Claude to name the evidence that would justify each option turns the discussion into a decision procedure instead of a preference contest. Now I'm not picking the option I like the aesthetic of. I'm asking "what's true about the codebase that determines which of these is right."

Step 3: pick by evidence, not aesthetic

This step is the one most seniors get wrong when they're new to the workflow.

The temptation, when Claude presents options, is to pick by which one looks nicest. Which one has the cleanest final code. Which one lines up with your general aesthetic. That's what mid-level engineers do, and it's why they don't get more leverage out of the loop.

The senior move is to pick by evidence. "Is there existing duplication that would justify option C? How many callers does the extracted domain model have today? What does the next phase of work need?"

These questions become tractable because Claude can scan the codebase in seconds. "Grep for this pattern across the four generation services." "Find every place we do x.presence || 'default'." "How often does this shape appear?" Answers come back concrete, and the option that had aesthetic appeal starts looking speculative when the evidence isn't there to support it.

For my refactor, this step killed one candidate (only one caller today) and elevated another (already duplicated in five places). Option C got refined into "extract only the one that has evidence, defer everything else." That's a much better decision than "extract all the domain models because Option C looks clean."

The specific prompts I use in this step:

  • "Grep the codebase for [pattern]. How many times does it appear? Show me the callers."
  • "Look at the roadmap. Which upcoming features would use this?"
  • "What's the cost of extracting this now versus extracting it later when the second caller shows up?"

The third question is the most valuable. It surfaces the real tradeoff: extract-now-and-be-wrong (you spend the extraction budget on something you didn't need) versus extract-later-and-fix (you eat some duplication temporarily until you have evidence). Most of the time, extract-later-and-fix wins. Speculative architecture is the more common failure mode.

Step 4: iterate on the output

Claude's first-pass implementation is usually competent but not great. The mid-level failure mode is to ship the competent one because it passes tests. The senior move is to read it again after Claude finishes and find what's still off.

For my refactor, the first pass extracted eight rule classes, each with an #items method that built its own item structure inline. Tests passed. Lint was clean. But every keyword argument being passed to the item constructor was derivable from self, which is the data-clump smell asking me to move the data onto the receiver.

I named that to Claude. "These rules are all constructing items inline. The parameters are all self-derived. This is the Template Method pattern asking to be born."

Claude iterated. The base class ended up owning the item construction. The subclasses collapsed to hook methods declaring properties. def kind = :blocking. def step = :onboarding. Five hook overrides per subclass. No parameter passing.

Would I have gotten there solo? Eventually. Would I have gotten there in one afternoon? No. The throughput on the iteration was the win.

The number of iteration passes matters. For a simple refactor, one pass. For a substantive one, two or three. If you're on iteration five and each pass surfaces new smells, the underlying design is probably wrong. Stop iterating and back up to the options step. Try a different approach entirely.

Step 5: verify against evidence, not intuition

At the end of the loop, before you commit, run one more check: is the final shape justified by the evidence you gathered in step 3, or has the refactor grown speculative in the iterations?

The failure mode is: you started with a clear evidence-backed refactor, but the iterations added abstractions that weren't justified by the original evidence. You extracted a helper because it was clean, not because it had a second caller. You made a class polymorphic because it might expand, not because it needed to today.

The check: does every abstraction in the final shape have a specific caller or an imminent one? If yes, ship. If no, back the speculative abstractions out.

This step catches speculation that got in through the iteration cracks. It's boring. It's also where the difference between "we refactored for real" and "we speculated our way into a bigger mess" gets decided.

What this workflow isn't

It's not pair programming. In pair programming, both people are engaged with the code at similar depth. The AI isn't. It's throughput on your intuition, not a peer at the same level.

It's not rubber ducking. Rubber ducks don't propose 2-3 structured alternatives in 30 seconds. The response loop changes the shape of the conversation you're willing to have.

It's not autocomplete. Autocomplete predicts the next few characters. This loop is about restructuring shape at the level of design decisions.

It's not "the AI writes the code and I review." The workflow requires you to drive the conversation, articulate the intuition, choose between options, iterate on output. If you're a passive reviewer, you're not doing this workflow. You're doing the "let AI drive" workflow, which produces different (worse) results.

The closest analogue I've found is working with a junior engineer who's very fast, very willing to iterate, has vast breadth of knowledge, and no depth of intuition. You give them the direction. They produce the artifact. You critique. They iterate. Multiply the throughput by ten. That's what the loop feels like.

What the AI contributes well

To be concrete about what the AI is actually doing in this loop:

Codebase scans. Finding every instance of a pattern across the app, faster than I can grep and read the results. "How often does presence || default show up in this codebase?" is a 30-second question with the AI, compared to five minutes of grepping and reading.

Option layouts with tradeoffs. Three alternatives with code sketches, in the time it takes me to explain the smell. This is the leverage step. I couldn't sketch three options that fast solo.

Full rewrites with tests still passing. When I've picked a direction, the AI can execute. Move the code. Rename the class. Update the callers. Run the tests. This is grunt work that used to eat the afternoon.

Naming brainstorms with comparison. "I want a name for this domain concept. Show me 3-4 candidates with fit and tradeoffs." Faster than I'd do it solo, and often better because the AI generates names I wouldn't have.

Documentation writing. After the refactor lands, I ask Claude to write the PR description, the changelog entry, and any docstrings. It knows what changed. It can articulate it faster than I can.

What the AI contributes badly

First-pass judgment on architecture. The AI will happily agree to speculative abstractions if you ask. You have to say no.

Sensing when the shape is wrong. The AI doesn't feel dissatisfaction. If you don't push back, it lands at "feature-complete and tested" and stops.

Refusing to over-engineer. Left alone, the AI will extract more abstractions than the codebase warrants. It's your job to hold the "wait for a second caller" line.

Distinguishing adjacent-but-different patterns. In my refactor, the AI initially claimed that four generation services were duplicating a completeness query. On re-grep, they were doing a related-but-different pattern. Conflating them would have made the extracted class a god-object. Catching that required me, not the AI.

Understanding the local team's norms. The AI knows patterns from training data. It doesn't know that your team has decided against a specific pattern for a specific reason. That kind of local context is on you to provide.

The workflow is a division of labor. AI does throughput. You do sensing. Neither works alone.

What to do when the AI pushes back

An interesting failure mode: sometimes Claude disagrees with the direction I've chosen. It'll propose a fourth option that wasn't in the original three, or it'll say "before I do this, are you sure the intended shape wouldn't be simpler?"

This is not failure. It's a good signal. When Claude pushes back, one of two things is happening. Either (a) Claude sees something I've missed and I should reconsider, or (b) Claude is being conservative in a way that doesn't serve this specific situation. My job is to figure out which.

The way to figure out which: ask Claude to make the case for its pushback with specific evidence. "Why do you think the fourth option would be better? What in the codebase supports that?" If the evidence is compelling, I take it. If the evidence is generic pattern-matching to a common practice that doesn't apply here, I proceed with my original direction.

The two-way conversation is a strength. It's when I've been sloppy and Claude catches me, and it's when Claude is being too cautious and I need to override.

Comparison with other AI-pair patterns

The articulation workflow lives in a specific spot in the space of AI-assisted development patterns.

AI as autocomplete. Cursor-style completion. Fine for typing efficiency but doesn't engage design judgment. Works at the level of the next few characters. The articulation workflow works at the level of design decisions.

AI as junior pair. "Explain this code to me." "Suggest a fix for this bug." Useful for narrow tasks. Doesn't build the loop I'm describing. Passive on the human's part.

AI as reviewer. "Review this PR." "Look for issues in this code." Useful after the fact. Doesn't shape design during the writing.

AI as driver. "Write me a new feature." "Refactor this class." The mode most likely to produce the failure modes I wrote about in "Why Letting AI Drive Produces Worse Code." The AI-driven workflow inverts the human/AI responsibilities.

The articulation workflow is: human drives design decisions, AI executes on them, human critiques, iterate. Human is the intuition; AI is the throughput. That specific division is what produces the best output I've found.

What it feels like

The best short description I have for the workflow is that it feels like turning up the clock speed on my own intuition. The intuition itself doesn't change. But the cost of acting on it, of exploring what it's pointing at, of committing to a change based on it, drops dramatically.

That's a real shift. The senior job used to be "have the intuition, do the work slowly." Now it's "have the intuition, drive the loop, ship the same day."

The engineers who figure this out are pulling ahead. The ones who haven't yet aren't slow, they're just still doing the "do the work slowly" version. The gap between the two ways of working will keep growing until it's obvious enough to fix.

The specific caution I'd offer, though: the workflow only works if the intuition is calibrated. If your dissatisfaction signal is wrong more often than it's right, running the loop harder just produces more speculative refactors faster. The intuition is the load-bearing part. AI can amplify it, but it can't replace it. Which means the value of this workflow is proportional to the value of your existing judgment. Seniors get more from it than mid-level engineers not because AI treats them differently, but because their intuition is better in the first place.

That's the real dependency. Everything else is throughput.


If you're a senior engineer trying to figure out how to work with AI without losing what makes you senior, that's what most of my client work has become about. If it'd help to compare notes, get in touch.

Next
Next

Ten Strategies for Saying No