Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Write down the JSON library policy and fix the drift it already caused

Open
#884 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
52/100
Issue type
Refactor
Clarity
Clearly specified
Activity status
Quiet
Tech stack
go
Domain
backend, databases

Research direction

Read the JSON guidance in internal/server/json_serializer.go and internal/server/messages_native.go, then inspect the helper pairs in internal/users/store_sql.go, internal/virtualmodels/store.go, and internal/storage/sqlutil/sqlutil.go. Update the guidance and rationale, and fold the store-local helpers into the shared SQL utilities as appropriate. Done when the policy is documented, the inaccurate comment is corrected, and tests for the affected stores and utilities pass.

Written by the indexing model from the issue text.

Description

good first issue

Noticed while working on #881. Not a bug — no user-visible impact — but the JSON story has three libraries, no written rule, and drift has already started.

What we use today

Library Prod files Job
github.com/goccy/go-json 146 Default marshal/unmarshal, including echo's response serializer (internal/server/json_serializer.go)
encoding/json 20 Sites that want exact stdlib semantics or the token-streaming decoder
github.com/tidwall/gjson 8 Read-only path queries over raw bytes, no unmarshal
gopkg.in/yaml.v3 2 config.yaml

All four earn their place — they do different jobs, and none is redundant:

  • goccy is load-bearing and guarded by the pre-commit hot-path alloc check.
  • gjson answers path queries on raw bytes without unmarshalling (selector peek, session detection, cache keys, upstream error-body extraction). goccy has no equivalent API.
  • stdlib cannot be dropped anyway: echo's binder uses DefaultJSONSerializer.Deserialize, and config/merge.go relies on json.NewDecoder to reject trailing data.

The problem is not the count. It is that the rule for choosing between them is written nowhere — it lives in scattered comments (internal/server/json_serializer.go, internal/server/messages_native.go) — so sites pick a library by whatever the neighbouring file happened to do.

The drift so far

1. Three copies of one helper, split across two JSON libraries.

  • internal/users/store_sql.go:102 / :113 — encodeAllowedModels / decodeAllowedModels, using encoding/json
  • internal/virtualmodels/store.go:35 / :60 — encodeUserPaths / decodeUserPaths, character-for-character the same idiom, using goccy
  • internal/storage/sqlutil/sqlutil.go:104 / :119 — NullableJSONStrings / StringsFromJSON, a third copy (goccy, nullable variant)

The first two back the same column shape (NOT NULL DEFAULT '[]'), so they are interchangeable. sqlutil is already the home for this; the store-local copies should fold into it.

2. An inaccurate rationale. internal/server/messages_native.go:5 says stdlib is used because rewriteMessagesModel "needs the decoder's InputOffset". goccy exposes both Token() and InputOffset() (decode.go:244, :255), so the stated reason does not hold. Probably not worth switching — goccy's InputOffset delegates to TotalOffset(), whose streaming semantics may differ, and there is nothing to gain — but the comment should state the real reason.

3. Deliberate mixing with no explanation. internal/auditlog/body.go imports both, goccy as json and stdlib aliased encjson purely for Valid(). Presumably intentional; nothing says why validation specifically wants stdlib.

Suggested work

  1. Write the rule down once — a short ADR, or a doc comment somewhere central: goccy for marshal/unmarshal, gjson for read-only extraction from raw bytes, stdlib only where its exact semantics are required, and each such site says which semantics and why.
  2. Fold users/store_sql.go's encode/decode pair onto the shared sqlutil helpers (and consider the virtualmodels/store.go pair too).
  3. Correct the messages_native.go comment.

No dependency should be removed; the // indirect JSON/YAML entries (google/jsonschema-go, go-openapi/*, sigs.k8s.io/yaml, go.yaml.in/yaml) are all transitive from the MCP SDK, swaggo (behind //go:build swagger) and testify.

Dominant language
Go
Stars
1.2k
Forks
111
Avg merge
14h 43m
Merged PRs (30d)
142

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from ENTERPILOT/GoModel

All issues in ENTERPILOT/GoModel

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.