The first version of mode-switching in CodePulse was clever. The bot looked at what the user was typing, examined the active topic, peeked at recent message history, and tried to infer whether they wanted Code mode or Real-life mode. We called it auto-detect. It was a feature we were proud of. It was the feature that broke the most. We eventually deleted it and replaced it with three buttons. The buttons shipped with no algorithm and no inference — just plain Code, Real-life, and Exit. User satisfaction with mode-switching went up the day we shipped them. Here's the story of why deleting our cleverness made the product better.
How auto-detect worked
The original heuristic ran on every user message. It looked at three signals:
- Topic context. If the active Telegram topic was already attached to a Code-mode session, treat the next message as Code-mode unless something contradicted it. If it was attached to a Real-life session, treat it as Real-life.
- Message content. Apply a set of regex patterns. Messages mentioning "git", "deploy", "test", "PR", or file paths leaned Code. Messages mentioning "email", "calendar", "send", "schedule" leaned Real-life.
- Recency. Look at the previous successful session in this topic. If the last query and reply were Code-mode, lean Code. If Real-life, lean Real-life. Decay this signal after 30 minutes of silence.
We weighted the three signals, applied a confidence threshold, and routed the message to whichever mode scored higher. If neither mode scored above the threshold, we asked the user to clarify with an inline prompt: "Which mode?"
The thing was internally consistent. It worked in our test scenarios. It looked good in demos. We shipped it.
What users actually experienced
The first sign that something was wrong came from a user trying to send an email about a code review. Their message was, "draft an email to the team summarizing the architecture changes from the PR I'm reviewing." The heuristic saw "architecture changes," "PR," "code review" — heavy Code-mode signals — and routed the message to Code-mode. Code-mode received the request, decided the user wanted to read the PR diff and write a summary file, started spawning a session.
The user saw what looked like a Code-mode session starting in their Telegram. They clicked Stop. They tried again with different wording: "send the team an email about the architecture changes." This time the heuristic caught "send," "team," "email" — heavy Real-life signals — and routed to Real-life. The user got their email. But they'd had to think about how to phrase the request to win the heuristic's vote.
Once the pattern was visible, it was visible everywhere. Users were learning the heuristic's patterns. They were avoiding certain words. They were rephrasing messages to game the routing. The bot was teaching them to be cautious instead of being expressive. Auto-detect was creating cognitive overhead, not removing it.
The second sign came from the support inbox. The most-asked question was "how do I switch modes?" The answer was "type something that triggers the other mode" — which was true and also a non-answer. Users wanted a switch. We'd given them a guess. They were now responsible for forming the right shape of message to influence the guess. That was a worse UX than just letting them pick.
The argument for keeping auto-detect
We resisted the change for a couple of weeks. The argument for keeping the heuristic was that it was more usable in the happy path. When the heuristic guessed right — which was most of the time — the user typed once and got the right behavior. When we replaced auto-detect with explicit picking, every interaction would be one tap longer.
That argument was true and irrelevant. The happy path got a little easier. The unhappy path got much harder. And the unhappy path was happening often enough — to enough users — that the average experience was worse than the explicit version. Users hated guessing wrong more than they appreciated guessing right.
We also had a maintenance argument. The heuristic was N hundred lines of code with a regex list, a weight tuner, a confidence threshold, decay logic, and a fallback prompt. Every time a user surfaced a misroute, we tweaked the weights. Every tweak broke something else. The code was not converging. It was a permanent maintenance burden that only existed to avoid the user picking.
The third argument — and the one that ultimately broke our resistance — was that the heuristic was actively hostile to user trust. CodePulse is a product that touches your repo and your inbox. The user has to trust that when they type something, the right code path will run. A heuristic that gets it right 90% of the time is asking the user to gamble on the other 10%. Trust requires predictability. Predictability requires explicit control.
The three-button picker
We replaced the heuristic with /select. The command shows three inline-keyboard buttons:
- Code — start (or resume) a Code-mode session in this chat
- Real-life — start (or resume) a Real-life session in this chat
- Exit — exit any active session and return to idle
That's it. There's no fourth button for "use auto-detect." There's no recommended option. The three buttons are equally weighted, displayed in a fixed order, and labeled in plain English. Tapping any button transitions the chat to that state immediately. Tapping the same button while already in that mode is a no-op. The picker stays available — it can be invoked any time.
The picker was implemented as a single-screen Telegram inline-keyboard panel. The buttons render large enough for thumb taps. The active mode is highlighted so users always know what state they're in. The picker also shows a one-line context — "Code-mode session active" or "Real-life session active, 14 tools loaded" — so users have orientation before they tap.
We deployed /select and removed the auto-detect heuristic in the same release. The first 24 hours produced a flood of feedback. About 90% of it was positive. Many users wrote things like "thank god" and "I never understood when it'd switch" and "this should have been the original design." About 10% of users asked where auto-detect had gone, and after a brief explanation they all said the same thing: "OK, I'm fine with the buttons."
Why three buttons beat one heuristic
Predictability creates trust. A button labeled Code always opens Code-mode. There is no scenario where it opens Real-life. There is no input the user can craft that will route differently. The contract is total. The trust that creates is disproportionate to the cost of the extra tap.
Explicit beats implicit on irreversible actions. Both modes can do irreversible things — Code mode can write files, Real-life mode can send emails. When the action is irreversible, the user wants to know exactly what mode they're in before they confirm. Auto-detect deferred that decision to the heuristic. The picker forces it to the front, which is where it belongs.
Three buttons compress to one decision. The picker is not three sequential decisions. It's one decision with three outcomes. The user looks at the three options, picks one, and moves on. Cognitive load is minimal. The "one tap longer" cost is real but negligible compared to the "two retries to get the right routing" cost auto-detect imposed when it guessed wrong.
Explicit state is testable. The picker's behavior is one switch statement. The heuristic's behavior was a continuous function of message content, history, topic, and recency. Testing the picker is enumerating three transitions. Testing the heuristic was a permanent QA tail. We deleted hundreds of lines of code and the dozens of tests that tried to pin its behavior, and the product got more reliable, not less.
Users can teach themselves. A new user sees three buttons and figures out what each does in seconds. A new user encountering a heuristic has no observable surface to learn from. They have to type things, observe the results, and slowly build a mental model. The picker is self-documenting.
The lessons we'll keep applying
Auto-detect is a debt instrument. Every time you build a heuristic, you're borrowing user goodwill against future tweaks. If the heuristic becomes part of the user's mental model, you can't change it without breaking them. If it doesn't become part of their mental model, they don't trust it. Either way you're trapped.
Cleverness should compete with the obvious. Before shipping a heuristic, write the dumbest version of the same feature — the explicit picker, the fixed default, the user's manual choice. If the dumb version isn't unambiguously worse, ship the dumb version. The dumb version will scale. The clever version will erode.
Tap economics are usually a red herring. "One tap longer" is the kind of metric that sounds like it matters but doesn't move user satisfaction in the direction the metric suggests. What moves satisfaction is certainty about what just happened. A picker that adds a tap and erases the uncertainty is a net win.
Removing features can be the feature. The day we deleted auto-detect was a release we were nervous about. We expected the response to be "you took something away." The actual response was "thank you for taking something away." Users had been carrying the heuristic's failures as a tax. Removing it didn't subtract a feature; it removed a frustration.
What we kept and what we extended
We didn't replace every CodePulse heuristic with a button. The bot still has heuristics for things where the user's mental model and the heuristic's output rarely diverge — autoselecting the active session within a mode, routing a reply to the most recent prompt, expanding a tool name into its category. Those heuristics work because the user doesn't have a mental model that conflicts with them. They run silently and are correct.
The mode-switching case was special because it touched two semantically distinct workflows that the user holds differently in their head. The user's intent was "I want to email someone" — a clear, conscious thought — not "I want to invoke a routing decision." A heuristic that reinterprets a clear intent will lose against an explicit picker every time.
The picker has been in production for several releases now. Mode-related support tickets have dropped to near zero. The code path is simpler. The tests are simpler. The product is more trustworthy. We replaced our cleverness with a button, and the button won.
When to use a heuristic anyway
Heuristics are still the right tool when one of three conditions holds. First, when the user has no preference between outcomes — pick a default and let them override only if they care. Second, when the user has a preference but can't easily articulate it — auto-detection of language, timezone, or theme works because users couldn't pick from a list anyway. Third, when the cost of the wrong outcome is trivially recoverable — autocomplete suggestions, sort orderings, layout defaults. CodePulse's mode-switching met none of those conditions. The user had a strong preference, could easily articulate it, and the wrong outcome had a non-trivial recovery cost.
If you're considering a heuristic, run your case against those three conditions. If your case fails any of them, the explicit version will probably win.
Tap /select to see the three-button picker in action. Download CodePulse, open any chat, run the command, and pick. The features page covers the rest of the product. Premium plans on the pricing page include AI commit review and the full Real-life Mode toolkit.