DEV Community

Jim L
Jim L

Posted on

The One Feature I Shipped That Doesn't Get a Second Try

Every feature I've shipped has a quiet second chance built into it. Ship it Monday, notice the copy is confusing, fix it Tuesday, nobody remembers by Friday. Then a customer wrote in asking for a song to play the second he got down on one knee, and I realized I'd never built anything before that only gets to work once.

I run a small AI custom-song generator. Most of what people order is birthday songs, anniversary songs, the occasional "get well soon" for a grandparent. Those are forgiving in a way I didn't appreciate until I started getting proposal requests. A birthday song can be a little corny and still land fine, because the room already knows why everyone's there and the bar is "nice surprise," not "life-altering moment." If the song is slightly off-key on the emotional register, someone laughs, someone claps, the cake still gets eaten.

A proposal song has none of that slack. It plays once, in front of one person, at the exact second someone's future either does or doesn't change. There's no changelog. If the tone is wrong, if it comes off cheesy instead of sincere, or sincere in a generic way instead of sincere about these two specific people, that's the whole experience. You can't push a hotfix to a moment that already happened.

That constraint changed how I had to think about the generation pipeline, not just the marketing copy. A normal feature I can ship at 80% and learn from real usage. With this one, "learn from real usage" means learning from someone's actual proposal, which is not a data source I get to iterate against. So the work moved upstream: instead of generating a song and hoping the tone lands, I had to get much more aggressive about constraining the input before generation even starts. More specific prompts about how the couple actually talks to each other, not generic "romantic" adjectives. A pacing structure that builds instead of front-loading the big swell, because in real proposals the emotional peak is a spoken sentence, not a chorus, and the song has to make room for that instead of competing with it. I also started defaulting to slightly less polished, more conversational lyrics over anything that sounded like a greeting card, because "sincere but plain" survives a live moment much better than "polished but generic."

Here's a composite version of the kind of request that made this obvious (details changed, but the shape is real): a guy wanted a song for a proposal at the restaurant where he'd had his first date, and his first draft of a description was basically "make it romantic, upbeat, something like a wedding song." That's exactly the input that produces the generic result I was worried about. What actually worked was pushing him to describe one specific dumb thing they do together, a joke only the two of them would get, and building the song around that instead of around "romantic" as a mood. The version with the inside joke in the second verse is the one he said made her cry before he even got the ring out. The version without it would have been a perfectly fine song that happened to play during the most important five minutes of his year.

That's the part of building this that I didn't expect going in: most AI-generated content gets forgiven for being a little generic, because there's always a next post, a next draft, a next version. A proposal song doesn't get that forgiveness, and building for zero forgiveness turned out to be a genuinely different design problem than building for "good enough, iterate later."

I laid out the actual constraints I build around now, prompt structure and all, on the proposal-song page, if you want to see what changed after I started treating this as a one-shot problem instead of a normal feature.

Top comments (0)