Blog

Why Parser Libraries Ship Data Apart From Code

Established user agent parsers keep their matching rules in a data file rather than in source code. The separation is a response to how quickly the rules go stale.

Rules change far faster than logic

The code that applies patterns and extracts groups changes rarely. The patterns themselves change whenever a browser ships a new token or a new product appears.

Bundling both means a release cycle for every rule change, which is far too slow for a dataset that shifts monthly.

Separating them lets rules update on their own schedule, independently of the library's version.

One dataset serves many languages

Because the rules are data, implementations in different languages can consume the same file. The parsing behaviour is then consistent across a polyglot stack.

This matters when a request is parsed at an edge in one language, logged, and reanalysed later in another. Divergent rules produce two answers for one request.

Shared data also concentrates contribution effort. A correction benefits every implementation rather than one.

Contribution is easier against data

Someone who notices a misidentified device can add a rule without understanding the library's internals. The barrier is knowing the string, not the codebase.

Since the people who encounter unusual strings are rarely the library's maintainers, lowering that barrier is what keeps coverage current.

The trade-off is that reviewing contributions requires care, because a badly placed rule can affect matches unrelated to the one being fixed.

Updating data has its own risks

Treating a data update as low risk because no code changed is a mistake. Parsing output is what downstream systems consume, and it can shift substantially.

Reported browser or device distributions may move because classification changed rather than because traffic did. Analyses spanning an update need to account for it.

Recording which dataset version parsed each request makes that traceable. Without it, reclassification is indistinguishable from a genuine change in the population.

Pinning and refreshing deliberately

Pinning the dataset gives reproducible parsing and steadily worsening coverage as new devices appear. Tracking the latest gives coverage and moving output.

Most teams want pinning with a scheduled refresh, so updates land at a known time and can be compared against the previous version.

Treating the dataset as a dependency with its own upgrade process, rather than as an implementation detail of the library, is what makes that possible.