Tools & Dev Environment3 min read

The highway cameras were never there

Porting a road-camera app to Android, I reread the API spec. The type parameter was not a filter — it selected a dataset, and the app only ever called one of them.

#api#debugging#gotchas#verification#android
Concept diagram: a type parameter that selects entirely different datasets rather than filtering one
A diagram summarising the post.

While porting a road-camera app to Android, I reread the public traffic API spec that the iOS side had been using.

That is when I learned the live iOS build had never shown a single highway camera. The store listing even used the word "highway."

How many parameters in the APIs you call have meanings you inferred from their names?

The name looked like a filter

This API's type parameter looks like something that narrows results. It actually selects which dataset you read.

  • type=its → a dataset containing national-road cameras only
  • type=ex → a completely different dataset containing highway cameras only

I called both with the same bounding box.

type=its -> 42 results (national roads)
type=ex  -> 296 results (highways)

Seven times the data. The app had only ever called its.

A defect no report can surface

An app sold as "cameras along your route" had shipped with most of what it sold invisible.

And the screen looks fine. A few national-road cameras appear, so "no cameras at all" is a report nobody can file. Users conclude this road simply has no cameras.

An empty screen gets reported. An underfilled one does not.

Here is where it splits

I merged both datasets. But labelling each camera as highway or national road on screen requires knowing its road type.

Cameras with "highway" in the name could just be classified as highway cameras.

Would you reclassify by name, or carry the source through?

Names are wrong. Real highway cameras exist whose names only mention a ring road or a junction, with no word for "highway" in them. Name-based classification files those as national roads.

The classification has to come from which dataset returned it, not from the label. Names are for display; provenance decides the type.

Two traps when merging

  • Deduplicate. The same camera — same coordinates, same name — can exist in both datasets.
  • One failure must not collapse the other. Raise an error only when both calls fail. Folding a single failure into "zero cameras" leaves an empty map with no explanation.

There was one more, unrelated trap. With lenient JSON parsing enabled, the XML error body the API returns on a bad key name gets read as an unquoted string and parsed successfully. The result is an empty list, and the screen says "no cameras on this route."

A real error disguises itself as a legitimate empty state. Strict parsing now makes that case fail as the error it is.

Self-check

  • Pick one API parameter and call it twice with different values under identical conditions. A 7x difference in result count means it was never a filter.
  • Do you distinguish "the screen does not look empty" from "everything came back"?
  • Is your parser swallowing error responses as success? Lenient parsing does this silently.

The honest part

I know exactly how long this defect existed: since the beginning. The app never called the highway dataset once. Nobody reported it, and I did not know until I reread the spec.

Without the port I still would not know. The uncomfortable part of this story is that the reason I reread it was other-platform work, not a bug.

Pick one filter-looking parameter in an API you call and try a different value, once.

Related