Reading — step 1 of 5
Read
~1 min readProduction
Production Formatters
What real formatters do:
gofmt (Go):
- Built into the language toolchain.
- One canonical style, no options.
- Universal in Go community.
- Example of "formatter as standard".
rustfmt (Rust):
- Some configurable, but discouraged.
- Tier 1 in Rust toolchain.
cargo fmtruns it.
prettier (JS/TS/etc):
- "Opinionated" but with knobs (semicolons, single vs double quotes).
- Multi-language: JS, TS, CSS, MD, JSON, YAML, GraphQL.
- Used by 100k+ projects.
black (Python):
- Strict opinions, minimal options.
- Replaced years of pep8-vs-yapf debates.
clang-format (C/C++):
- Highly configurable.
- Hundreds of options.
- Can match nearly any existing style.
- LLVM project's own backbone.
ruff format (Python, 2023+):
- Drop-in black replacement, written in Rust, much faster.
- Plus linter functionality (combined tool).
Design lessons:
- Few/no options: less bikeshedding, more consistency.
- Opinionated: pick one good style, stick to it.
- Fast: ms-scale or developers won't run it.
- Idempotent: format(format(x)) == format(x).
- Stable: format doesn't change between minor versions.
Common gotchas in implementations:
- Trailing newlines: file should end with \n.
- Tabs vs spaces: pick one, enforce.
- Line endings: CRLF vs LF (Git config eol).
- String quotes: single vs double; pick one.
- Trailing commas: in lists, function calls (multi-line).
- Blank lines: limit consecutive blanks.
Building a formatter:
- Pick a target language.
- Use existing parser (tree-sitter, language-server).
- Define style rules.
- Implement Doc algebra or similar.
- Test extensively (regression suite of files).
Don't:
- Roll your own parser (incomplete edge cases).
- Make options for everything (option fatigue).
Do:
- Use existing libraries (prettier, Black, gofmt source).
- Focus on style decisions, not parser implementation.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…