From Idea to Shipped Feature in a Flash: How the Sausage Gets Made With an AI Assistant

A coder working with an AI assistant

I’ve written at length about working with AI (ChatGPT-4o to be precise), but I haven’t shown the process in action yet. This will give you a sense of how quickly you can go from idea for a feature to shipped code. But first, I want to rewind a week.

Last Thursday night was the first eviction episode of the season on Big Brother 26. Eviction episodes are game-changing and live, so always garner a lot of interest from viewers. My app’s usage spikes the most during eviction episodes and the aftermath, and last Thursday was no exception. Because it was the first one of the season and under this new app, there was a detail I had forgot. Spoilers.

I like probably many others watch shows delayed on a DVR, in my case YouTube TV, including this one. Sometimes I start late and skip commercials to catch up, or sometimes I’m just late to the party altogether. Such was the case here, and I quickly checked the app to make sure it wasn’t having any issues on such a heavy-usage night, and immediately saw the spoilers for who got evicted in the episode. Dang. This issue is magnified for West Coast viewers, because even though Big Brother is filmed in Los Angeles, they get the show delayed by three hours because it airs live on East Coast time.

So after I watched the show, I told ChatGPT my problem and how I wanted to fix it, and by Saturday we had two new features live on the App Store: Hide Spoilers, and Hide Spoilers During Eviction. These features automatically blur the Hamster AI summary, and if During Eviction is on, it happens automatically from 8pm EST to 9PM PST. For anyone using the app, I recommend turning that feature on if you worry about spoilers. You can always reveal them by tapping the show/hide icon above.

ChatGPT suggested code to handle this, we iterated back and forth to dial it in, and by Friday night I’d submitted it. A nice, convenient feature to keep you from accidentally seeing something you wanted to wait for.

That brings us to this morning. Last night was another live eviction, and the episode featured one player a bit more than usual, T’kor. Her name gives the app problems because of the apostrophe, and the live feeders' various ways to write it. The end result was there were zero updates for her in the newly released Player Profile feature, because the search couldn’t find results using the proper apostrophe, and CloudKit changes it to a dash.

So while getting ready for work this morning, I paused a moment to run this problem by ChatGPT. Here’s how features like this go from idea to shipped in almost no time flat:


Me:

Ok that update is released to the App Store now.

I noticed that T’kor isn’t showing any latest updates on her profile page. That name has given me problems because of the apostrophe.

What do you think about adding search terms as a string array to the Houseguest record type, and using them for the “Latest updates for (houseguest)” feature on player profiles.

We could use that to improve the search and highlight feature, as well as possibly for another feature I want to add, which is to summarize this player’s game so far from the available updates.

This way I can add multiple variations on the player’s name, and account for nicknames, shorthand, and misspellings by the live feeders. And also fix searching for T’kor’s unusual name.


ChatGPT-4o:

Adding a search terms array to the Houseguest record type is a great idea to improve the functionality and accuracy of your features, especially for handling names with special characters like T’kor. Here’s how this approach can enhance your app’s features:

Benefits of Using a Search Terms Array

  1. Improved Search Accuracy:

    • Variation Handling: You can account for variations in name spelling, nicknames, and common misspellings, ensuring that all relevant updates are captured.
    • Special Characters: By specifying multiple variations, you can avoid issues caused by special characters or unusual name formats.
  2. Enhanced Highlighting:

    • Consistent Highlighting: The search terms array allows you to highlight the correct variations in the updates, making it easier for users to recognize references to a houseguest.
  3. Customizable Summarization:

    • Targeted Summaries: By using search terms, you can more accurately summarize a player’s game by capturing all mentions, even when they are referenced differently.
  4. Future-Proofing:

    • Scalability: As your app grows, this system will allow you to easily adapt to new variations or nicknames, providing flexibility for future updates and expansions.

Implementation Steps

  1. Update the Houseguest Record Type:

    • Add a new property searchTerms as an array of strings to your Houseguest model. This will store various search terms associated with each player.
    struct Houseguest {
        var id: CKRecord.ID
        var name: String
        var profileImageURL: URL?
        var age: Int?
        var hometown: String?
        var currentCity: String?
        var occupation: String?
        var season: Int?
        var searchTerms: [String]
    }
    
  2. Update Your CloudKit Fetching Logic:

    • Modify the logic to fetch and save the searchTerms from CloudKit. Make sure your CloudKit schema includes this field.
    func fetchHouseguests() {
        let query = CKQuery(recordType: "Houseguest", predicate: NSPredicate(value: true))
        publicDB.fetch(withQuery: query) { result in
            switch result {
            case .success(let records):
                DispatchQueue.main.async {
                    self.houseguests = records.map { record in
                        Houseguest(
                            id: record.recordID,
                            name: record["name"] as? String ?? "",
                            profileImageURL: (record["profile_image"] as? CKAsset)?.fileURL,
                            age: record["age"] as? Int,
                            hometown: record["hometown"] as? String,
                            currentCity: record["current_city"] as? String,
                            occupation: record["occupation"] as? String,
                            season: record["season"] as? Int,
                            searchTerms: record["searchTerms"] as? [String] ?? [record["name"] as? String ?? ""]
                        )
                    }
                }
            case .failure(let error):
                print("Error fetching records: \(error)")
            }
        }
    }
    
  3. Modify the Feed Filtering Logic:

    • Use the searchTerms array to filter feed updates.
    private func filterUpdates(for houseguest: Houseguest) -> [FeedItem] {
        return feed.items.filter { item in
            houseguest.searchTerms.contains { term in
                item.title.localizedCaseInsensitiveContains(term) ||
                item.body.localizedCaseInsensitiveContains(term)
            }
        }
    }
    
  4. Update Highlighting Logic:

    • Ensure your text highlighting logic takes the searchTerms into account to highlight all variations.
    private func highlightedText(for text: String, with searchTerms: [String]) -> AttributedString {
        var attributedString = AttributedString(text)
        for term in searchTerms {
            if let range = attributedString.range(of: term, options: .caseInsensitive) {
                attributedString[range].foregroundColor = .yellow
            }
        }
        return attributedString
    }
    
  5. Summarization Feature:

    • Use the search terms when generating AI summaries to ensure all relevant mentions are captured.
    private func generateSummary(for houseguest: Houseguest) {
        let combinedText = filterUpdates(for: houseguest)
            .map { "\($0.title) \($0.body)" }
            .joined(separator: " ")
    
        aiSummarizer.summarize(
            text: combinedText,
            tone: appSettings.selectedTone,
            emotion: appSettings.selectedEmotion,
            contentFocus: appSettings.selectedContentFocus,
            conciseness: appSettings.selectedConciseness
        ) { result in
            switch result {
            case .success(let summary):
                self.summary = summary
            case .failure(let error):
                print("Failed to generate summary: \(error)")
            }
        }
    }
    

Considerations

  • CloudKit Schema: Ensure your CloudKit record schema is updated to include the searchTerms field and that you have migrated existing records as needed.
  • UI Updates: Update any relevant UI components to reflect these changes, particularly where houseguest information is displayed or interacted with.
  • Testing: Thoroughly test the implementation to ensure it handles various edge cases, especially for names with special characters or multiple variations.

By implementing these changes, you will improve the robustness and user experience of your app, allowing for more accurate and comprehensive search functionality and content presentation.


Now right now, I don’t know that this code will actually work yet. But it’s clearly on the right track, and if it doesn’t work, I’m positive a few minutes back and forth will get it across the finish line. We’ll find out after work today, when I start adding these features to the app.

So that in a nutshell is a lot of how this sausage has been made – I tell ChatGPT what I want, it hands me some code and discussion back, I either plug it in as-is or I ask for changes, and we go back and forth honing this new feature. It could take ten minutes or it could take a few hours, but to me the big win is it didn’t take ten days. Or even two.

This could easily be looked at as a case of “veteran coder is too lazy to spend the time writing the code”. To that I say, hell yes, you got it exactly right – except for the “lazy” bit. I’d swap that out for “smart” instead. Why should I spend time writing all the code when I could instead describe what I want, and evaluate and adjust minor bits instead?

The fact is, I’m still using my software engineering skills, but I’m also able to seriously exercise my creativity, and react in nearly real-time to issues that arise and address them swiftly for the app’s users' benefit. After all, the primary stakeholders of mobile apps are always the users themselves. If that’s not the rule, it should be.

I think working like this is on the cutting edge of AI-augmented software engineering, and that is far more exciting to me today, than sitting down and writing a ton of code.

Update from about an hour after work:

The code from ChatGPT was in the ballpark, but needed a little work, because I didn’t want to have to set search terms for every houseguest. So we generate some default terms for all houseguests to combine with what comes from CloudKit, and voila. Coming soon to an app near you!