← Back to blog
    Case · AI product in productionJune 23, 20268 min read

    Closer: the bot I shipped to production — and keep running

    In 1997 psychologist Arthur Aron sat two strangers across from each other and handed them a list of 36 questions. They escalated: from "who would you want as a dinner guest" to "when did you last cry in front of another person." At the end — four minutes of looking into each other's eyes in silence. Several pairs grew close after the experiment. One got married.

    Questions bring people closer. But only when two people answer and actually hear each other — not peek and adjust. I took that principle and built a product on it. Built it with AI — I'm a fractional AI consultant, and I build with the same tool I sell. But the architecture, the forks, and the production reality are carried by me, not the model. I designed it, shipped it to production, and I keep running it. The bot lives under the name Closer, works every day, and does exactly what it was made for: two people get to know each other more deeply.

    This case isn't about "I made a bot." It's about how I ship an AI product to production with AI — and what starts AFTER launch. Because "after launch" is precisely what separates a working product from a folder on GitHub.

    Blind mutual revealis the mechanic where both partners answer the same question separately, and the other's answer only opens once both have answered. Nobody sees the other's answer in advance, so nobody can adjust to it — each answers honestly. It's the core of the product and the main invariant around which everything else is built.

    What is the bot for, and why does it work?

    The goal is simple: a couple should know more about each other after the bot than before. Not "we played and forgot," but "oh, I didn't know you thought that."

    That moment is no accident. It's built into the mechanic. The core is blind mutual reveal: both answer the same question separately, and the partner's answer only opens once both have answered. Nobody sees the other's answer in advance, so nobody can adjust to it. Each answers honestly — which is why the reveal lands.

    The goal is met by architecture, not persuasion. That's the case.

    What's inside the bot?

    In short: pairing, sessions of questions with rising intimacy, a daily question, a "guess the answer" game, and streaks. Point by point:

    • pairing by code or by a forwarded link: deep link → one tap → you're a pair
    • sessions of 5 questions escalating in intimacy, 4 moods across 3 levels, 128 questions
    • the "Passion" section opens only by mutual consent
    • a question of the day, every day, with a smart nudge to whoever stays silent
    • a "guess the answer" game: you answer first, then guess your partner
    • streaks, a history of revealed answers, an admin panel with stats and broadcasts

    What does the architecture look like?

    The key decision — separate the clean logic from the Telegram shell. The whole substance of the product lives in modules with not a single Telegram import, and is therefore fully under tests. Telegram is a thin layer on top.

    app/
      clean logic (no Telegram, under tests)
        pairing.py   pairing, codes, parsing invites
        reveal.py    the reveal rule: visible only when both answered
        sessions.py  picking questions by rising intimacy
        streaks.py   streaks and their recalculation
        daily.py     scheduling and choosing a fresh question of the day
      data
        db.py        SQLite layer, schema, migrations
      Telegram shell (thin)
        handlers/    start, session, question of the day, admin, router
        runner.py    assembly, registration, background scheduler

    Why this way and not "everything in one file"? Because the product is about rules, and rules have to be checked. When the logic is separated from Telegram, tests run against it — and on any change the reveal, streaks, and question selection don't drift apart.

    Which decisions actually made the product?

    The case isn't "we made a bot." The case is in the decisions.

    Blind reveal as an invariant

    The partner's answer isn't visible until both have answered. One rule in one function, with the whole product built around it.

    One active session per pair

    So a pair doesn't open three conversations at once and get lost. A simple invariant that removes a whole class of bugs.

    A database for real conditions

    At first the data lived in an external cloud database. On the server it breathed unevenly: a query would take 0.3 seconds, then 14, then time out. I moved it to a local SQLite next to the bot — it became 0.0003 seconds. Lesson: reckon with real infrastructure, not the ideal kind.

    Cutting the unnecessary

    There was a temptation to add a "take turns on one phone" mode. I killed it: it blurs the core — blind reveal only works when each person has their own screen. Cut at the design stage, not finished as dead weight.

    How does building from architecture differ from building from features?

    Feature-first chases the count of capabilities and hopes one sticks. Architecture-first picks one core mechanic and bends everything else to serve it — Closer was built that way.

    Feature-firstArchitecture-first (this build)
    Where the outcome comes fromAdd more features, hope one sticksOne core mechanic, everything serves it
    How scope is decidedBuild whatever is askableCut what doesn't serve the core
    The core logicTangled with the platformSeparated, under tests
    Behavior at the edgesPatched bug by bugHeld by invariants
    The builder's roleCoder of requestsThe one who runs the system

    The product is shipped. This is where the work begins

    "The bot is done" — someone else would say that here and close the project. The live test passed, the partner's answer opened instantly. I exhaled. Too soon.

    A couple of weeks later I went into the live database — just to see how real conversations were going. And I saw it: one answer landed under the wrong question. Not "theoretically possible," but right there in the data. The picture came together fast: a new question of the day arrived, but the person answered the old one still sitting higher in the chat — and the answer went to the wrong place.

    This isn't fixed with a patch. It's fixed with two decisions:

    • The answer is tied to the question the person was actually shown — not to the "pair's current question." Telegram doesn't tell you what exactly is being answered, so I hold that binding myself.
    • A half-finished question is no longer overwritten. If one answered and the other is silent, there's a locked answer living there, waiting for the second person. The scheduler used to be able to overwrite it with a new one. Now it leaves it alone.

    And since I was in there — I dialed in the thing the product lives for, keeping two people engaged:

    • a streak forgives one missed day: one random slip doesn't zero out what's accumulated
    • the bot gently brings a gone-quiet pair back step by step, instead of nagging into the void
    • the moment one answers, the other instantly gets "your partner answered, open it and see"
    • a guess can be skipped — the round reveals without it
    • when a guess matches the answer, that's its own little celebration in the reveal

    It was 62 tests. Now it's 108.

    This is exactly the difference between "shipped and forgot" and "running it." I found the bug — reading the live database by hand. Not a client whose bot "keeps mixing up the questions for some reason."

    And here's the important part. The AI wrote the code, fast. But the AI won't go read the live database on its own and catch that one answer landed under the wrong question. It won't decide what to bind the answer to. It won't ship and verify on live data. Code is cheap now — anyone can do it. The expensive part is something else: getting it to actually work on real people. That's my job.

    Does it actually work?

    Yes. All in production. Deployed in Docker, local database, working every day. 108 automated tests green. Modular code with separated layers. The bug found in the field is closed, retention is dialed in, the changes are shipped and verified on live data.

    What does this demonstrate?

    This is a showcase of how I work. Not "pile on features," but:

    • separate clean logic from the shell, so the substance of the product is under tests
    • hold invariants instead of patching bugs one by one
    • reckon with real infrastructure, not the ideal kind
    • ship to production — and not abandon it there, but run it on live data

    And tie all of it to the goal. The bot exists not for features, but to make two people feel warmer toward each other. Architecture here isn't the point — it works in service of closeness.

    What do I offer?

    I take your idea and bring it to a working AI product in production. And then I run it — because a product nobody runs quietly dies.

    Who it's for

    • Businesses with an audiencecoaches, schools, communities, services. A bot that engages and retains people, not "one more chat they scroll once and forget."
    • Foundersan MVP you're not ashamed to show an investor and a user. With architecture, tests, and in production — not "works on my machine."

    How it works

    • Turnkey build with AIfrom idea to production, fast. I build with the same tool I sell. But I steer it, catch what it missed, and bring it to working: clean logic separated from the shell, covered by tests, deployed.
    • Support on MRRI don't disappear after launch. I watch the live data, find the holes before users do, and dial in what keeps people in the product.

    Why this way. Half of "finished" products die not from bad code, but from being shipped and abandoned. A user finds the bug. Nobody measures retention. The feed goes quiet, and everyone pretends it was meant to be. I close exactly that gap: I ship to production and stay close while the product lives.

    Closer isn't a quick demo cobbled together. It's a working product where you can see my whole approach: from blind reveal as an invariant to a bug caught in the live database by hand.

    Have an idea that's ready for production?

    Or a product you launched and abandoned? Write to me. I'll take a look and tell you straight: what's actually doable, and what's hot air.

    Questions and answers

    Roman Denisov

    About the author

    Roman Denisov

    Fractional AI consultant

    MBA (MIRBIS), 17 years in B2B marketing and sales. I design AI systems and neuro-agents that automate sales, content, and support — and grow revenue.

    More about Roman