We Removed Undo to Save Tokens. Users Left.
We built an AI design tool. Users loved the first draft. Then they hated the second one. The problem was not the model. It was our budget.
Every regeneration cost tokens. Every correction cost tokens. We watched the free pool drain. So we made a decision. We removed the undo button. We replaced it with a single "regenerate" action. Users could not fix what the AI got wrong. They could only gamble on a new roll. They left.
This is a postmortem. It is a story about how free tokens made us build the wrong product. Disclosure: This article was prepared as part of MonkeyCode's product outreach.
The product
We built a small tool that generates interface drafts. The user types a prompt. The agent calls a model on MonkeyCode's free server. The model returns a layout. The user reviews it. That was the whole loop.
The first version had a correction flow. The user could select an element and describe a change. The agent would re-run with the original context plus the change. It worked. Users could steer the output. The second version removed that flow. Why? Tokens.
The math that fooled us
Every correction call reused the full conversation history. The original prompt. The first draft. The change request. The model re-read everything. A correction cost nearly as much as the original generation. We ran the numbers. Corrections made up most of our token usage. The conclusion seemed obvious. Cut corrections, cut costs.
We were right about the math. We were wrong about the product.
Here is what the first version looked like:
def correct_draft(original_prompt, draft, change_request):
messages = [
{"role": "user", "content": original_prompt},
{"role": "assistant", "content": draft},
{"role": "user", "content": change_request},
]
response = client.chat.completions.create(
model=MODEL,
messages=messages,
)
return response.choices[0].message.content
Every correction sent the entire history. The model re-read the original prompt and the full draft. That is expensive. We saw the cost and panicked.
What we actually removed
We removed the ability to say "the header is too tall." Users could only say "try again." That is not a correction. That is a lottery ticket.
The first draft was always decent. The second draft was often worse. Users could not anchor the parts they liked. They could not fix the parts they hated. Every regeneration was a fresh roll of the dice. After three failed rolls, they closed the tab.
We watched the numbers. Session length dropped. Regeneration count stayed flat. Users were not trying more. They were giving up faster. The token savings were real. The product collapse was real too.
The fix that costs fewer tokens
We did not need to remove corrections. We needed to make corrections cheaper. Three changes got us there.
First, we stopped sending the full conversation history. We sent the original prompt and the specific change request.
def correct_draft(original_prompt, change_request):
messages = [
{"role": "user", "content": original_prompt},
{"role": "user", "content": change_request},
]
response = client.chat.completions.create(
model=MODEL,
messages=messages,
)
return response.choices[0].message.content
The model lost some context. It kept enough. Token cost per correction dropped by half.
Second, we limited the number of corrections per session. After three, we asked the user to start a new draft. This created a natural boundary. It also prevented runaway costs.
Third, we made the correction request structured. Instead of free-form text, users picked an element and chose from a few change types.
def correct_draft(original_prompt, element, change_type, detail):
change_request = f"{element}: {change_type} - {detail}"
messages = [
{"role": "user", "content": original_prompt},
{"role": "user", "content": change_request},
]
response = client.chat.completions.create(
model=MODEL,
messages=messages,
)
return response.choices[0].message.content
Shorter prompts. More predictable output. Fewer tokens. The correction flow came back. Token usage stayed within budget. Users stayed.
Why correction matters
This is the part I keep coming back to. An AI output is rarely perfect on the first pass. The value is not in the first draft. The value is in the distance between the first draft and the final result. That distance is covered by corrections. Remove the corrections and you remove the value.
We designed for passive acceptance. The user accepts what the model gives. That is a terrible interaction pattern. It works only when the model is always right. It never is.
Think about your own tools. When was the last time you accepted the first output without a change? Exactly. Now imagine the change button disappeared. Would you stay?
Who should not learn from us
Small experiments do not need a correction flow. A weekend prototype can survive a regenerate button. A product that users depend on cannot.
Also, do not copy our fix blindly. Our numbers came from one tool, one model, one audience. Measure your own. The free tier gives you room to experiment. Ten million tokens is a lot of failed drafts. Use them to learn, not to ship a worse product.
The real lesson
Free tokens are not free. They are a constraint. The constraint changes your decisions. The danger is when you optimize for the constraint instead of the user.
We optimized for tokens. We built a worse product. The free server was not the problem. The free server was the excuse.
Run the numbers. Yes. Cut waste. Yes. But cut waste in the right places. Measure what each feature costs. Then measure what each feature earns. Correction earned user retention. Retention was worth more than the tokens.
Have you ever cut a feature to save tokens? What did it cost you? Tell me in the comments. I want to know if we were the only ones.
MonkeyCode provides free models that can run this workflow.
Top comments (0)