genericmilk/docudoodle
Docudoodle is an AI-powered PHP documentation generator that analyzes your codebase and writes Markdown docs. It supports OpenAI, Claude, Gemini, Azure and Ollama, with smart caching that only processes changed files and skips existing docs.
1. Install and publish config
composer require genericmilk/docudoodle
php artisan vendor:publish --tag=docudoodle-config
2. Set a provider key — the simplest path is OpenAI:
OPENAI_API_KEY=sk-...
DOCUDOODLE_API_PROVIDER=openai
DOCUDOODLE_MODEL=gpt-4o-mini
(Alternatives: CLAUDE_API_KEY, GEMINI_API_KEY, Azure AZURE_OPENAI_*, or DOCUDOODLE_API_PROVIDER=ollama for fully local runs.)
3. Run it
php artisan docudoodle:generate
Markdown lands in the configured output_dir, mirroring your source tree.
Where to look first: config/docudoodle.php. The three settings that matter most day-to-day are output_dir, default_extensions (php, yaml, yml by default), and default_skip_dirs (vendor/, node_modules/, tests/, cache/ by default). Get those right before your first real run.
First use case: inheriting an undocumented app. Point Docudoodle at a subdirectory (e.g. app/Models), set output_dir to a scratch folder, and run once. This validates your key, model, cost-per-file, and output quality in a few minutes rather than spending an hour and a few dollars documenting the whole codebase blind. Once the output reads well, widen the scope and commit the docs to docs/ for PR review.
Scope runs by directory, not by whole repo. The command has no --path flag, so use source_dirs/skip_dirs in config (or per-environment config) to chunk work. Documenting app/Models, then app/Http/Controllers, then app/Services in separate runs gives you reviewable diffs and lets you abort early if quality is off.
Let the cache drive the workflow. Caching is the core ergonomic win: unchanged files are skipped, and deleted sources have their docs pruned. A realistic rhythm is:
php artisan docudoodle:generatedocs/ in sync automaticallyKeep the cache file alive across runs. In CI, restore .docudoodle_cache.json (or whatever cache_file_path points to) from your CI cache before running, or every build is a full-price rebuild.
Scheduled top-up. Wrap the command in the scheduler so docs never drift far from code:
$schedule->command('docudoodle:generate')
->weeklyOn(1, '03:00')
->withoutOverlapping()
->onOneServer()
->runInBackground();
withoutOverlapping() is not optional — the command is synchronous and single-threaded, and a long run against a real codebase will happily overlap the next tick.
Custom prompt templates per domain. DOCUDOODLE_PROMPT_TEMPLATE accepts a markdown file, and the available variables are {FILE_PATH}, {FILE_CONTENT}, {FILE_NAME}, {EXTENSION}, {BASE_NAME}, {DIRECTORY}. Commit templates to resources/prompts/ so they're versioned with the code. Good candidates for separate templates:
| Template | Target | Emphasis |
|---|---|---|
prompts/api.md |
app/Http/Controllers |
routes, request/response shapes, auth |
prompts/domain.md |
app/Models, app/Services |
relationships, invariants, side effects |
prompts/infra.md |
config/, *.yml |
keys, defaults, environment coupling |
The default template in v2.3.0 already asks for sequence diagrams, class diagrams, use cases and workflows — start there and trim rather than rewrite.
Treat generated docs as build artifacts you review. Put them in docs/ (or docs/generated/), commit them, and review diffs in PRs. If you want a gate:
php artisan docudoodle:generate
git diff --exit-code -- docs/ || echo "Docs are stale — commit the regenerated output"
Stakeholder publishing. --jira and --confluence push output to Atlassian. The useful combination is --confluence --no-files for a docs-only publish pipeline, keeping the repo clean while non-engineers get readable pages in a space. Requires guzzlehttp/guzzle:^7.0 explicitly installed.
Provider strategy. Use the cheap default (gpt-4o-mini) for bulk coverage, and re-run high-value modules with a stronger model by overriding DOCUDOODLE_MODEL for that single invocation. For regulated or closed-source code, DOCUDOODLE_API_PROVIDER=ollama keeps everything on-box; Azure gives you compliance and regional data handling without changing the command.
Changing the model, provider, or prompt template invalidates the entire cache. The cache stores a _config_hash covering exactly those inputs. Bumping from gpt-4o-mini to gpt-4o isn't an incremental upgrade — it's a full-price rebuild of every file. If you experiment with prompts, isolate the blast radius with --cache-path:
php artisan docudoodle:generate \
--cache-path=/tmp/docudoodle-experiment.json
Orphan cleanup deletes files. This is the sharpest edge in the package. If a source file disappears, Docudoodle removes its corresponding doc. That's correct only if output_dir contains nothing but generated output. Never point output_dir at a folder holding handwritten architecture notes, ADRs, or onboarding guides — keep human-authored docs somewhere else entirely.
Manual edits to generated files are lost on the next run. The cache skips unchanged source files, so a doc you hand-tuned survives while its source is untouched, then gets clobbered the moment that file changes. Any correction you care about belongs in the prompt template, not the output.
--no-files produces nothing on its own. It only makes sense combined with --jira or --confluence; --no-files alone is a no-op run that still burns API tokens for output it then discards.
Token limits truncate silently. max_tokens defaults to 10000 and applies per call. Large controllers, fat models, or long service classes can exceed the input side, producing docs that quietly omit half the methods. Watch for suspiciously thin docs on your biggest files — split the class, add the file to skip_dirs, or document it by hand.
Skip-directory patterns are prefix/wildcard strings, not globs. Entries look like vendor/ and /wildcard/*/path/, matched against the file path. Wildcard support is new in v2.3.0, so verify on a pinned older version. Confirm your patterns actually bite by pointing output_dir at /tmp and checking which files produced output.
No documented dry-run. The safest proxy is a scratch output_dir plus a narrow source subset. Do this before the first full run — a misconfigured default_extensions (e.g. adding blade.php-adjacent things, or js) will balloon token spend fast.
Rate limits and long runs. There's no queueing, retry policy, or parallelism exposed. A 429 mid-run means a partial output set; re-running is cheap thanks to the cache, so prefer several smaller scoped invocations over one marathon. Set a generous timeout() if you drive it from the scheduler or a CI step.
Key hygiene. OPENAI_API_KEY is the standard Laravel-ecosystem variable name and may collide with other packages reading it. In CI, use a budget-capped key and never a production one — and remember that every processed file's contents are sent to the provider. For proprietary code, use Ollama or Azure, and check your data-handling policy before the first run.
Maturity and stability. 163 stars, zero dependents, and a small maintainer pool means you should pin the version in composer.json ("genericmilk/docudoodle": "^2.3" at most) and read release notes before upgrading. v2.3.0 is your floor on modern stacks — it's the release that added Laravel 13 and PHP 8.5 support and refreshed the Claude API handling, so older tags will break on Claude. If the command's behaviour matters to your pipeline, wrap it in your own Artisan command or CI job rather than calling it directly from fifty places, so a package change is a one-line fix.
Extension points are thin. Config plus prompt templates are the whole surface — there are no documented events, middleware, or output formatters. If you need post-processing (adding frontmatter, moving files, building an index page), do it as a shell step after the command, not by patching vendor code.
How can I help you explore Laravel packages today?