PYX data product agentic engineering
← Field notes·01·Requirements·6 min

The clarify loop you used to run in Slack

Forty messages over six days to pin down “active customer”. The loop was never the waste. The waste is that nothing survived it.

Every analytics team runs the same loop. Nobody’s ever given it a name, which is probably why nobody’s ever costed it.

Someone asks for a number in a meeting. A BA writes two pages about it. An engineer reads the two pages, finds the four places they’re ambiguous, and asks in a channel. Three people answer. Two of them disagree. The disagreement gets settled by whoever’s still online at six. Then the SQL gets written.

Six months later somebody asks why the number is what it is, and the only thing left standing is that SQL. It records the decision. Nothing else. Not the argument, not the trade-off, not the person who made the call.

For years I blamed the loop. Wrong target. The loop is the work. Deciding whether a customer who moved out on the 3rd counts for that month isn’t overhead on top of building the data product. It is the data product, and everything after it is typing. The waste is that the loop leaves nothing behind. Every hand-off downstream runs it again from memory and lands somewhere slightly different.

What the model is actually good at

Not answering. Models are unreliable at deciding whether an executed move-out excludes the account, because that’s a policy your business made and it lives in neither the schema nor the training data. What they’re very good at is finding every place a requirement hasn’t been decided yet. That’s pattern-matching across a huge pile of vague specifications, and it’s the exact job a human is worst at by four in the afternoon.

So flip it. Don’t ask the AI for the number. Ask it for the questions. Paste the requirement the way the person actually wrote it, hedges intact, because the hedges are the tell, and let it grill the thing into typed claims and blocking questions. A human answers the blockers once. And the answer gets stored on the requirement instead of in a channel.

Ask the AI for the questions. Keep the answers. The questions are a language problem. The answers are your business.

The org chart nobody wants to look at

Here’s the uncomfortable part. A modern analytics team is mostly a translation layer. Count the roles whose real output is somebody else’s meaning restated in a different notation. It’s most of them.

Before
  • BAwrites a spec that’s read once and never opened again
  • Eng ×2translate the spec into SQL, guessing at the gaps
  • Analystbuilds the same logic a third time in DAX, because the report never matched the warehouse
  • Stewardaudits it months later against a spec that’s already drifted
Five people. Four translations. One quarter. Meaning re-derived by hand at every boundary.
After
  • BAthe requirement they wrote is the artifact: clarified, typed, binding
  • Engrules on drafted bindings instead of authoring the model
  • Translatorthe ticket-taking SQL translator
  • Rebuilderthe DAX re-implementation of logic that already exists
  • Stewardcontrols bound at design time, so the audit is a read, not a project
The translation work is what disappears. The judgement work grows: grain, policy, exclusions, what counts. It’s the only work left.

I’m not going to dress that up. If your job is converting a paragraph into a GROUP BY, that job is on a clock. But the people doing it aren’t bad at the judgement half. They’ve just never been given a week where the judgement half was the whole job. Every team I’ve walked through this kept the same people and stopped shipping the same number three times.

How to run it

Paste it verbatim. Don’t tidy it up first. Where someone wrote “broadly” or “where relevant”, that’s the part that’ll hurt you in month four.
Read the blocking questions before the claims. Any question you can’t answer without going to find someone is the requirement’s real cost, and now you know it on day one instead of week three.
Answer, or park. Parking is a real state. A parked question is a stated unknown that blocks what it should block, rather than a silent guess made by whoever happened to write the code.
Argue with the claims. The model over-claims. That’s what it does. Demote the inventions. And because the claims are typed, one that compares a date to an identifier gets refused before it can become a column.

The payoff people notice first is speed, and it’s the least interesting one. The payoff that matters shows up two years later, when someone asks why the number excludes flat-rate accounts and the answer isn’t a person’s memory. It’s a clarification, dated, attributed, sitting on the requirement, still binding the model that still generates the table.

So the next time a requirement lands in your queue, ask yourself where the argument about it is going to live. If the honest answer is “a channel”, you already know how this ends.

On timing claims. Where we say a quarter of hand-built work compresses to weeks, that’s a design target, not a measured result. It gets measured on a real estate, including where it comes in slower. See the build stage.
Start a conversation

Have a data problem in mind?