# bytedance/sonic: a JIT and SIMD JSON codec for Go

> Sonic replaces encoding/json with a runtime-compiled codec that binds Go structs without code generation. It is fast on AMD64 and ARM64, but it carries real platform and toolchain constraints.

**bytedance/sonic** — A blazingly fast JSON serializing & deserializing library

- Repository: https://github.com/bytedance/sonic
- Stars: 9,608 · Forks: 477
- Language: Go
- License: Apache-2.0
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/bytedance-sonic

## What Sonic replaces and who it is for

Sonic is a JSON serializing and deserializing library for Go, published by ByteDance under Apache-2.0. It targets the same job as encoding/json: turning structs into bytes and bytes back into structs. The difference is the implementation strategy. The README describes it as accelerated by JIT (just-in-time compiling) and SIMD (single-instruction-multiple-data), and lists three features: runtime object binding without code generation, complete APIs for JSON value manipulation, and speed.

The audience is narrow but real. If you run a Go service that spends a visible share of CPU inside JSON, and you are on AMD64 or on ARM64 with Go 1.20 or above, Sonic is aimed at you. If your service is I/O bound and JSON never appears in a profile, the added platform constraints buy you nothing. The README's own benchmark table compares Sonic against encoding/json, json-iterator/go and gjson across generic, binding and parallel scenarios, which tells you the intended comparison set is the standard library and the common third-party codecs, not a new abstraction.

## How the JIT and SIMD path actually works

The mechanism is a codec that compiles its own decoders and encoders at runtime rather than generating Go source ahead of time. That is what the README means by "runtime object binding without code generation": there is no go:generate step, no generated file to commit, and no build tag to manage. The binding happens when the program runs.

The repository layout confirms the split. There is a decoder/ directory, an encoder/ directory, an ast/ package for JSON value manipulation, an internal/ tree, a native/ directory, a loader/ module, and a separate go.mod dependency on github.com/bytedance/sonic/loader v0.5.2. The loader is versioned independently, with its own release tag loader/v0.5.2. There is also an unquote/ package and a utf8/ package, which is what you would expect from a codec that wants to avoid calling into the standard library for string and encoding work.

The dependency list is a good signal of how the speed is obtained. go.mod pulls github.com/klauspost/cpuid/v2 for CPU feature detection, github.com/cloudwego/base64x for base64 work, github.com/twitchyliquid64/golang-asm and golang.org/x/arch for assembly generation, and github.com/bytedance/gopkg. This is not a pure-Go rewrite of encoding/json. It inspects the CPU and emits architecture-specific code paths at runtime.

## Getting Sonic into a Go module

The README does not print an install command. What it does give is the module path, fixed by go.mod: module github.com/bytedance/sonic, and a pointer to the API reference with "see go.dev". The repository also ships a compat.go file at the root, which is the path for swapping Sonic in behind the encoding/json API surface rather than calling Sonic types directly. The README does not document the compat layer in prose, so treat the file itself as the reference.

What the README does document is the requirement block, and that is where an install decision actually gets made. Before fetching anything, check the Go version and the CPU. The README lists Go 1.18 through 1.27, with Go 1.24.0 excluded, and it lists Linux, macOS and Windows on AMD64, or ARM64 with Go 1.20 above. The README gives one workaround for the 1.24.0 gap, a build flag:

```
-ldflags="-checklinkname=0"
```

That flag is the only build-time knob the README names. If your toolchain sits at 1.24.0 and you cannot thread that flag through every compile step, stop here rather than fetching the module.

For a first real use, the README points at pkg.go.dev for the API surface and the repository carries examples/example_stream_test.go for a streaming case. The README does not describe the streaming API in prose, so that example file is the documentation. The README also does not document rollback or a migration path away from Sonic beyond the presence of compat.go, so plan the swap as something you validate against your own test suite rather than something the project walks you through.

## The Go 1.24.0 gap and the architecture matrix

The most concrete limitation is stated plainly in the README's Requirement section. Sonic supports Go 1.18 through 1.27, with one exclusion: Go 1.24.0 is not supported, because of an issue tracked at golang/go#71672. The README offers two ways out, either use a higher Go version or pass the build flag -ldflags="-checklinkname=0".

That is a sharper constraint than it first appears. A toolchain patch release is normally something a team takes without a second thought. Here, moving to 1.24.0 breaks the library unless you also change your link flags, and a link flag is a build-system change that has to be threaded through every place you compile. If your organisation pins Go versions centrally and 1.24.0 is in the approved set, Sonic is the wrong choice until that pin moves.

The second constraint is the CPU and OS matrix. The README lists Linux, macOS and Windows, and AMD64 or ARM64, with ARM64 requiring Go 1.20 or above. Nothing in the README mentions other architectures. Because the speed comes from JIT code generation and SIMD, that narrow matrix is not an oversight you can work around with a build tag; it is the design. If you ship to an architecture outside that list, encoding/json remains the portable answer.

## What the benchmark table does and does not tell you

The README's benchmark section opens with a strong claim: for all sizes of JSON and all scenarios of usage, Sonic performs best. The table behind that claim is a single machine, and the README says so: goos darwin, goarch amd64, an Intel Core i9-9880H at 2.30 GHz, Go 1.17.1, on a 13KB payload with 300+ keys and 6 layers.

That is a useful disclosure because it bounds the claim. The numbers are from one CPU, one OS and one Go version, on a medium-sized document. The README does not publish results for small payloads, for very large payloads, or for ARM64. If your JSON is a few hundred bytes per request, the fixed cost of runtime binding may matter more than the throughput figures shown here, and the table will not tell you that either way.

The comparison set is equally specific. Encoder and decoder benchmarks cover Sonic, json-iterator/go, gjson and the standard library, in generic, binding and parallel variants. The GetOne and SetOne benchmarks cover Sonic against gjson, json-iterator/go and sjson. Those are the alternatives the project chose to measure against, and they are the ones to reproduce locally.

## json-iterator/go and gjson as the practical alternatives

json-iterator/go is the closest drop-in alternative. It also presents an encoding/json-compatible surface, but it does not compile decoders at runtime and does not depend on assembly generation. That means it works on a wider set of architectures without a CPU feature check, at the cost of the throughput shown in Sonic's table. If your deployment includes an architecture outside AMD64 and ARM64, json-iterator/go is the safer default and the benchmark difference is the price of portability.

gjson solves a different problem. It is a read-only path for pulling single values out of a JSON document, and Sonic's own GetOne benchmarks place it in that comparison. If all you do is extract a field or two from a large payload, gjson avoids building a Go value at all. Sonic's ast/ package covers similar ground inside the same library, so the choice between them is whether you want one dependency for both full binding and path reads, or a smaller dependency for reads only.

There is also the option the README does not push: staying on encoding/json. It is in the standard library, it has no architecture list, and it has no toolchain exclusions. Sonic is a replacement for it, not a complement, and the compat.go file exists precisely to make that replacement reversible.

## Maintenance, licensing and the cost of the loader split

The repository is not archived, and the last push was on 2026-09-11. Releases are frequent: v1.15.4 on 2026-09-11, v1.15.3 on 2026-08-26, and loader/v0.5.2 on 2026-07-30. The loader is tagged on its own cadence, separate from the main module. That split is worth understanding before you adopt. Your go.mod will pin github.com/bytedance/sonic/loader independently of github.com/bytedance/sonic, so an upgrade can move one without the other. The go.mod also carries a commented-out replace directive for the loader, which is the mechanism the maintainers use to develop against a local copy.

Licensing is Apache-2.0 for the main module, and the repository root contains a LICENSE file, a licenses/ directory and a .licenserc.yaml, which suggests third-party licence tracking is part of the build. Apache-2.0 is permissive and includes an explicit patent grant, but it also carries notice and attribution obligations, so redistributing a binary built with Sonic is not the same as redistributing one built only on the standard library. That is a compliance question for your own legal review, not something this article can settle.

The upgrade cost is the toolchain coupling. Every Go release in the supported window needs to be checked against the library, and the Go 1.24.0 exclusion shows that this check is not theoretical. Budget for a Go version bump to be a two-part change: the toolchain and the codec.

## Conclusion

Adopt Sonic when you are on Linux, macOS or Windows with an AMD64 or ARM64 CPU, you can pin your Go toolchain, and JSON encode/decode time shows up in a profile. Do not adopt it if you need a broad architecture matrix, if you cannot move off Go 1.24.0, or if your JSON volume is small enough that encoding/json is not a measurable cost. Before switching, verify three things on your own build: that your Go version is outside the 1.24.0 gap, that your target GOARCH is AMD64 or ARM64 with Go 1.20 or above on ARM64, and that your test suite passes under the compat layer. The compat.go file at the repository root is the switch that makes that last check cheap.

## FAQ

### Does bytedance/sonic work on ARM64?

Yes, the README lists ARM64 as supported, with the additional requirement that the Go version be 1.20 or above. AMD64 has no such extra requirement.

### Which Go versions does bytedance/sonic support?

The README states Go 1.18 through 1.27, with Go 1.24.0 excluded because of a reported issue. For 1.24.0 the README suggests either a higher Go version or the build flag -ldflags="-checklinkname=0".

### Does bytedance/sonic need code generation?

No. The README lists runtime object binding without code generation as a feature, so binding happens while the program runs rather than through generated Go source.

## Sources

- [bytedance/sonic on GitHub](https://github.com/bytedance/sonic)
- [Issues](https://github.com/bytedance/sonic/issues)
- [License: Apache-2.0](https://github.com/bytedance/sonic/blob/main/LICENSE)
- [README](https://github.com/bytedance/sonic/blob/main/README.md)
- [Releases](https://github.com/bytedance/sonic/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/bytedance-sonic
