docs: rewrite README around defineErrors and add philosophy articles#108
Merged
docs: rewrite README around defineErrors and add philosophy articles#108
Conversation
…s Error convention New philosophy article arguing that wellcrafted errors use `name` and `message` because JavaScript's Error class already established that convention. Covers JSON serializability, pragmatic Rust adaptation, and plain objects over classes. Also adds error-api-evolution to docs nav and cross-references from existing docs.
…gument Retitle from topic heading to argument: "defineErrors Is thiserror Without Macros". Expand template literals section with side-by-side definition code. Add closing section on how TypeScript's type inference replaces proc macros.
…omparison table Lead with "Define your errors. Type the rest." tagline. Add "Why define errors at all?" section explaining that errors cluster into logical groups per domain. Fix hero example logic (AlreadyExists, not NotFound for create flow). Expand comparison table to include better-result and fp-ts alongside neverthrow and Effect, honestly showing wellcrafted's manual composition tradeoff. Add Philosophy section on staying idiomatic to JavaScript. Reference ecosystem consistency with Supabase, SvelteKit, and TanStack Query patterns.
…cript Reframes the "I tried Effect and came back" narrative as a broader argument about why the silent majority of developers who tried typed errors gave up. Core thesis: TypeScript lacks the five language features (? operator, macros, pattern matching, ADTs, traits) that make FP error handling ergonomic in Rust and Haskell. wellcrafted sits in the pragmatic middle ground.
Targets the "almost-functional programmer" who wanted typed errors from Rust/Effect but found the FP machinery didn't fit TypeScript. States the compromises plainly and positions wellcrafted as the smallest useful subset of FP error handling ideas.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The README was selling wellcrafted as a grab-bag of utilities ("Delightful TypeScript utilities for elegant, type-safe applications") when the actual value proposition is
defineErrors— typed, serializable, composable error variants that no other Result library provides. This rewrites the README to lead with that, and adds five philosophy articles that explain the design decisions behind the API.The old README had a scattershot structure: emoji-heavy feature cards, a before/after try-catch comparison, and examples that used generic names like
ValidationErrorandApiErrorwithout showing the composition story. More concretely, the hero example had a logic bug — it returnedNotFoundwhen a user already existed during creation. The new README opens withdefineErrors, shows a realisticcreateUserflow, then builds up to cross-layer composition.README changes
The README now follows a deliberate progression:
defineErrorswith a realisticcreateUserthat showsAlreadyExistsandCreateFailedvariants,tryAsyncwrapping, andswitchdiscriminationdefineErrorsas the TypeScript equivalent of Rust'sthiserrortrySync/tryAsyncwithcatchhandlers that returndefineErrorsfactoriescause, then an API handler mapping domain errors to HTTP responsesif (error)composition vs method chains/generators){ data, error }shape as deliberately idiomatic, not novelPhilosophy articles
Five articles in
docs/philosophy/:"We Wrote a Builder Pattern, Then Deleted It" (
error-api-evolution.mdx) traces the full design journey — hand-written object literals →createTaggedErrorfactory → typed generics with overloads → fluent builder →defineErrors. The key data point: an audit of 321 call sites revealed that no error type mixed static and dynamic messages, which killed the builder's raison d'être."We Didn't Invent
nameandmessage— JavaScript Did" (why-name-and-message.mdx) argues that usingnameandmessageisn't arbitrary — it followsError's existing convention. Covers why plain objects over classes (serialization), whyObject.freezeover ownership (immutability), and how Rust's patterns map to JavaScript idioms without new abstractions."defineErrors Is thiserror Without Macros" (
rust-inspiration.mdx, updated) — retitled from a generic heading to a direct argument. Expanded with side-by-side Rust/TypeScript definition code, a table of five divergence points (proc macros → template literals, ownership → Object.freeze, etc.), and a section on how TypeScript'sconstgeneric inference replaces what proc macros do in Rust."You Tried Typed Errors and Gave Up. The Problem Wasn't You." (
from-effect-to-pragmatic-errors.mdx) — argues that the silent majority who tried FP error handling in TypeScript and walked away weren't wrong. TypeScript lacks the five language features (? operator, macros, pattern matching, ADTs, traits) that make FP error handling ergonomic in Rust and Haskell. wellcrafted sits in the pragmatic middle ground."wellcrafted Is for the Pragmatic Almost-Functional Programmer" (
for-the-pragmatic-fp-developer.mdx) — targets developers who wanted typed errors from Rust/Effect but found the FP machinery didn't fit TypeScript. States the compromises plainly, positions wellcrafted as the smallest useful subset of FP error handling ideas, and includes an honest ecosystem comparison table.