Inspiration
Picture this: it's your first day at a new job, facing a massive polyglot microservices codebase. Eight services, dozens of classes, and your tech lead just tells you to "get familiar with the visits feature." You end up wasting two hours opening files, blindly cmd+clicking through interfaces, and drawing messy arrows on a notebook just to map one single request flow. We've all been there, and it's awful.
We wanted to compress those hours of frustration into ten seconds. We wanted to build dynamic documentation that actually regenerates from the source code instead of going stale the moment someone pushes a commit.
What it does
devmap is a CLI tool that takes a Spring Boot microservices repo and turns it into an interactive dashboard, focusing on one specific use-case at a time.
If you run devmap discover, it scans the repo and lists the main request flows (capped at 20, prioritizing the complex cross-service ones for maximum impact). Then, you run devmap feature and boom: a browser opens with a 6-tab dashboard for that exact flow:
Flow: A Mermaid sequenceDiagram showing the request path, alongside a narrative paragraph written by an LLM explaining the logic.
Dependencies: A React Flow graph. Node size shows lines of code, color shows the microservice, and the lines tell you if it's an import, HTTP call, or gateway route.
Persistence: An ER diagram that actually spots cross-service foreign keys (the sneaky joins held by value with no JPA relationship that always confuse new engineers).
API: Every endpoint touched by the use-case.
Components: A filterable grid of classes. If you click one, it deep-links straight into VS Code or Cursor at the exact line.
Events: Completely honest about async messaging. It lists the patterns it scanned for and says detected: false rather than hallucinating queues that don't exist.
It's fast: under 25 seconds for a cold run with the LLM. If the cache is warm (or you use --airplane mode), it's instant.
How we built it
We set it up as a monorepo with three pnpm workspaces:
agent/: Node + TypeScript. This is our regex-only Java indexer (we avoided booting a heavy JVM). It makes two Sonnet 4.6 calls (discover and scope), fires off Haiku 4.5 in parallel to summarize the classes, and uses Sonnet once more to write the narrative. We built a file-based cache keyed by sha1(repoPath + git-HEAD + devmapVersion) to save time. It's served by Express + Vite middleware.
web/: Vite + React + Tailwind v4 + shadcn/ui. We used React Flow and Mermaid v11 for the visuals. It just fetches feature.json and renders the tabs.
schema/: A single Zod schema that exports types to both packages so everything stays perfectly in sync.
The pipeline looks like this:
Plaintext .java sources → static regex indexer → Sonnet "discover use-cases" → list of 13 use-cases → Sonnet "scope " → exact FQNs in the request flow → local view builders prep data (components, dependencies, API) → Haiku "summarize components" (parallel) → Sonnet "reconstruct flow narrative" → feature.json → Express + Vite + browser open
Challenges we ran into
Oh man, quite a few:
The lexical-match foot-gun: We initially used substring matching for scoping. We ran devmap feature pets and got 43 components because the word "pet" is in "petclinic", matching every package. We tried word boundaries (\bpet\b), but that broke camelCase like PetResource. Eventually, we built an experimental branch where we ripped out lexical matching completely, sent the full index to Sonnet, and let the LLM do the scoping. It dropped from 43 wrong components to 9 perfect ones.
Mermaid v11 strictness: Our ER diagrams kept crashing into our amber fallback screens. Mermaid hated our bare FK_byValue tags and choked on Java generics because the < > in Set broke the parser. We had to sanitize the types and emit custom comments to fix it.
Vite proxy nightmares: Our web proxy was hardcoded to localhost:3000. When we tried to demo two features side-by-side, the second Vite instance booted on port 5174 but still pulled data from the first app's Express server. Both windows showed the exact same feature! We fixed it by injecting a DEVMAP_API_PORT env var dynamically.
Cross-service FKs: Finding relationships where a petId is just stored as an int (because the actual Pet is in another database) was super tricky without JPA annotations. We had to write custom rules matching field names and @Entity tags across services. It became our best "wow" feature.
Hackathon WiFi: Live demos are scary. We built an --airplane mode that reads from a pre-committed JSON cache so we can demo in under 3 seconds, zero internet required.
Accomplishments that we're proud of One keystroke to a polished dashboard: pnpm demo goes from cold-terminal to browser in well under 5 seconds. No API keys or network needed.
Two scoping algorithms: We have v1 (lexical + LLM classifier) on main for our bulletproof demo, and v2 (full LLM scoping) on feature/use-case-mode pushing the real product vision.
Honesty: The Events tab refuses to hallucinate async events. It tells you exactly what it looked for and what it didn't find.
126 passing tests: Across 25 files, we kept our test suite green during a hackathon, including a live-LLM-gated integration test!
Single source of truth: Our Zod schema handles runtime validation and generates TS types for both the frontend and backend.
What we learned Substring matching is a trap: For lexical scoping, you either tokenize perfectly (camelCase + separators) or you just let an LLM do the judgment.
Prompt caching saves lives (and credits): Sending a 200-line index to four different prompts is way cheaper and faster if you use cache_control: ephemeral.
Defense-in-depth for UI: Server-side structural checks aren't enough. You need strict client-side parsing with graceful UI fallbacks, otherwise a single bad Mermaid string breaks the whole app.
Pre-warmed caches are the ultimate hackathon cheat code: Pushing a cached JSON file and adding an airplane mode flag removes 99% of demo anxiety.
What's next for devmap Generalizing the configs: We want to support custom microservice naming patterns and configurable gateway YAML locations via a devmap.config.json file.
Beyond Spring Boot: Since our indexer uses static regex instead of JVM reflection, we could easily support Quarkus or Micronaut just by swapping out the annotation patterns.
Watch mode: Re-index and re-scope automatically on file changes so the dashboard updates live while the developer codes.
Global distribution: Right now you have to run it from the workspace root. We want devmap to be a true global command you can run anywhere (pnpm link --global).
Built With
- claude-haiku-4-5
- claude-sonnet-4-6
- commander
- dagre
- express.js
- lucide-react
- mermaid
- node.js
- ora
- pnpm
- react
- shadcn-ui
- tailwindcss
- typescript
- vite
- zod
Log in or sign up for Devpost to join the conversation.