← All projects

Product & engineering

BusyNow: helping people decide where to go next

A consumer product for choosing a nearby coffee shop, restaurant, or bar before heading out, with crowd information that tells you how old it is and where it came from.

Thomas Howard · 7 min read

My role
Founder & Software Engineer
Product
Coffee, restaurants, and bars
Project stage
Live consumer product

Picture walking to a café to get some work done and finding every seat taken. Or picking a bar for a night out and finding it empty. Neither is a disaster, but both are the moment BusyNow is built for.

Founder of BusyNow, helping people see what’s busy nearby before heading out. I founded and built it, and I own its product direction, engineering, and operation.

A good place is not always the right place right now

The same venue can be a good fit now and a poor fit an hour later. Someone after coffee may want a quiet table to work. Friends choosing a bar may want a lively room.

Ratings, photos, and menus describe what a place is like. The immediate question needs time-sensitive context: how crowded it seems, how recent that is, whether it is open, and how far it is.

BusyNow puts those into one comparison. A longer walk may be worth it for a fresher crowd report; the closest place may still win when time matters more. The product shows the tradeoff and leaves the choice with the person.

Usefulness is local, and it expires

A big list of venues is only a starting point. The useful unit is a decision in one neighborhood, at one time. BusyNow earns a place in someone’s routine only if that decision gets easier than checking places one by one.

That makes it a local data-density problem. Recent reports around the places someone actually visits matter more than a large total spread thinly across a city. So coverage, freshness, and ease of contribution are one product concern. Where evidence is thin, the app says what is known and what isn’t.

Four steps from an area to a decision

The experience is a few comparable choices, not a directory. The map gives context; the list keeps the deciding facts side by side.

  1. Choose where to look. Use your current location deliberately, or search for an area, address, or landmark, useful if you’d rather not share location or are planning ahead.
  2. Choose the kind of place. Coffee, Restaurants, or Bars. BusyNow shows up to three options first, with up to three more on request, so the first screen never becomes a long list.
  3. Compare the tradeoffs. Each row combines crowd evidence, opening status, and travel. Walking time appears when a route is available; otherwise distance. The map keeps the alternatives in view.
  4. Inspect and act. The detail view explains the evidence and makes Directions the main next step, with hours and links under More. People on site can add a crowd report.

Never show a crowd label without its age and source

“Quiet” is meaningless if you can’t tell who said it or when. A confident label that turns out wrong teaches people to ignore the app. So the product separates what was observed, what is typical, what is estimated, and what is unknown.

These categories live in the shared data contract, not just the UI. Each observation has explicit freshness deadlines: fresh, aging, stale, expired. The app rechecks that state itself, so a cached response never pretends it was just observed.

Explanations must agree with the evidence underneath. A historical pattern cannot become “quiet now” because that makes a nicer recommendation.

Assemble one answer, so every screen tells the same story

If the list, map, and detail view each interpret a venue on their own, they will eventually disagree. A user who sees “quiet” in one place and “unknown” in another stops trusting both.

The web app is React with JavaScript and TypeScript, backed by a Node.js/Express API. Static assets are served through Amazon S3 and CloudFront. The containerized backend runs on AWS ECS behind an Application Load Balancer, described in Terraform.

From a nearby search to a comparable set of places
  1. 01 · BrowserArea + intent

    The React interface sends the chosen area and category or search intent to the API.

  2. 02 · API on ECSDiscovery + assembly

    The backend combines place information, travel, opening status, and available crowd evidence.

  3. 03 · Data boundariesProviders + stored reports

    External services supply place and route information. PostgreSQL holds accepted community reports; Redis supports temporary coordination.

  4. 04 · Shared contractValidated decision snapshot

    The response carries comparable venues, evidence state, freshness, and any degraded data domains back to the interface.

So the API assembles a single decision snapshot. Shared runtime validation checks its structure, timestamps, venue identities, and that comparison statements match the evidence. Every screen reads the same meaning.

Google Places supplies place information and discovery; BusyNow records and evaluates community reports. Keeping them apart stops a provider response, an old pattern, or a missing value from being mistaken for a recent on-site observation.

A double tap must not look like two witnesses

Reporting should be a quick tap. Behind it sit ordinary distributed-system problems: a dropped connection, a late response, a second tap, a report that needs correcting.

Get this wrong and the evidence lies. A retry counted twice makes a place look better supported than it is. A correction that doesn’t replace the original leaves two answers in the current state.

PostgreSQL is the authority for accepted reports. Each submission has a stable identity, so a retry returns the original result instead of counting again. That idempotency is protected by transactions, constraints, and locking when requests overlap. Corrections use the same write path and supersede the earlier observation while keeping the link between them.

The interface never says “saved” without an accepted result. Redis handles temporary coordination, such as rate limits and provider-budget admission; the durable record stays in PostgreSQL. A cache failure cannot quietly redefine the source of crowd truth.

When a provider fails, say what broke and keep what still works

Local discovery runs on networks and paid services. A slow provider makes the app feel stuck, and repeated requests cost money without adding value.

So the service has bounded provider calls, caching, rate limits, budgets, and circuit breakers, each containing a specific failure mode.

The fallback depends on what failed. Missing routes fall back to distance. Missing optional details should not erase a useful comparison. A failed crowd refresh shows an explicit old or unknown state. The response records which part is degraded, and the interface offers a matching recovery path. A labeled partial answer beats an error screen.

Frontend and backend ship separately. ECS deploys use explicit image versions, health checks, service-stability checks, and rollback paths. Logs and metrics cover report outcomes, provider protection, and storage readiness, so a failure can be traced to its boundary.

For a founder-operated product, understandable infrastructure is a requirement. I need to say which dependency is failing, what the user sees, what it costs, and how to restore service. Every component has to justify the work it creates.

One owner for the promise and the system behind it

My work runs from the consumer experience to the runtime: what belongs in the comparison, the interface and API, the evidence contract, report integrity, and the AWS operation. A promise on screen becomes a requirement for the data model, failure behavior, and release process.

I use Codex and supervised agent workflows for implementation, debugging, tests, and review. Changes still need evidence that fits their risk, and releases stay under human control. The checks that matter most target the product’s promise: freshness boundaries, retries, corrections, missing provider data, and recovery.

BusyNow is live, and it still depends on the density of information for a given place and time. Coverage and easier contribution remain open challenges.

What I’d tell a founder