# dedoc/scramble and the bet on generated OpenAPI docs over annotated ones

> Scramble generates OpenAPI 3.1.0 documentation for a Laravel application by reading the code instead of your PHPDoc comments, and it ships a React viewer plus a JSON document at two routes that stay in the local environment until you define a gate. The bet is that generated documentation cannot go stale, and the interesting question is where the semantic layer lives once reflection does the work.

**dedoc/scramble** — Modern Laravel OpenAPI (Swagger) documentation generator. No PHPDoc annotations required. 

- Repository: https://github.com/dedoc/scramble
- Website: https://scramble.dedoc.co/
- Stars: 2,216 · Forks: 209
- Language: PHP
- License: MIT
- Published: 2026-09-30 · Updated: 2026-09-30 · Language: en
- Canonical page: https://hysenlabs.com/projects/dedoc-scramble

## The problem Scramble takes over: PHPDoc that stops matching the code

The standard way to document a PHP API is to write docblocks and run a generator over them. That puts a second, hand-maintained description of your API next to the code that implements it, and the two drift apart the moment somebody adds a field without updating a comment. The failure is quiet: the documentation endpoint keeps returning 200, the schema keeps parsing, and the field you shipped last week is simply absent.

Scramble's stated motive is to remove that second copy. The project describes itself as generating API documentation for a Laravel project without requiring you to manually write PHPDoc annotations, and the argument in the README is that this lets you focus on code and avoid annotating every possible parameter and field, because annotated documentation goes out of date. The output format is OpenAPI 3.1.0.

That is a real trade, and it is worth being precise about which side of it you are on. A reflection-driven generator cannot document a field nobody has described, so descriptions, examples, and statements about which fields are safe to expose are exactly the things it cannot infer. A generated document is therefore accurate about shape and silent about meaning. The README's claim that your documentation will always be up to date and can be trusted is true about the schema and does not extend to the prose, and any team evaluating this needs to know that before they look at the output.

The audience is Laravel teams with a broad API surface: internal APIs consumed by a separate front end, mobile clients, or partner integrations, where the cost of a stale schema is measured in support tickets.

## Two routes, one gate, and an environment-based default

Installation adds exactly two routes to your application, and the README names both.

`/docs/api` is the user interface for reading the documentation. `/docs/api.json` is the OpenAPI document itself in JSON. That second route is the one that matters for tooling, because it is what you point a client generator, a mock server, a contract test or an SDK builder at, and because a document served as JSON is diffable in a pull request. A generator whose only output is a web page is a documentation tool; one that emits JSON is part of a pipeline.

The default is a security decision, and a good one. Both routes are available only in the `local` environment unless you change it, and the change is made by defining a `viewApiDocs` gate, with the README linking to the docs authorization section of the hosted documentation for how. An API document is a map of your system: it lists every route, every parameter and, depending on how your models are resolved, a great deal about your database schema. Leaving it on in production by accident is the kind of mistake that gets noticed externally.

The gate is also the right level of control, since it is an authorisation check rather than a configuration flag, so you can attach it to whatever your application already uses for permissions instead of inventing a second mechanism. The README does not describe the gate's signature or its default return value, so read the linked page rather than guessing at it.

One limitation follows from the architecture. Because the document is produced by your application from its own code, it is a live view of your routes, not a snapshot you can ship. Anything that changes behaviour between environments changes the document too.

## Installing the package and taking the first look at the document

The whole install is one Composer command, taken from the README:

```bash
composer require dedoc/scramble
```

There is no service provider to register by hand, no asset build to run and no publishing step in the README, which is consistent with a package that registers its own routes and ships a prebuilt viewer. After that, run your application in the `local` environment and open `/docs/api` to read the generated documentation, and request `/docs/api.json` to see the raw OpenAPI 3.1.0 document.

What you should look at first is the JSON, not the viewer. Open the document and check a handful of routes you know well: are the path parameters typed as strings or as integers, does a request body that you defined with a form request come back as a schema or as a bare object, and are error responses represented at all. Those three answers tell you how much configuration the project is going to need from you before the output is useful to a client generator.

The second thing to check is coverage. Because the generator reads the code, anything that is not statically visible to reflection will be missing or thin: routes registered through closures with untyped parameters, responses shaped by a DTO that is built at runtime, and polymorphic models are the usual suspects. The README does not document how to annotate a case that reflection cannot handle, and the hosted documentation at scramble.dedoc.co is where that answer lives.

If you serve the document to a team, pin the review to the JSON. A pull request that changes `/docs/api.json` is a pull request that changes your API contract, and it can be reviewed as one.

## The viewer is a separate React package built with Vite

The repository carries a second package manifest, and it is not the Composer package. `package.json` at the top level is named `@dedoc/scramble-dev-tools`, it is marked private, and it is a modern front-end project: React and React DOM at 19, Vite 8, Tailwind CSS 4 through the Vite plugin, and TypeScript 7, with a Vite React plugin and Node type definitions. The script list is three lines long:

```json
"dev": "vite",
"build": "vite build",
"typecheck": "tsc --noEmit"
```

So the docs interface is a React single-page app built with Vite, developed separately from the PHP package and published to nobody, since the private flag means it is not on any registry. A `vite.config.ts` and a `tsconfig.json` sit beside it, and a `dist/` directory is present at the top level of the repository next to `src/`, `resources/` and `routes/`.

The reasonable inference, and the README does not state it, is that `dist/` holds the built viewer so the Composer package can serve the interface from PHP without requiring a Node toolchain on the machine that installs it. That is the only arrangement that makes `composer require` a sufficient install step, which is what the README claims. It also means the front end is a build artefact travelling inside a PHP distribution, so a mismatch between the shipped `dist/` and the current `src/` would show up as a viewer that does not match the document.

The split also tells you something about priorities. The PHP half carries PHPStan, PHPUnit and Pint configuration, while the JavaScript half has a typecheck and no test script at all in the scripts shown. The document generator is the tested part; the interface around it is not.

## config/, dictionaries/ and stubs/ are where the semantics have to live

Three directories at the top level explain how much configuration you can expect to do, even though the README says nothing about them.

`config/` is the ordinary Laravel convention for a package that wants to be adjusted without edits to `src/`. `dictionaries/` is the more interesting one: a directory of data files whose name suggests a mapping layer, plausibly from PHP types and Laravel constructs to OpenAPI schema types. If that reading is right, it is the answer to the question the README leaves open, which is how you correct a type that reflection gets wrong. It is also the place a project like this would put overrides per model, per field or per route.

`stubs/` is the third. In Laravel packages, stub files exist so a published file can be copied into the host application, which is how a package lets you own a config file, a service provider or a route file. Its presence suggests there is something to publish, even though the README describes no publish step, which is a small gap between the docs and the tree.

None of this is documented in the excerpt, so treat it as a map to check rather than a specification. The practical sequence is: install, look at `/docs/api.json`, find the routes that are wrong, and then go looking in `config/` and `dictionaries/` for the override rather than reaching for annotations. A project that only worked by annotation would not need a dictionaries directory, and its existence is the strongest available evidence that Scramble is intended to be configured rather than merely installed.

## Scramble against annotation-based generation, and where it is the wrong tool

The alternative is the one the README names: write PHPDoc annotations and run a generator that reads them, which is the conventional approach in the PHP ecosystem. The difference in approach is the direction of the dependency. Annotation-driven generation has code as its only source of truth for behaviour and comments as its only source of truth for meaning, so a human decides what a field means and the generator reports what the human wrote. Reflection-driven generation inverts that: shape comes from the signature, and meaning has to come from configuration.

The practical consequences run in both directions. With annotations, coverage is a discipline problem, and stale documentation is the failure. With reflection, coverage is automatic and semantic quality is the failure. If your team has a strong documentation culture and a small API, annotations are cheaper. If your API has two hundred routes and one technical writer, the discipline does not scale and the automatic path wins.

There are clear cases where neither is the answer. Scramble documents a Laravel application's routes. It does not document third-party services you call, it does not generate clients, and it does not validate that your implementation matches the schema it produced, which is the job of a contract test. It is also not a substitute for prose: a public API consumed by external developers needs a hand-written narrative alongside the generated document, and the generated document will never contain a worked example, because nothing in your code says what a realistic request looks like.

One more limitation is structural. The default environment gate means the document is not part of your deployed application unless you choose to expose it, and if you do expose it, you are publishing a description of your internals. Decide that on purpose rather than by convenience.

## A 0.13.x line, same-day patch tags, and a PHPStan baseline

The licence is the MIT License, with the text in `LICENSE.md` at the repository root. That is permissive, allows use in commercial applications, and requires the notice to travel with copies. This is a reading of the licence rather than legal advice.

The release pattern is worth understanding before you depend on it. Recent tags include v0.13.45 and v0.13.44, both published on 2026-09-18, and 0.13.43 on 2026-09-08. Two releases on one day means patches are shipped as they are made, and the last push was on 2026-09-28, so the work is current. The line is still 0.13.x, and under semantic versioning a 0.x version carries no compatibility promise, so a minor bump inside 0.13 can break you. Pin the exact version in your `composer.json` and read `CHANGELOG.md` at the repository root before upgrading, which exists for exactly this purpose.

The repository also carries its own quality tooling, and one file in it is a warning sign worth understanding. `phpstan.neon` and `phpstan-baseline.neon` mean static analysis runs with a recorded list of accepted violations, which is normal practice in a project that wants analysis enabled today without fixing a backlog first. The consequence is that the baseline grows quietly unless someone watches it. `phpunit.xml.dist` and a `tests/` directory mean there is a test suite, `pint.json` pins the code style, and `.editorconfig` and `.gitattributes` handle the rest of the housekeeping.

The gap an evaluator will notice first is versions. The README states no minimum PHP version and no minimum Laravel version, and it does not say which Laravel releases are supported or whether each major version of the framework needs a different Scramble release. The `composer.json` is in the repository, so the answer exists, but the README is where you would expect it, and its absence is the first thing to check before you try the install on an older application.

## Conclusion

Adopt dedoc/scramble for a Laravel API whose surface is large enough that keeping PHPDoc annotations honest has become the bottleneck, and configure the viewApiDocs gate deliberately before you think about serving the document anywhere but your own machine. Do not adopt it expecting hand-written descriptions and examples, since the README makes no claim about either, and do not assume a stated version floor, because the README names no minimum PHP or Laravel version. Verify first by running composer require dedoc/scramble, opening both /docs/api and /docs/api.json on the local environment, and reading the docs authorization page linked from the README before changing the gate.

## FAQ

### How do I install Scramble for Laravel?

The README gives a single Composer command: composer require dedoc/scramble. There is no service provider to register by hand, no asset build and no publish step described, and after installing you get two routes added to your application.

### Which routes does Scramble add to a Laravel app?

Two: /docs/api, which is the user interface for reading the documentation, and /docs/api.json, which is the OpenAPI document in JSON. By default both are available only in the local environment.

### How do I make the Scramble documentation available outside the local environment?

By defining the viewApiDocs gate, which the README links to the docs authorization section of the hosted documentation for. The README does not describe the gate signature or its default return value, so that page is the place to check.

### What OpenAPI version does Scramble generate?

The README states that documentation is generated in OpenAPI 3.1.0 format. The JSON is available at /docs/api.json, which is the route to point a client generator or a contract test at rather than the HTML viewer.

### What are the minimum PHP and Laravel versions for Scramble?

The README does not say. It states no minimum PHP version, no minimum Laravel version and no list of supported framework releases, so the composer.json at the repository root and the documentation at scramble.dedoc.co are where to check.

### How often is Scramble released, and is it stable?

Recent tags include v0.13.45 and v0.13.44, both on 2026-09-18, and 0.13.43 on 2026-09-08, with the last push on 2026-09-28. The line is still 0.13.x, which carries no compatibility promise, so pin an exact version and read CHANGELOG.md before upgrading.

## Sources

- [dedoc/scramble on GitHub](https://github.com/dedoc/scramble)
- [License: MIT](https://github.com/dedoc/scramble/blob/main/LICENSE)
- [Project website](https://scramble.dedoc.co/)
- [README](https://github.com/dedoc/scramble/blob/main/README.md)
- [Releases](https://github.com/dedoc/scramble/releases)

---

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