Ten billion lines. That's how much code quicktype has generated. Before GitHub Copilot, before ChatGPT, it was arguably the most prolific code generator on the planet.

Mark and I launched quicktype in July 2017 as an open-source tool that generates strongly-typed models and serializers from JSON. By its first birthday, we'd crossed a billion lines.

It supports over 30 languages—TypeScript, Python, Go, Rust, Swift, Kotlin, Java, C#, C++, Haskell, Dart, Elm, and more—each generating idiomatic, human-quality code. The architecture we built, with clean separation between the type graph and language-specific renderers, made it possible for the community to add many of these.

The Origin Story

In 2016, I was building a Swift app before Swift 4 and Codable. There was no nice way to serialize JSON—you had to write tedious boilerplate for every model type:

struct User {
    let name: String
    let email: String
    let age: Int

    init?(json: [String: Any]) {
        guard let name = json["name"] as? String,
              let email = json["email"] as? String,
              let age = json["age"] as? Int else {
            return nil
        }
        self.name = name
        self.email = email
        self.age = age
    }

    func toJSON() -> [String: Any] {
        return ["name": name, "email": email, "age": age]
    }
}

Every time I changed my data model, I had to update serialization methods by hand. This is a job for computers.

I hacked together a prototype—paste JSON, get Swift structs—and showed it to Mark. He had deep expertise in type systems and compilers, and saw immediately that this wasn't just string munging. How do you infer the best types from sample data? How do you handle heterogeneous arrays? How do you know when an object is really a dictionary?

The premise was simple: paste JSON, get type-safe code. The execution required solving some surprisingly deep computer science problems.

The Architecture: Read, Simplify, Render

quicktype processes JSON in three stages, like a compiler (see quicktype Under the Hood for the deep dive):

Read: Parse JSON and infer an initial type graph. Each unique structure becomes a node—an array of objects creates an Array type containing a Class type.

Simplify: Optimize the type graph. Create union types when the same position can contain different types ([1, "1"]Array<Int | String>). Merge equivalent types. Detect maps vs. objects using a Markov chain (more on this below).

Render: Convert the type graph to source code. Each language has its own renderer handling naming conventions, serialization, type syntax, and imports.

This architecture made it easy to add languages—just implement a new Renderer. That's how we went from 2 languages at launch to over 30.

Little Big Details

We called these "Little Big Details"—the subtle niceties that make quicktype's output look human-written.

Property Names

JSON property names come in all shapes:

{
  "user_name": "alice",
  "firstName": "Alice",
  "LAST-NAME": "Smith",
  "EmailAddress": "alice@example.com"
}

quicktype detects naming styles, splits names into words, reconstructs them in the target language's convention, and generates mapping code when needed. For Swift:

struct User: Codable {
    let userName: String
    let firstName: String
    let lastName: String
    let emailAddress: String

    enum CodingKeys: String, CodingKey {
        case userName = "user_name"
        case firstName
        case lastName = "LAST-NAME"
        case emailAddress = "EmailAddress"
    }
}

Contextual Class Names

Consider this JSON:

{
  "user": {
    "address": { "street": "123 Main St", "city": "Springfield" }
  },
  "company": {
    "address": {
      "street": "456 Business Ave",
      "city": "Commerce City",
      "suite": "100"
    }
  }
}

Both have an address with different structures. A naive approach names both Address and causes conflicts. quicktype uses contextual naming:

interface User {
  address: UserAddress;
}

interface UserAddress {
  street: string;
  city: string;
}

interface Company {
  address: CompanyAddress;
}

interface CompanyAddress {
  street: string;
  city: string;
  suite: string;
}

Prefixes are only added when necessary.

Detecting Maps with Markov Chains

My favorite innovation (I wrote a blog post about it). Consider this Bitcoin blockchain data:

{
  "000000000000000000c846dee2e13c6408f5": {
    "hash": "000000000000000000c846dee2e13c6408f5",
    "height": 503162,
    "time": 1515187634
  },
  "00000000000000000024fb37364cbf81fd95": {
    "hash": "00000000000000000024fb37364cbf81fd95",
    "height": 503163,
    "time": 1515188162
  }
}

A naive translation would create a class with properties like the000000000000000000C846Dee2E13C6408F5—clearly wrong. A human would immediately recognize this as a Map<String, Block>.

How? The property names don't look like property names—they look like data. We quantified this intuition with a Markov chain trained on typical property names. The chain models character sequence probabilities: qui is high probability, qux is near zero.

quicktype scores each property name against this model. Names like firstName score high; hashes score near zero. Below a threshold, it generates a map:

typealias Blocks = [String: Block]

struct Block: Codable {
    let hash: String
    let height: Int
    let time: Int
}

Multiple Samples for Better Types

A single JSON sample might not represent all possible values (more details). Provide two samples:

// Sample 1: User is online
{ "name": "Alice", "status": "online", "lastSeen": null }

// Sample 2: User is offline
{ "name": "Bob", "status": "offline", "lastSeen": "2018-03-08T10:30:00Z" }

And quicktype correctly infers lastSeen: string | null by merging type graphs from all samples.

TypeScript as a Schema Language

You can write TypeScript type definitions and use them as input:

interface Person {
  name: string;
  nickname?: string; // an optional property
  luckyNumber: number;
}

TypeScript is more readable than JSON Schema, making it an excellent "schema language":

quicktype pokedex.json -o pokedex.ts --just-types  # Infer types from JSON
quicktype pokedex.ts -o src/ios/models.swift       # Generate Swift from TypeScript

Cross-Language Type Conversion

quicktype can also read source files from one language and generate equivalent types in another:

quicktype --src-lang csharp Models.cs -o Models.swift

What We Learned

Most developers don't know JSON. We expected valid JSON. We got trailing commas, single quotes, unquoted keys, comments, JavaScript objects. We built increasingly sophisticated error recovery to handle the garbage people pasted in.

Details matter. Property naming, contextual class names, map detection—these made quicktype feel magical. Generated code that looks human-written encourages adoption.

Meet developers where they are. The web app was a great demo, but adoption exploded with CLI and IDE integrations.

Open source works. The Rust, Kotlin, and Python renderers came from contributors. We couldn't have built 30+ languages ourselves.

Why quicktype Still Matters

In the age of Copilot and ChatGPT, quicktype does something LLMs struggle with: the more data you give it, the better it understands your schema. Feed quicktype a hundred JSON samples and it produces more accurate types. Feed an LLM a hundred samples and it will hit context limits or hallucinate properties.

quicktype is deterministic. Same input, same output. It doesn't guess—it infers types through principled algorithms. That predictability matters for production code.

Give it a spin at quicktype.io, or contribute on GitHub.