ertac.paprat.com
EN

← Writing

AI Pair Programming: A Tool That Never Asks Why

· 2 min read · English

Rewritten: . Rewritten with AI assistance. Examples and tool references follow the original publication period.

Ask an AI coding assistant to “calculate the total after a discount,” and a plausible answer is easy:

def discounted_total(subtotal_cents, discount_percent):
    return round(subtotal_cents * (1 - discount_percent / 100))

It is short. It looks readable. For many ordinary inputs, it returns what you expect.

But whose rounding rule is this? Are discounts allowed to exceed 100%? Is a negative subtotal a refund or an invalid input? Does the discount apply before tax, and does it apply to every line item?

The function cannot answer because the request did not answer. The assistant filled a gap with a reasonable-looking choice.

The useful conversation starts with a constraint

For a deliberately simplified example, suppose the requirement is: nonnegative integer amounts in cents, whole-number discounts between zero and 100, applied once to the subtotal, with half cents rounded upward. Taxes and refunds are outside this function.

Under that stated contract, a possible implementation is:

def discounted_total(subtotal_cents, discount_percent):
    if type(subtotal_cents) is not int or type(discount_percent) is not int:
        raise TypeError("Use integer cents and a whole-number percentage")
    if subtotal_cents < 0 or not 0 <= discount_percent <= 100:
        raise ValueError("Invalid subtotal or discount")

    numerator = subtotal_cents * (100 - discount_percent)
    return (numerator + 50) // 100

assert discounted_total(105, 50) == 53
assert discounted_total(1000, 0) == 1000
assert discounted_total(1000, 100) == 0

This is an illustration, not a universal billing function. A real payment system must follow its own accounting, tax, and currency requirements. The improvement here is that the code now has an explicit rule to satisfy.

An assistant can help express that rule, suggest edge cases, and draft tests. It can also write a test that simply repeats its own mistaken assumption. Generating code and generating approval of the code are not independent evidence when both start from the same incomplete prompt.

Divide the work by what can be checked

Good candidate tasks include finding related code, proposing a local refactor, drafting repetitive cases, or explaining unfamiliar syntax. Each gives the developer something inspectable.

The developer’s contribution is not limited to accepting or rejecting suggestions. It includes supplying context the repository may not contain: a contractual requirement, a migration plan, a previous production failure, or the reason an apparently awkward branch exists.

When the assistant explains its output, use the explanation to generate questions. Then inspect the relevant code and run the checks. A confident account of why a function works is still a claim about the function.

Count the review, too

Faster typing is one possible benefit. Total delivery time also includes reading the proposal, rejecting unsuitable changes, testing, and fixing mistakes discovered later.

For a fair comparison, try bounded tasks and record that whole process. Separate a first draft from a merged, understood change. Do not turn one pleasant session into a claim that a tool makes every developer a fixed percentage faster.

The most useful pairing session may end with less generated code than expected. In the discount example, the valuable output is the rounding rule and the test that exposes it. Once those are settled, the implementation is almost incidental.