Peeking Behind the Curtain of Secret Max Bid

Whatnot Engineering
Whatnot Engineering
8 min readJan 12, 2023

Pranav Vadrevu | Engineering Intern, Community Team

As a Whatnot intern, I had the opportunity to collaborate on plenty of exciting, impactful, and diverse projects. One standout feature was Secret Max Bid, which enables users to set a maximum bid amount on an item.

With the update, the app auto-bids on a user’s behalf until they become the highest bidder or the max limit is reached. Don’t worry — the max bid amount is kept hidden from other users.

How users can enter max bids

From an engineering perspective, there was a thrilling aspect to Secret Max Bid since the app must auto-bid on behalf of placed max bids while also supporting users’ standard way of bidding, all within our fast-paced live auctions (typically lasting 30 sec — 1 min).

Why we introduced max bids

Our platform offers two auction formats:

  • Regular auctions: When a user adds a new bid within the last 10 seconds of an auction, the auction timer is reset back to 10 seconds.
  • Sudden death auctions: The auction timer is never reset in this format.

Before max bids, there was a problem with the bidding system — with sudden death auctions, users who were willing to pay the most money didn’t necessarily win. Many users commonly engage in bid sniping (adding new bids at the very last second) to try to win sudden-death auctions. As a result, the last person to successfully add a bid becomes the winner, rather than the user who is willing to pay the most for the item. If a user wanted to nearly guarantee to win a sudden death auction, they’d have to enter a high custom bid amount and likely overpay on an item.

We introduced max bids to solve the problem and simultaneously add a nifty little tool to buyers’ bidding toolkits.

With max bids:

  • If you are willing to pay the most amount for an item, you will win sudden-death auctions using max bids without overpaying. Enter a max bid, and the app will auto-bid on your behalf until you become the highest bidder or your max limit is reached.
  • If you know the max amount you’d spend on an item, you can enter and leave us with the headache of bidding in your stead. Give your overworked fingers and twitchy eyes a break from looking out for bid snipers lurking in the bushes.

Now, we’ll look at the development of this feature from a bird’s-eye view.

Planning & design

The planning phase of this feature involved product and design teammates. One of the original design flows proposed how auto-bidding should interact with other users who are bidding against a high secret max bid. The idea behind this flow was to ensure that auto-bidding doesn’t feel robotic. Consider the scenario:

  1. User U1 enters a secret max bid of $5 and is currently winning at $3.
  2. Then, user U2 enters a bid of $4.

The original design showed U2 as the highest bidder at $4 for ~500ms. Then, the app shows U1 is winning at $5.

Here’s how it would’ve looked (pranav_22 is the max bidder in the below case):

Original design behind other bids competing against max bids (pranav_22 is the max bidder)

However, implementing the original design didn’t seem feasible due to some edge cases. In a sudden death auction, if U2 entered the $4 bid during the final 500ms of an auction, the original design shows U2 (inaccurately) as the highest bidder till the end of the auction. Then, users see that the auction ended with the winner being U1. U2 would feel cheated because the highest bidder was updated to U1 seemingly out of nowhere. If they knew U1 became the highest bidder at $5, they would’ve had the chance to enter one more bid.

We were trying to reconcile two seemingly conflicting requirements:

  1. Auto-bidding should feel like bidding in real life — it shouldn’t feel robotic (design requirement)
  2. We should always show an accurate state of the highest bidder so that users can bid based on an accurate state (engineering requirement)

We had to strike a balance and settled on the following experience when a user manually bids against a max bidder.

Users bidding against a max bidder below the max bidder’s limit (smekicks)

In the above GIF, the first pill “smekicks is winning!” reflects the reality that they are the highest bidder, but a second pill “Bid $X” depicts other people adding bids and driving up the price when manual bids compete against max bids.

To educate users who are bidding against max bids, the design additionally incorporated educational pop-ups such as the following:

A one-time educational pop-up shown to manual bidders bidding against max bids

After finalizing the design, it was time to start building.

Implementation

After the design handoff, I had the opportunity to brainstorm the backend design. It was an amazing new learning experience for me as an intern. An implementation plan was then created, outlining the backend and client-server API changes.

With this document, both client and backend work could be parallelized to some extent, but some client work for end-to-end testing was blocked by backend changes.

Backend changes

The majority of backend changes took place in Live-Service, our backend service responsible for handling real-time functionalities like chat and auctions (written in Elixir). Live-Service employs Phoenix WebSocket channels as the primary means of communication with our clients (clients subscribe to these WebSocket channels to send and receive events).

When a user places a new bid on the app, the client sends a Phoenix WebSocket place bid request event to Live-Service via our auction channel, which Live-Service handles.

If the new bid’s amount is greater than the current highest bid’s amount (which should almost always be the case), Live-Service sends an “ok” reply to the client and broadcasts a bid placed event to all the clients subscribed to that auction channel. Clients use the payload of that event to update the state of the auction.

For this feature, clients additionally send the bid type in the place bid request payload. Live-Service uses that information and broadcasts the bid placed event to all the clients subscribed to that auction channel. The payload event has additional information such as the max bid price of the highest bid.

Before this feature, all clients used to receive the same payload for the bid placed broadcast event. But, with secret max bid, we don’t want to expose the price placed by a user to others. So, we intercepted this broadcast event on a channel-subscriber basis to customize the payload. This way, a user only receives max bid price information in the payload if they placed the max bid.

We initially had concerns with intercepting this event since the broadcast is re-encoded per subscriber, instead of one shared encoding for all subscribers. But load testing on intercepting events for even busier events warranted that this was not an area of concern. There was an alternative solution considered which would require clients to maintain extra information separately from reply payloads to the place bid requests, but we decided against it since it is safer and significantly less error-prone for clients to simply rely on the backend-driven payloads.

Frontend changes

Max Bid UI Components

The frontend changes include:

  • Crafting new max bid components and revamping existing ones to support max bids
  • Updating clients’ local state management in accordance with the updated API and payloads sent via WebSocket events
  • Incorporating our Experience Framework on each client for the user education pop-ups to trigger once per user upon specific conditions

Technical considerations

  • Maintaining backward compatibility is paramount. We needed to accommodate users on older versions of the app while maintaining compatibility with both old and new versions of bid data in our data store (DynamoDB).
  • The backend shouldn’t leak a given user’s secret max bid price in any of the WebSocket payloads for any user apart from the one who placed the max bid. Otherwise, malicious actors can intercept the payloads to obtain that information, defeating the whole point of this feature.
  • This feature shouldn’t overcomplicate our bidding logic. We needed to reframe our internal bid model to simplify and unify the bidding logic for both max and standard bids.

Launch, results, and learnings

Once the implementation was complete, we opened it up for internal dogfooding. After squashing bugs and deciding the feature was in a good place, we launched the feature to all users. Within the next 25 days, we saw the following results:

570,000+ max bids were placed by users, accounting for ~4% of total bids placed during that time. Keep in mind that max bids only need to be used sparingly since the app continues to auto-bid till the max limit behind the scenes.

107,000+ auctions were won using max bids (accounting for ~4% of auctions with winners)

  • 35% of these winners saved $1+
  • 5% of these winners saved $14+
  • 1% of these winners saved $50+

Adoption of max bids is steadily increasing each day

Adoption of Max Bids from 14th December 2022–9th January 2023

One learning was that we could have added a one-time in-app user education modal to let buyers know of the existence of this feature. Overall, the user sentiment toward this feature was great and has saved our buyers plenty of money and hassle.

What’s next?

Development work on Secret Max Pre-Bid will begin soon, replacing our existing Pre-Bid feature. After this change, users can enter max bids on an item in advance before it is auctioned. If you’re unable to watch a livestream, this feature allows you to place advance bids on items without paying beyond your max limit.

Acknowledgments

A huge thank you to the people who took this feature from conception to completion: Xixia Wang, Kevin Fu, Ariel Alvarez, Natalie Muenster, Adam Sinda, Avery Segal, and Sophia Feng.

Pranav completed this project as a spring 2022 intern. You can read more about what it’s like to be an intern at Whatnot here, and apply to join us here.

--

--