An instructor told me the attendance-code flow in uPoll was confusing students.
The obvious response was to make the instructions clearer. We could have changed the wording, moved the code higher on the screen, or added another line to onboarding. Each option would have been quick. Each would also have preserved the thing causing the confusion.
So we removed attendance-by-code entirely in uPoll v3.0.0.
Students now get an alert when attendance opens. There is no code to receive, remember, or type. The instructor still gets the outcome the feature was built to provide. The student carries less of the design.
That was the strongest product decision I made this week, and most product teams are organized to avoid it.
A shipped feature starts defending itself
Once a feature exists, every conversation about it starts with the same quiet assumption: the feature stays.
People discuss copy, placement, speed, error messages, and training. The debate sounds practical because those are all things a team can change. Nobody has to reopen the earlier decision or explain why the work was built in the first place.
The feature also has weight now. Somebody designed it. Somebody wrote the code. Tests cover it. Documentation names it. A release note announced it. Removing it can feel like admitting that work was wasted.
That feeling is expensive.
The work is already spent. Keeping the feature doesn’t recover it. It only asks every future user to keep paying for the original decision. This is known as the sunken cost fallacy: a cognitive bias where people continue an endeavor or stick with a decision because they have already invested time, money, or effort, even when quitting is the more logical choice.
In this case, the payment was tiny: one code entered during class. But tiny friction repeated across a room still becomes the product. A student doesn’t experience the architecture or the reason the feature made sense on a whiteboard. The student experiences one more thing standing between opening the app and being counted present.
The instructor’s report gave us a chance to defend the interface or protect the outcome. We chose the outcome.
The feature wasn’t the job
Attendance codes were one way to answer a larger need. An instructor opens attendance. Students respond. The instructor gets a usable record.
The code was an implementation detail inside that flow. Over time, it had started acting like the flow itself.
This happens easily when you build software. The database has a concept, the interface gets a screen for it, and the team starts speaking about the screen as if users arrived wanting that object. Users rarely want the object. They want to finish a piece of work with less effort and more confidence.
That distinction matters because a request about a feature can carry two very different messages.
Some feedback says the feature is doing the right job and needs a better edge. In the same release, direct user requests led to collapsible poll answer options and a grade-display fix. Those changes preserved the underlying interaction and made it easier to use.
Other feedback says the user is carrying work that belongs to the product. The attendance-code report was that kind. Improving the code screen would have made us better at asking students to do something the alert model could remove.
You can’t tell the difference by counting requests. One clear report from a person using the product in a real classroom can expose more than a backlog full of feature ideas. The useful signal is where the burden sits.
Deletion requires a written outcome
Teams struggle to remove features when nobody wrote down what those features were meant to accomplish.
Without that sentence, the feature becomes its own justification. Attendance-by-code exists because the product has attendance codes. The circular logic feels solid inside a roadmap review because everyone already knows the nouns.
A plain outcome breaks the circle.
For this flow, the outcome is an instructor opening attendance and getting a reliable record while students do as little extra work as possible. Once that is visible, the code has to compete with every other way to reach the same result. An alert is simpler for the student, so the old implementation loses.
I use four lines to pressure-test a feature before adding another patch:
Name the user outcome without using the feature’s name.
Name the work the current design puts on the user.
Sketch the shortest path to the outcome with the current feature removed.
Compare that path with what real users have already reported.
The first line does most of the work. If a team can’t describe the outcome without naming the interface, it has probably confused its solution with the user’s job.
The second line makes small burdens visible. A code, an extra confirmation, or a required status choice can look trivial in isolation. Users experience the accumulation.
The third line creates permission to start over on paper. No migration plan is needed yet. No estimate. Just a sketch of the cleanest route.
The fourth line keeps the exercise honest. A tidy diagram made in a product meeting is weaker than a sentence from somebody using the thing under real conditions.
Feedback is evidence, not a specification
The instructor didn’t arrive with a replacement architecture. The report was narrower: the attendance-code flow confused students.
That distinction protects both sides of the product conversation. Users shouldn’t have to design the system for the team. They know the moment where the work becomes awkward. The builder owns the translation from that moment into a product decision.
Sometimes the translation is direct. A request for collapsible poll options points to a clear display problem. A grade that appears incorrectly points to a clear result that needs correction. The job and the requested change line up.
The attendance report reached deeper. The confusion lived inside a step that the product could remove. Treating the report as a request for clearer code instructions would have translated the words while ignoring the work.
This is why a feature-request list isn’t a roadmap by itself. Two requests with the same number of votes can carry different product information. One asks you to make an interaction work as intended. The other reveals that the interaction has become a tax on the outcome.
I don’t need a large survey to inspect that second kind of report. I do need enough context to understand the real use, the stakes of changing it, and the path that replaces it. Deletion should come from evidence and a protected outcome, not from impatience with maintenance.
The standard is simple: remove the feature only when the replacement asks less from the user and still does the job. That is subtraction with responsibility behind it.
AI makes the wrong fix easier to ship
AI can produce all the obvious patches fast.
Give it a confusing screen and it can rewrite the instructions, move the field, add validation, generate tests, and draft the release note before the meeting ends. That speed makes a patch feel cheap.
The hidden cost sits with the user. Every patch that preserves the wrong interaction adds more code around the original assumption. The system gets larger while the experience barely improves.
This is one reason I don’t treat AI as the product decision-maker. It can explore five implementations in minutes. It can’t decide which burden my users should keep carrying under my name. That call needs the context around the work and responsibility for what ships.
The useful 70/30 split shows up clearly here. AI can handle a large share of production. I keep the judgment about what deserves to exist.
Faster building raises the value of deletion because adding another layer has almost no immediate friction for the builder. When code was expensive, effort forced some restraint. Now the restraint has to come from taste, observed use, and a clear outcome.
Deleting a feature is still product work
Removal can look like less ambition from outside the team. Inside a product that people use, it is often the cleaner form of progress.
We still had to change the flow, update the surrounding behavior, and make sure the alert model carried the job the code used to do. Deletion didn’t mean shrugging and walking away. It meant spending the effort on a simpler contract with the user.
That contract matters more as a product grows. uPoll now has 297 students, 120 lectures, 520 polls, and 1,172 responses in its production aggregates. Those are counts, not engagement claims. They tell me the product is far enough past its first demo that a small burden can reach a lot of real use.
At that stage, shipping more surface area isn’t automatically progress. The product earns trust when each new version asks less from the people using it while protecting the outcome they came for.
The attendance change did that. Students stopped carrying a code. Instructors kept the attendance flow. The product became smaller at the exact point where it became more useful.
A ten-minute deletion review
Open the backlog for one product or internal workflow you own. Pick the feature that keeps attracting requests for clearer instructions, better placement, or one more exception.
Set a timer for ten minutes and write four lines:
The outcome the user needs.
The work the feature puts on the user.
The simplest route with the feature removed.
The evidence from someone who actually uses it.
If the new route protects the outcome and removes work from the user, give deletion a real place in the next product conversation. Don’t let the time already spent make the decision for you.
The best fix may leave you with less software to maintain and a better product to use.
PS. If your business is carrying workflows that keep getting patched but never get simpler, I help map the outcome, remove the unnecessary steps, and build the parts worth owning. Start at coaching.g8n.ai.



