How I use Claude to develop SlashFeed: from idea to production
Anthropic recently published an article on how to use Opus 4.7 with Claude Code. I read it with interest because it partially reflects what I’ve built over the past few months working on SlashFeed — but only partially. Their model is optimised for autonomous, long-running sessions, delegating entire tasks to the model with minimal interaction. My workflow is different: more collaborative, more fragmented, and built around a specific setup that combines Claude.ai, Zed, Tidewave, and a local MCP server.
This article describes how my development process actually works — from the first idea through to release — and where Anthropic’s guidance integrates with what I’ve learned in practice.
The starting point: context as infrastructure
The most important thing I understood in the first few months is that the quality of Claude’s output depends almost entirely on the quality of the context you give it. That’s not a trivial observation: it means that preparing Claude to work with you is a technical investment, not a footnote.
In SlashFeed, that context lives in three places.
CLAUDE.md is the primary document that describes the project to Claude Code. It contains the directory structure, naming conventions, the main modules, non-obvious dependencies, and code style rules. It’s not human documentation repurposed — it’s written explicitly to be read by a language model at the start of each session. The difference is subtle but real: less prose, more structure, constraints stated explicitly.
The .md files in docs/ cover specific architectural decisions. Every non-obvious decision — why I use two separate Finch pools, how the Oban worker load-gating system works, the structure of the feed parser — has a dedicated document that describes the problem, the alternatives considered, and the chosen solution. These files serve two purposes: they force me to think before I implement, and they give Claude the reasoning behind the code, not just the code itself.
Tidewave MCP closes the loop by connecting Claude to the application runtime. Instead of explaining the Ecto schema structure every time, Claude can query the database directly, read the logs, and understand the current state of the app. The difference in response quality when Claude has live access to the app versus operating on static descriptions is significant.
Anthropic’s article makes the same point in different words: give the model the context it needs in the first turn, rather than building it progressively across many interactions. I agree completely — but in my experience the problem isn’t how to structure the session prompt. It’s the preparatory work that makes that first turn complete and precise in the first place.
Phase 1: the idea and converging toward a solution
Everything starts with an idea — sometimes vague, sometimes precise. How I develop it with Claude depends on its nature.
If it’s a technical idea with architectural implications — for example, adding an alert rule system to notify when a certain keyword appears in a feed — the process goes like this.
I open a conversation on Claude.ai (not Claude Code — that distinction matters) and describe the problem narratively. I don’t ask “how do I implement X” — I ask “I have this problem, these are my constraints, what am I not considering?” The difference is that the first question gets me a solution; the second gets me an analysis.
At this point I attach or describe the relevant .md files from the project documentation. Claude reads the existing structure — Ecto schemas, naming conventions, patterns already in use — and proposes an approach that integrates with what exists rather than ignoring it. Without that context, proposals tend to be correct in the abstract but difficult to integrate in practice.
The convergence session can run long. I discuss alternatives, ask for deeper exploration of edge cases, push back. What Anthropic describes as “reducing the number of user interactions” is valid for the implementation phase — but in the design phase, asking a lot of questions is exactly the point. A model that responds quickly is less useful than one that helps you find problems before they exist in the code.
The output of this phase is a document: a description of the agreed solution, with the main choices made explicit and the discarded alternatives explained. That document becomes the input for the next phase.
Phase 2: the briefing for Claude Code
When I’m ready to implement, I switch tools. Claude.ai is for reasoning; Claude Code — in my setup, integrated in Zed — is for operating on the code.
The transition isn’t automatic. I need to translate the design conversation into an effective briefing for the implementation session. This is where Anthropic’s guidance on using Opus 4.7 becomes most relevant.
The briefing I write in the first turn includes:
- The problem to solve, framed in terms of expected behaviour
- The relevant files — not all project files, but the ones Claude will need to read or modify
- Explicit constraints: conventions to follow, patterns to respect, dependencies not to break
- Acceptance criteria: how will I know the implementation is correct?
Anthropic says Opus 4.7 handles ambiguity better than previous models, and I believe it — but in practice I find that precise briefings produce more reliable results than vague ones, regardless of the model’s ability to infer missing context. The difference between “add alert rules” and “add an alert rule system that works like this: AlertRule Ecto schema with these fields, an Oban worker that checks every new entry against the user’s active rules, notification via this existing mechanism” is the difference between a session that produces code to review and one that produces code to test.
One practical detail I’ve learned: CLAUDE.md is re-read at the start of every Claude Code session. This means global conventions don’t need to be repeated in the briefing — they’re already there. The first turn can focus on the specific problem, not on the general project context.
Phase 3: implementation and the feedback loop
In the implementation phase my role changes. I’m no longer the designer — I’m the reviewer. Claude Code proposes the code, I check whether it makes sense, run the tests, and report what doesn’t work.
Some concrete observations about how this loop functions.
Claude Code reads before it writes. When I provide the relevant files in the briefing, the model analyses them before producing code. This dramatically reduces the probability of implementations that ignore patterns already in use in the codebase. Without this step, the code produced is often technically correct but stylistically inconsistent with the rest of the project.
The feedback loop must be precise. When something doesn’t work, “it doesn’t work” is not sufficient information. “The test test/slashfeed/alerts/alert_rule_test.exs:45 fails with this specific error” is the kind of input that produces a targeted fix rather than a rewrite. Anthropic notes that Opus 4.7 calls tools less and reasons more — that’s useful, but the quality of the reasoning depends on the quality of the observation you give it.
Tidewave changes the dynamic in a non-trivial way. When Claude can query the live database and read the application logs, the feedback loop shortens. Instead of describing the state of the app, Claude can observe it directly. For bugs that depend on database state — queries producing unexpected results, associations not loading as expected — this is the difference between an hour of debugging and ten minutes.
The {:snooze, n} Oban worker pattern is a concrete example. The load-gating feature that reads pg_stat_activity to decide whether to defer a job was debugged entirely with Tidewave active. Claude could see the actual Postgres statistics values, not my description of what I thought they were showing.
Phase 4: tests
Testing is where my workflow diverges most from what Anthropic describes. Their model assumes Claude Code runs tests autonomously as part of the task. In my setup that’s not yet automatic — I run mix test manually and report the results.
What I’ve learned along the way.
I ask Claude to write tests before the code when the behaviour is complex. It’s not TDD in the formal sense — it’s using the act of writing tests to verify that Claude and I have understood the problem in the same way. If the test Claude writes tests the wrong behaviour, I find out before I have production code to review.
Oban worker tests are a special case. Oban provides specific helpers for testing workers in isolation — calling perform/1 directly without going through the queue. Claude knows these patterns, but tends to use more generic helpers if you don’t specify. “Write the tests using Oban.Testing“ in the briefing produces far more usable output than “write tests for this worker”.
For integration tests involving the database, I pass the database state directly via Tidewave rather than building elaborate fixtures. It’s faster and tests the same scenario.
Phase 5: documentation and updating the .md files
I never skip this phase, even when I’m in a hurry. It’s also the phase where Claude is most useful in an underrated way.
After every significant implementation I update the files in docs/. This isn’t user documentation — it’s the context I’ll use in the next design session. If I don’t update these files, the next session starts with an outdated picture of the system, and the quality of proposals degrades accordingly.
The concrete process: I ask Claude to produce a documentation diff based on the implementation conversation. Not a rewrite — a targeted update that reflects the decisions made. This takes ten minutes and is worth considerably more.
For CLAUDE.md I’m more conservative. I update it only when something structural changes — new modules, new conventions, added dependencies. I don’t want it to become a long, disorganised document, because it’s re-read at the start of every session and signal degrades with noise.
Phase 6: release
The SlashFeed release process is manual, involving Docker, launchd, and Cloudflare Tunnel. I don’t have automated CI/CD — that’s a deliberate choice for a personal project running on an M1 Mac.
Claude is useful here in two specific ways.
Release checklist. I ask Claude to generate a release checklist based on the session’s changes. Not a generic checklist — one specific to the feature just implemented. “Before releasing, verify that migrations have been applied, that the new Oban workers are declared in application.ex, that any required environment variables are documented in env.example.” This kind of output eliminates the dumb mistakes.
Regression post-mortems. When something breaks in production — and it does — a post-mortem conversation with Claude is faster than analysing logs alone. I bring the full log, describe the expected versus observed behaviour, and work with Claude to identify the cause. With Tidewave able to query the database state in production (I have a dev instance that replicates the setup), diagnosis time halves.
What doesn’t work, and stop pretending it does
There are things Claude doesn’t do well, and pretending otherwise wastes time.
Reasoning about performance at scale. Claude can suggest Postgres indexes, optimise queries, reason about pool sizes. But it can’t predict how the system will behave under real load on specific hardware. That’s what profilers and benchmarks are for, not conversations.
Product direction decisions. “What should I build next?” is a question Claude answers plausibly but not usefully. The responses are reasonable, but they lack the context only I have — what people using the app have asked for, what seems most interesting to explore, which problem annoys me most when I use the product myself.
Coherence across long sessions on complex problems. Anthropic’s article notes that Opus 4.7 carries context across sessions more reliably than previous models. That’s true. But for problems that develop over days — a feature requiring changes across many layers — a single session isn’t enough. My system of .md files is exactly the response to this: context persists in documents, not in the model’s memory.
Debugging systemic problems. A bug that depends on a race condition, the specific behaviour of the BEAM garbage collector, or an undocumented interaction between libraries — these require deep understanding of the runtime that Claude doesn’t have. It can help formulate hypotheses, but the final diagnosis is always empirical.
On Anthropic’s model
Anthropic’s article describes a usage pattern optimised for autonomous, well-defined tasks: provide complete context in the first turn, let the model execute with auto mode, check the result. It’s an effective pattern for feature implementations where requirements are clear.
My pattern is different because my problem is often the opposite: I don’t know exactly what I want to build, or I know what I want but not how it integrates with what already exists. In these cases the value of Claude is in collaborative reasoning — not in autonomous execution.
Where I agree completely: specifying the task precisely in the first turn is the investment with the highest ROI. Every minute spent making the initial briefing precise is worth ten minutes saved in subsequent iterations. And the recommendation to treat Claude like an engineer you’re delegating to — not a pair programmer you’re guiding line by line — is right. Autonomy works when context is complete; without context, autonomy produces code that looks correct but doesn’t integrate.
The xhigh effort level that Anthropic recommends as the default for Opus 4.7 is consistent with what I see in the design phase: it responds more completely, considers more edge cases, is less likely to propose the first plausible solution. For the implementation phase on simpler tasks, lower effort levels work well and cost less. That distinction is worth making explicit in your own workflow.
The workflow, without prose
Here’s how the SlashFeed development cycle is structured.
-
Idea → conversation on Claude.ai with the relevant
.mdcontext files attached - Convergence → iterative discussion until the solution is clear and documented
- Briefing → first turn in Claude Code / Zed with complete context: relevant files, constraints, acceptance criteria
- Implementation → Claude Code produces the code, Tidewave provides live access to the app
-
Tests → manual
mix test, precise error reporting, iteration -
Documentation → update the
.mdfiles with the decisions made in the session - Release → Claude-generated checklist, manual deploy, log monitoring
Each phase uses Claude differently. Phases 1 and 2 are conversational and use Claude.ai. Phases 3, 4, and 5 are operational and use Claude Code in Zed. Phase 6 uses Claude.ai again for synthesis. The release is manual but assisted.
It’s not the most automated workflow possible. It’s the workflow that produces the best code given the time I have.
Sometimes we wrote the code, sometimes AI did. The most important is that we used the best tool for the job and every line of code was reviewed at least twice: by the author and a team member.
SlashFeed is a personal RSS reader built in Elixir/Phoenix. I’m documenting the development process here as I go.