Decode Base64 in Go β€” encoding/base64 Guide + Examples

·Systems Engineer·Reviewed byHana NovÑkovÑ·Published

Use the free online Base64 Decode Online directly in your browser β€” no install required.

Try Base64 Decode Online Online β†’

Base64 decoding in Go comes up constantly β€” JWT inspection, binary file attachments, API payloads from cloud services. Go's standard encoding/base64package handles all of it, but picking the wrong encoding variant (standard vs URL-safe, padded vs unpadded) is the single most common source of β€œillegal base64 data” errors. This guide covers StdEncoding, URLEncoding, RawURLEncoding, streaming decoding with base64.NewDecoder, JWT payload inspection, and four mistakes that trip up almost everyone the first time. For one-off decoding in the browser, ToolDeck's Base64 decoder gets the job done instantly without writing a line of code.

  • βœ“encoding/base64 is part of the Go standard library β€” no go get required
  • βœ“Use RawURLEncoding for JWT tokens and most modern APIs (no padding, URL-safe alphabet)
  • βœ“StdEncoding uses + and / with = padding; URLEncoding swaps to - and _ but keeps padding
  • βœ“base64.NewDecoder wraps any io.Reader for streaming decode without loading into memory
  • βœ“Always check the returned error β€” invalid padding and wrong alphabet produce illegal base64 data

What is Base64 Decoding?

Base64 encoding represents binary data as ASCII text using 64 printable characters (A–Z, a–z, 0–9, plus two extras). Decoding reverses this β€” it converts that ASCII representation back into the original bytes. Every 4 Base64 characters decode to exactly 3 bytes. The scheme exists because many transport layers (email, HTTP headers, JSON fields) are designed for text, not raw binary. Below is what the round-trip looks like:

Go 1.21+
package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	// Raw bytes β†’ Base64 encoded β†’ decoded back to raw bytes
	original := []byte("service_token:xK9mP2qR")
	// Encoded: "c2VydmljZV90b2tlbjp4SzltUDJxUg=="

	encoded := base64.StdEncoding.EncodeToString(original)
	decoded, _ := base64.StdEncoding.DecodeString(encoded)
	fmt.Println(string(decoded) == string(original)) // true
}

Decode Base64 in Go with encoding/base64

The encoding/base64 package ships with Go β€” no external dependencies. It exposes four pre-defined encoding variants as package-level variables. The most commonly used function for string input is DecodeString, which returns a byte slice and an error.

Go 1.21+
package main

import (
	"encoding/base64"
	"fmt"
	"log"
)

func main() {
	// Standard Base64 β€” the alphabet uses + and / with = padding
	encoded := "eyJob3N0IjoiZGItcHJvZC51cy1lYXN0LTEiLCJwb3J0Ijo1NDMyfQ=="
	decoded, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		log.Fatalf("decode error: %v", err)
	}
	fmt.Println(string(decoded))
	// {"host":"db-prod.us-east-1","port":5432}
}

The Decode method works on byte slices rather than strings, and writes the result into a pre-allocated destination buffer. You need to size the buffer correctly β€” use base64.StdEncoding.DecodedLen(len(src)) to get the maximum size (it may be a few bytes larger than the actual decoded length due to padding).

Go 1.21+
package main

import (
	"encoding/base64"
	"fmt"
	"log"
)

func main() {
	src := []byte("eyJob3N0IjoiZGItcHJvZCIsInBvcnQiOjU0MzJ9")
	dst := make([]byte, base64.RawStdEncoding.DecodedLen(len(src)))

	n, err := base64.RawStdEncoding.Decode(dst, src)
	if err != nil {
		log.Fatalf("decode: %v", err)
	}
	fmt.Println(string(dst[:n]))
	// {"host":"db-prod","port":5432}
}
Note:DecodedLen returns an upper bound, not the exact length. Always use the n return value from Decode to slice the result correctly: dst[:n].

StdEncoding vs URLEncoding β€” Choosing the Right Variant

This is where most confusion lives. Go's encoding/base64 exposes four encoding objects, and picking the wrong one is guaranteed to give you an error. The difference comes down to two things: the alphabet and padding.

Go 1.21+
package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	// JWT header payload β€” URL-safe, no padding
	jwtHeader := "eyJhbGciOiJSUzI1NiIsImtpZCI6IjIwMjMtMDkifQ"

	// Wrong: StdEncoding fails on URL-safe input without padding
	_, err1 := base64.StdEncoding.DecodeString(jwtHeader)
	fmt.Println("StdEncoding error:", err1)
	// StdEncoding error: illegal base64 data at input byte 43

	// Correct: RawURLEncoding β€” no padding, URL-safe alphabet
	decoded, err2 := base64.RawURLEncoding.DecodeString(jwtHeader)
	fmt.Println("RawURLEncoding ok:", err2, "β†’", string(decoded))
	// RawURLEncoding ok: <nil> β†’ {"alg":"RS256","kid":"2023-09"}
}

The four variants in plain terms:

Function / Method
Encoding
Padding Required
Returns
base64.StdEncoding.DecodeString(s)
Standard (+, /)
Yes (=)
([]byte, error)
base64.URLEncoding.DecodeString(s)
URL-safe (-, _)
Yes (=)
([]byte, error)
base64.RawStdEncoding.DecodeString(s)
Standard (+, /)
No
([]byte, error)
base64.RawURLEncoding.DecodeString(s)
URL-safe (-, _)
No
([]byte, error)
base64.StdEncoding.Decode(dst, src)
Standard (+, /)
Yes (=)
(n int, error)
base64.NewDecoder(enc, r)
Any encoding
Yes
io.Reader

My rule of thumb: if it came from a JWT, OAuth flow, or a cloud provider SDK, reach for RawURLEncoding first. If it came from email attachments or old-school web forms, try StdEncoding. The error message always tells you the exact byte position where decoding failed.

Decode Base64 from a File and API Response

Reading a Base64-encoded file

Binary files (images, PDFs, certificates) are sometimes stored Base64-encoded on disk. Read the file, strip any trailing whitespace, then decode:

Go 1.21+
package main

import (
	"encoding/base64"
	"fmt"
	"log"
	"os"
	"strings"
)

func main() {
	raw, err := os.ReadFile("certificate.pem.b64")
	if err != nil {
		log.Fatalf("read file: %v", err)
	}

	// Strip newlines β€” Base64 files often have line breaks every 76 chars
	cleaned := strings.ReplaceAll(strings.TrimSpace(string(raw)), "\n", "")

	decoded, err := base64.StdEncoding.DecodeString(cleaned)
	if err != nil {
		log.Fatalf("decode: %v", err)
	}

	if err := os.WriteFile("certificate.pem", decoded, 0600); err != nil {
		log.Fatalf("write: %v", err)
	}
	fmt.Printf("decoded %d bytes β†’ certificate.pem\n", len(decoded))
}

Decoding a Base64 field from an API JSON response

Cloud APIs frequently return binary data (encryption keys, signed blobs, thumbnails) as Base64 strings inside JSON. Unmarshal the JSON first, then decode the target field:

Go 1.21+
package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

type SecretResponse struct {
	Name    string `json:"name"`
	Payload string `json:"payload"` // Base64-encoded secret value
	Version int    `json:"version"`
}

func fetchAndDecodeSecret(secretURL string) ([]byte, error) {
	resp, err := http.Get(secretURL)
	if err != nil {
		return nil, fmt.Errorf("http get: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
	}

	var secret SecretResponse
	if err := json.NewDecoder(resp.Body).Decode(&secret); err != nil {
		return nil, fmt.Errorf("decode json: %w", err)
	}

	value, err := base64.StdEncoding.DecodeString(secret.Payload)
	if err != nil {
		return nil, fmt.Errorf("decode base64: %w", err)
	}
	return value, nil
}

func main() {
	value, err := fetchAndDecodeSecret("https://api.example.com/secrets/db-password")
	if err != nil {
		log.Fatalf("fetch secret: %v", err)
	}
	fmt.Printf("secret value: %s\n", value)
}
Note:Wrap errors with fmt.Errorf("decode base64: %w", err) rather than losing context. The original error message from encoding/base64 includes the byte position of the failure, which is useful during debugging.

Streaming Large Base64-encoded Files

Loading a 500 MB Base64-encoded file into memory with os.ReadFile then calling DecodeString uses roughly 750 MB of RAM (the encoded string plus the decoded bytes). base64.NewDecoder wraps any io.Reader and decodes in chunks, keeping memory usage near-constant. Combine it with io.Copy for a clean streaming pipeline:

Go 1.21+
package main

import (
	"encoding/base64"
	"fmt"
	"io"
	"log"
	"os"
)

func streamDecodeFile(srcPath, dstPath string) error {
	src, err := os.Open(srcPath)
	if err != nil {
		return fmt.Errorf("open source: %w", err)
	}
	defer src.Close()

	dst, err := os.Create(dstPath)
	if err != nil {
		return fmt.Errorf("create dest: %w", err)
	}
	defer dst.Close()

	decoder := base64.NewDecoder(base64.StdEncoding, src)
	written, err := io.Copy(dst, decoder)
	if err != nil {
		return fmt.Errorf("stream decode: %w", err)
	}
	fmt.Printf("written %d bytes to %s\n", written, dstPath)
	return nil
}

func main() {
	if err := streamDecodeFile("backup.tar.b64", "backup.tar"); err != nil {
		log.Fatal(err)
	}
}
Warning:base64.NewDecoder expects clean, uninterrupted Base64 data. If the source file has line breaks (common in PEM and MIME-encoded files), wrap the source reader with a line-stripping reader or pre-process the file to remove newlines before streaming.

Base64 Decoding from the Command Line

Every macOS and Linux system ships with base64; on Windows, PowerShell has a built-in equivalent. For quick inspection of API payloads, these are faster than writing a Go script.

bash
# Decode a Base64 string (Linux / macOS)
echo "eyJob3N0IjoiZGItcHJvZCIsInBvcnQiOjU0MzJ9" | base64 --decode
# {"host":"db-prod","port":5432}

# Decode and pretty-print with jq (pipe the JSON output)
echo "eyJob3N0IjoiZGItcHJvZCIsInBvcnQiOjU0MzJ9" | base64 --decode | jq .
# {
#   "host": "db-prod",
#   "port": 5432
# }

# Decode a Base64-encoded file to binary
base64 --decode < encrypted_payload.b64 > encrypted_payload.bin

# macOS uses -D flag instead of --decode
echo "c2VjcmV0LXRva2Vu" | base64 -D

For inspecting JWT tokens without any tools installed, paste the token into ToolDeck's Base64 decoder β€” split on the dots and decode each part.

High-Performance Alternative: encoding/base64 is Already Fast

Unlike Python, where orjson vs jsonis a meaningful performance conversation, Go's encoding/base64is already assembly-optimized and genuinely fast for most workloads. That said, if you're processing millions of records in a tight loop, filippo.io/base64 provides SIMD-accelerated decoding with a drop-in API.

bash
go get filippo.io/base64
Go 1.21+
package main

import (
	"fmt"
	"log"

	"filippo.io/base64"
)

func main() {
	// Drop-in replacement β€” same API as encoding/base64
	encoded := "eyJob3N0IjoiY2FjaGUtcHJvZCIsInBvcnQiOjYzNzl9"
	decoded, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		log.Fatalf("decode: %v", err)
	}
	fmt.Println(string(decoded))
	// {"host":"cache-prod","port":6379}
}

The performance gain is most visible on amd64 with AVX2 support β€” benchmarks show 2–4x throughput improvement on large inputs. For everyday API response decoding (a few hundred bytes at a time), stick with the standard library.

Decoding Base64 JWT Payload in Go

JWT inspection comes up in almost every backend service. In my experience, most debugging sessions boil down to β€œwhat's actually in this token?” β€” and you can answer that without pulling in a full JWT library. The token has three Base64url-encoded segments separated by dots. The middle segment is the payload you actually care about:

Go 1.21+
package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"strings"
)

type JWTPayload struct {
	Subject  string   `json:"sub"`
	Issuer   string   `json:"iss"`
	Expiry   int64    `json:"exp"`
	Roles    []string `json:"roles"`
}

func decodeJWTPayload(token string) (*JWTPayload, error) {
	parts := strings.Split(token, ".")
	if len(parts) != 3 {
		return nil, fmt.Errorf("invalid JWT: expected 3 segments, got %d", len(parts))
	}

	// JWT uses RawURLEncoding β€” URL-safe alphabet, no = padding
	raw, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return nil, fmt.Errorf("decode payload: %w", err)
	}

	var payload JWTPayload
	if err := json.Unmarshal(raw, &payload); err != nil {
		return nil, fmt.Errorf("unmarshal payload: %w", err)
	}
	return &payload, nil
}

func main() {
	token := "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c3ItNDQyIiwiaXNzIjoiYXV0aC5leGFtcGxlLmNvbSIsImV4cCI6MTc0MTk1NjgwMCwicm9sZXMiOlsiYWRtaW4iLCJhdWRpdG9yIl19.SIGNATURE"

	payload, err := decodeJWTPayload(token)
	if err != nil {
		log.Fatalf("jwt: %v", err)
	}
	fmt.Printf("Subject: %s\n", payload.Subject)
	fmt.Printf("Issuer:  %s\n", payload.Issuer)
	fmt.Printf("Roles:   %v\n", payload.Roles)
	// Subject: usr-442
	// Issuer:  auth.example.com
	// Roles:   [admin auditor]
}

Common Mistakes

I've encountered all four of these in real code reviews β€” the first two in particular show up almost every time someone integrates a new auth provider.

❌ Using StdEncoding on URL-safe input

Problem: JWT tokens and OAuth tokens use the URL-safe Base64 alphabet (- and _). Passing them to StdEncoding.DecodeString fails with 'illegal base64 data'.

Fix: Check your input source: tokens from auth systems use RawURLEncoding; binary attachments use StdEncoding.

Before Β· Go
After Β· Go
// JWT header β€” URL-safe, no padding
token := "eyJhbGciOiJSUzI1NiJ9"
decoded, err := base64.StdEncoding.DecodeString(token)
// err: illegal base64 data at input byte 19
// JWT header β€” correct encoding
token := "eyJhbGciOiJSUzI1NiJ9"
decoded, err := base64.RawURLEncoding.DecodeString(token)
// decoded: {"alg":"RS256"}
// err: nil
❌ Ignoring the n return value from Decode

Problem: Decode writes into a pre-allocated buffer and returns the number of bytes actually written. DecodedLen returns an upper bound, so the tail of the buffer may contain garbage bytes.

Fix: Always slice the result with dst[:n] β€” not dst[:len(dst)].

Before Β· Go
After Β· Go
dst := make([]byte, base64.StdEncoding.DecodedLen(len(src)))
base64.StdEncoding.Decode(dst, src)
fmt.Println(string(dst)) // may include trailing zero bytes
dst := make([]byte, base64.StdEncoding.DecodedLen(len(src)))
n, err := base64.StdEncoding.Decode(dst, src)
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(dst[:n])) // correct β€” only the decoded bytes
❌ Not stripping whitespace before decoding

Problem: Base64 strings copied from terminals, emails, or config files often have trailing newlines or spaces. Passing them directly to DecodeString fails at the whitespace character.

Fix: Call strings.TrimSpace (and strings.ReplaceAll for embedded newlines) before decoding.

Before Β· Go
After Β· Go
// Value read from a config file with a trailing newline
encoded := "c2VydmljZV9rZXk6eEtNcDI=\n"
decoded, err := base64.StdEncoding.DecodeString(encoded)
// err: illegal base64 data at input byte 24
encoded := "c2VydmljZV9rZXk6eEtNcDI=\n"
cleaned := strings.TrimSpace(encoded)
decoded, err := base64.StdEncoding.DecodeString(cleaned)
// decoded: "service_key:xKMp2"
// err: nil
❌ Storing decoded bytes as string incorrectly

Problem: Calling string(decoded) on binary data (images, compressed payloads) produces invalid UTF-8 strings. Go strings can hold arbitrary bytes, but some operations will mangle the content.

Fix: Keep binary data as []byte throughout your pipeline. Only call string(decoded) when the decoded content is guaranteed to be text.

Before Β· Go
After Β· Go
decoded, _ := base64.StdEncoding.DecodeString(pngBase64)
// Treating binary PNG as a string loses data
imageStr := string(decoded)
os.WriteFile("image.png", []byte(imageStr), 0644) // may corrupt
decoded, err := base64.StdEncoding.DecodeString(pngBase64)
if err != nil {
    log.Fatal(err)
}
// Write bytes directly β€” no string conversion
os.WriteFile("image.png", decoded, 0644)

Method Comparison

All variants ship in the standard library β€” no external dependencies for any of these.

Method
Input Type
Encoding Variants
Streaming
Custom Alphabet
Custom Padding
Requires Install
StdEncoding.DecodeString
string
Standard
βœ—
βœ—
βœ—
No (stdlib)
URLEncoding.DecodeString
string
URL-safe
βœ—
βœ—
βœ—
No (stdlib)
RawStdEncoding.DecodeString
string
Standard (no pad)
βœ—
βœ—
βœ—
No (stdlib)
RawURLEncoding.DecodeString
string
URL-safe (no pad)
βœ—
βœ—
βœ—
No (stdlib)
StdEncoding.Decode
[]byte
Standard
βœ—
βœ—
βœ—
No (stdlib)
base64.NewDecoder
io.Reader
Any
βœ“
βœ—
βœ—
No (stdlib)
encoding/base64 + NewEncoding
string
Custom alphabet
βœ—
βœ“
βœ“
No (stdlib)

For JWT tokens and OAuth flows: RawURLEncoding. For email attachments and MIME data: StdEncoding. For large binary files from disk or network: wrap a reader in base64.NewDecoder β€” it keeps memory use flat regardless of file size. Need a custom alphabet? base64.NewEncoding(alphabet) builds a new encoding object for exotic use cases.

For quick one-off checks during development, the online Base64 decoder is faster than spinning up a Go program.

Frequently Asked Questions

How do I decode a Base64 string in Go?

Import encoding/base64 and call base64.StdEncoding.DecodeString(s). It returns ([]byte, error) β€” always check the error. If the string uses URL-safe characters (- and _ instead of + and /), use base64.URLEncoding.DecodeString instead. For JWT tokens and most modern APIs, RawURLEncoding (no padding) is the right choice.

Go 1.21+
package main

import (
	"encoding/base64"
	"fmt"
	"log"
)

func main() {
	encoded := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
	decoded, err := base64.RawURLEncoding.DecodeString(encoded)
	if err != nil {
		log.Fatalf("decode: %v", err)
	}
	fmt.Println(string(decoded))
	// {"alg":"HS256","typ":"JWT"}
}

What is the difference between StdEncoding and URLEncoding in Go?

StdEncoding uses the standard Base64 alphabet with + and / characters and = padding β€” defined in RFC 4648 Β§4. URLEncoding substitutes + with - and / with _ making the output safe in URLs and HTTP headers without percent-encoding β€” defined in RFC 4648 Β§5. Use URLEncoding for JWT tokens, OAuth tokens, and any data embedded in query strings.

Go 1.21+
package main

import (
	"encoding/base64"
	"fmt"
)

func main() {
	// Standard: may contain + / and = characters
	std := base64.StdEncoding.EncodeToString([]byte("hello/world"))
	fmt.Println(std) // "aGVsbG8vd29ybGQ="

	// URL-safe: replaces + with - and / with _
	url := base64.URLEncoding.EncodeToString([]byte("hello/world"))
	fmt.Println(url) // "aGVsbG8vd29ybGQ=" (same β€” diff shows with different bytes)

	// JWT headers never have padding β€” use RawURLEncoding
	raw := base64.RawURLEncoding.EncodeToString([]byte("hello/world"))
	fmt.Println(raw) // "aGVsbG8vd29ybGQ" (no trailing =)
}

How do I fix "illegal base64 data" errors in Go?

This error means the input contains characters outside the expected alphabet, or the padding is wrong. Three common causes: using StdEncoding on URL-safe input (swap to URLEncoding), using a padded encoder on unpadded input (swap to RawStdEncoding/RawURLEncoding), or trailing whitespace/newlines. Strip whitespace with strings.TrimSpace before decoding.

Go 1.21+
package main

import (
	"encoding/base64"
	"fmt"
	"log"
	"strings"
)

func main() {
	// Input from a webhook payload β€” has newlines stripped from wire format
	raw := "  aGVsbG8gd29ybGQ=  \n"
	cleaned := strings.TrimSpace(raw)

	decoded, err := base64.StdEncoding.DecodeString(cleaned)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(decoded)) // hello world
}

How do I stream-decode a large Base64-encoded file in Go?

Use base64.NewDecoder(base64.StdEncoding, reader) which wraps any io.Reader and decodes on the fly. Pipe it through io.Copy to write to the destination without buffering the entire file in memory.

Go 1.21+
package main

import (
	"encoding/base64"
	"io"
	"log"
	"os"
)

func main() {
	src, err := os.Open("attachment.b64")
	if err != nil {
		log.Fatal(err)
	}
	defer src.Close()

	dst, err := os.Create("attachment.bin")
	if err != nil {
		log.Fatal(err)
	}
	defer dst.Close()

	decoder := base64.NewDecoder(base64.StdEncoding, src)
	io.Copy(dst, decoder)
}

Can I decode a Base64 JWT payload in Go without a JWT library?

Yes. A JWT is three Base64url-encoded segments joined by dots. Split on "." and decode the second segment (index 1) with base64.RawURLEncoding.DecodeString β€” JWT headers and payloads use the URL-safe alphabet and no padding. The signature segment (index 2) is binary and usually only needed for verification.

Go 1.21+
package main

import (
	"encoding/base64"
	"fmt"
	"log"
	"strings"
)

func main() {
	token := "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3ItOTAxIiwicm9sZSI6ImFkbWluIn0.SIG"
	parts := strings.Split(token, ".")
	if len(parts) < 2 {
		log.Fatal("invalid JWT format")
	}

	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		log.Fatalf("decode payload: %v", err)
	}
	fmt.Println(string(payload))
	// {"sub":"usr-901","role":"admin"}
}

What encoding should I use to decode Base64 data from an HTTP API response?

Check the API docs or inspect the encoded string. If it contains + or / characters and ends with =, use StdEncoding. If it uses - and _ characters without =, use RawURLEncoding. When uncertain, try RawURLEncoding first β€” most modern APIs (OAuth2, JWT, Google Cloud, AWS) use URL-safe Base64 without padding.

Go 1.21+
package main

import (
	"encoding/base64"
	"strings"
)

// Detect encoding variant from the encoded string
func decodeAPIPayload(encoded string) ([]byte, error) {
	// URL-safe characters without padding β€” common in modern APIs
	if !strings.Contains(encoded, "+") && !strings.Contains(encoded, "/") {
		return base64.RawURLEncoding.DecodeString(encoded)
	}
	// Standard Base64 with padding
	return base64.StdEncoding.DecodeString(encoded)
}
  • Base64 Encoder β€” encode binary data or text to Base64 in the browser, useful for generating test fixtures to paste into your Go unit tests.
  • JWT Decoder β€” split and decode all three JWT segments at once, with field-by-field payload inspection β€” no Go code required when you just need to read a token during debugging.
  • URL Decoder β€” percent-decode URL-encoded strings, handy when API responses mix Base64url data with percent-encoded query parameters.
  • JSON Formatter β€” after decoding a Base64 JWT payload or API response, paste the JSON here to pretty-print and validate the structure instantly.
Also available in:JavaScriptPythonJavaC#
JO
James OkaforSystems Engineer

James is a systems engineer and Go enthusiast who focuses on high-performance microservices, command-line tooling, and infrastructure automation. He enjoys the simplicity and explicitness of Go and writes about building fast, reliable backend systems. When not coding he explores distributed systems concepts and contributes to open-source Go libraries.

HN
Hana NovΓ‘kovΓ‘Technical Reviewer

Hana is a backend engineer who has built production gRPC and REST services in Go for cloud-native environments. She cares deeply about API correctness, protobuf schema design, and the operational side of running Go services in Kubernetes. She writes about the Go standard library, encoding and marshalling patterns, gRPC best practices, and the subtleties of writing idiomatic Go that is easy to test and maintain.