Customer Journeys Belong Next to Your Code
2026-05-13
2026-05-13
I am building TheBest.Ink alone. The whole product vision lives in my head. Even with that, I was getting overwhelmed holding all the scenarios in mind. Who claims what. Which path triggers moderation. When an artist gets marked as listed.
So I opened docs/customer-journeys.md and started writing user journeys as prose, with a Mermaid flowchart for each one. The first time I rendered them, I had a small, sharp moment: "oh, that is what this project does." The charts gave the project a shape I could not see from the code alone. It felt like seeing the project's soul. Not in a mystical way, but as the actual sequence of promises the product makes to users.
This post is about that moment, and the artifact it produced. It follows two earlier posts of mine, one arguing that spec-driven development solves the wrong problem, the other arguing that human and agent docs should be the same docs. This is the doc category neither of those named.
I do not think every customer journey in every company must live next to the code. That would be too broad. Some journeys belong in product discovery, design tools, analytics dashboards, or support processes. But for journeys that define how the system should behave end-to-end, especially in a small product team or an AI-assisted codebase, I think there is a strong case for keeping them in the repo.
The file sits at docs/customer-journeys.md, one section per journey. Each section is a few paragraphs of prose, a Mermaid flowchart, and status markers (NYI for "not yet implemented", NI for "needs improvement", CTA for "call to action", NC for "needs clarification").
The artist discovery and booking flow looks like this:
Five journeys live in that file today: artist discovery, studio discovery, review, claiming, and new artist or studio creation. Each one starts with "what is the user trying to do here, end-to-end" and ends with the path through the system that gets them there.
A simplified journey section could look like this:
### J-ARTIST-CLAIM-01: Artist claims an existing profile
Status: NI
Intent:
An artist should be able to prove ownership of an existing profile without blocking legitimate claims too early.
Key guarantees:
- Unclaimed profiles show a claim CTA.
- Suspicious claims are not hard-blocked immediately.
- Claims with weak evidence enter moderation.
- Verified claims unlock profile editing.
Coverage:
- E2E: artist-claim.spec.ts
- Integration: claim-moderation.service.spec.ts
That format is still experimental, but the idea is important: the journey should not only describe a flow. It should also describe the product intent and the behavioral guarantees that matter.
The doc types most repos have do not cover this.
None of them answer "what is the user actually trying to accomplish, end-to-end, across modules?" That is the gap.
I want to be honest about the prior art, because there is a lot of it. User journey mapping has been a UX practice for years. Use cases go back to Ivar Jacobson. User story mapping comes from Jeff Patton. BDD and specification by example come from Dan North and Gojko Adzic. Domain storytelling is from Stefan Hofer and Henning Schwentner. Living documentation is from Cyrille Martraire. The building blocks are old.
What I am doing sits closest to domain storytelling plus living documentation plus BDD without the Gherkin syntax. The difference is small but real. None of the prior art was written with AI agents as a first-class reader. The shape of the artifact changes when the reader is not only a human PM or a developer on the team, but also an agent that will read three or five files in a cold-start context and try to do something useful.
That is the only thing I claim is new here. The format, the in-repo location, and the freshness mechanism follow from that one shift in audience.
When I think about what agents struggle with on my repo, three things come up over and over.
Cold-start orientation. An agent opens three to five files at the start of a task and needs to know what the project does. A top-level README gives it the elevator pitch. A module README gives it local scope. Neither tells it what the user is actually trying to accomplish across modules. The journey doc does. On TheBest.Ink, the difference between "search for artists" and "search for artists, view a card, fall back to an external channel, or follow the booking path" is the difference between a code tour and a usable orientation.
Cross-module shape. Agents follow code paths inside a file well. They lose the thread when a flow crosses three modules and a background job. A flowchart in prose closes that gap. The agent sees the whole flow before it reads any of the code, so it knows which file fits where. The artist claim flow on TheBest.Ink touches the artist module, the studio module, the email module, the Instagram OAuth callback, and an LLM moderation step. No single file shows that. The journey does.
Intent disambiguation. "Should this case block the user or just warn them?" is a question I cannot always answer by reading the code. The journey can answer it, because intent is the whole point of the journey doc. When an artist tries to claim a profile and the email domain does not match the studio website, should we hard-block, throttle, or fall back to Instagram OAuth? The code could branch either way and both are technically reasonable. The journey tells the agent which branch matches the product intent.
That last point is the most important one. Code tells the agent what exists. The journey tells the agent what should happen.
There is a real caveat here, and I want to keep it visible. When the journey drifts from the code, the agent believes the wrong thing more confidently than if no doc had existed at all. A wrong map is worse than no map. That is exactly why the freshness story matters.
There is another caveat that matters once this leaves a solo project: ownership.
In my case, the answer is easy. I own the code, the product, the docs, and the tests. In a team, that is not always true. If the journey lives in Git, engineering becomes the default gatekeeper. That can be good because the doc sits close to the implementation, but it can also exclude the people who understand parts of the journey best: product, design, support, QA, or customer success.
So the question is not only "where should this document live?" It is also "who is responsible for keeping it true?"
A repo-based journey doc needs an owner. Maybe that is the engineer changing the flow. Maybe it is a product engineer. Maybe it is product and engineering together in PR review. But it cannot be an orphaned artifact. If nobody owns the journey, putting it next to the code only makes it stale in a more official-looking place.
Documents that describe behavior go stale. That is the oldest problem in technical writing. PRDs go stale because they are pre-build, stakeholder-facing, and nobody reads them after launch. Module READMEs survive because they sit next to the code, and they break in code review when someone changes the public API.
Customer journeys need the same kind of forcing function. My first idea was a 1:1 mapping between each journey and an e2e test. If the artist discovery journey says "unclaimed profile shows the claim dialog, no booking section", there should be a Playwright test with that exact assertion. If the test breaks, either the code is wrong or the journey is wrong. Either way, someone has to look at both.
But the 1:1 idea is probably too simple.
A journey and an e2e test are not naturally the same size. One journey can include branches, emails, background jobs, external services, moderation, retries, and admin decisions. One Playwright test should not necessarily cover all of that. Otherwise the tests become slow, brittle, and hard to debug.
A better version is this: each journey should contain behavioral claims, and the important claims should point to automated coverage where practical.
That coverage can be different depending on the claim:
The journey is not the test. The journey is the spine that connects intent, implementation, and coverage.
This is the part where BDD deserves an honest comparison. A Gherkin .feature file IS the test. Cucumber parses the prose, runs the step definitions, and the build breaks the moment the scenario drifts from the code. The freshness mechanism is automatic and free.
I dropped Gherkin because prose plus a flowchart reads better to both humans and agents than Given / When / Then does, and a flowchart shows branching that linear Gherkin cannot. That trade is deliberate, and I want to be clear about what it cost. I kept the BDD goal of keeping prose and code in sync. I gave up the BDD mechanism that did it for free.
Free prose and Mermaid are more expressive, but they are also more dangerous. They invite narrative drift. Someone can write a beautiful journey that no system actually follows.
So what I am really choosing is readability and system shape over executability. That gives me a better orientation artifact, but a weaker verification mechanism.
What I am left with is a manual mapping between journey claims and automated tests, plus a CI check I have not built yet.
Customer journeys are useful, but they are not enough by themselves.
They do not replace ADRs. A journey can show that a claim goes through moderation, but an ADR can explain why moderation exists and why it was designed that way.
They do not replace domain rules. A journey can show that a suspicious claim is not hard-blocked, but a domain document or test should define what "suspicious" means.
They do not replace analytics, support knowledge, permission models, legal constraints, or operational runbooks.
The journey is the spine. ADRs explain major decisions. Tests verify behavior. Domain docs define rules. The journey links them together.
That is the role I want this document to play.
A few things are not solved, and I think it is more useful to name them than to pretend they are.
File structure at scale. One customer-journeys.md works for five journeys. It will not work for fifty. Do I split by domain (docs/journeys/artists/, docs/journeys/studios/), or keep one file and use anchor links? I do not know what breaks first.
Stable IDs. If a test references journey 3.2 step 4 and I reorder the journeys, the reference rots. I probably need stable IDs per journey and per step, like J-ARTIST-DISCOVERY-04. I have not committed to a scheme yet.
Ownership in teams. In a solo project, I can update the journey when I change the feature. In a team, that responsibility needs to be explicit. Otherwise the file becomes one more abandoned doc with better syntax highlighting.
The limit of automatic checks. A CI check can verify that a journey file and a test file change in the same PR. That is coupling, and a machine can enforce it. It cannot verify that the new Mermaid arrow actually corresponds to the new test assertion. That is semantic alignment, and it needs a human who understands intent. There is no way to close that gap with tooling, at least not reliably. Every doc that is not executable has the same gap. ADRs have it. Module READMEs have it. Conventions files have it. I accept human review as the alignment mechanism for those. Journeys are the same.
So the honest version of the question is not "can I close the semantic gap?" It is "is the gap small enough that code review can hold it?" For five journeys, yes. For fifty, I am not sure.
For now I am running with one file, five journeys, prose plus Mermaid, and a loose intent to map each important journey claim to automated coverage where practical. The cognitive payoff alone, after almost a year of typing the code, has already paid for the writing. Whether the artifact stays useful at fifty journeys, or quietly drifts into another wrong map, is the part I do not yet know.
Some product knowledge is too important to live only in people's heads, tickets, or stale PRDs. For flows that define how the system should behave end-to-end, a repo-based customer journey document can act as shared context for humans, tests, and AI agents.
But it only works if it has ownership, stable IDs, and a review mechanism that keeps it close to reality.