← back

Shipping an AI Journal in 12 Sessions: Trade-offs in Compliance, Context, and Vibe Coding at Speed

#ui-ux#compliance#context-management#edge-functions#ccpa

A stateless search architecture that eliminates the vector database entirely, compliance features designed as architectural patterns instead of checkboxes, and managing AI development context as production infrastructure.

Shipping an AI Journal in 12 Sessions: Trade-offs in Compliance, Context, and Vibe Coding at Speed

A 12-session sprint is not a sustainable development cadence. But it produced a ship-ready AI journaling app—complete with privacy compliance, polished UI, and a stateless search architecture that sidesteps an entire class of infrastructure complexity. This is a post-mortem on the strategic decisions, failure modes, and mental models that made that possible.

Stateless Search as an Architectural Bet

The conventional path for semantic search over user-generated content is an embeddings pipeline: vectorize entries, store them in a vector database, query by cosine similarity. It works. It also introduces a new persistence layer, a synchronization problem between your primary datastore and your vector index, and ongoing embedding costs that scale with corpus size.

I chose a different trade-off. The search implementation sends all entry summaries to Gemini in a single API call and lets the model perform relevance ranking directly. No embeddings database. No vector store. No sync jobs.

This is a deliberate constraint-driven decision. For a solo-built journaling app where per-user entry counts are bounded (hundreds, not millions), the context window is the index. The latency and token cost are acceptable. The operational complexity savings are not marginal—they eliminate an entire infrastructure dependency. The mental model here: don't build for the scale you might have; build for the architecture you can operate.

This search capability, along with chat and summarization, runs through a single unified Edge Function toggled by a mode flag. Three capabilities, one deployment surface, zero routing complexity.

Compliance as Product Architecture, Not Legal Afterthought

Two compliance features illustrate a principle I keep returning to: regulatory requirements are product architecture decisions, not checkboxes.

The age gate uses a privacy-by-design pattern. Users enter a date of birth, but the system stores nothing—only a boolean pass/fail flag persists in localStorage. This isn't just COPPA-adjacent caution; it's a data minimization decision that eliminates an entire category of liability. You cannot leak what you never stored.

The data export feature required a harder trade-off. CCPA mandates machine-readable data portability. Users expect something they can actually read. I built a dual-format export using a collector pattern—a single data-gathering pass that simultaneously generates JSON (for regulatory compliance) and PDF (for human consumption). The collector pattern matters because it scales: adding a new export format means writing a new renderer, not re-engineering the data pipeline.

The onboarding privacy disclosure uses scroll-to-accept with server-side acceptance recording. Client-side consent tracking is legally fragile. Server-side timestamps provide defensibility.

Context Management as a Core Competency

The most transferable lesson from this sprint has nothing to do with the product. It's about managing the AI development environment itself.

I maintain a canonical project instructions file—a "golden master"—that serves as the single source of truth for AI-assisted development. Over time, this file accumulated resolved decisions alongside active constraints, consuming context window capacity with information that no longer influenced outputs. The fix was straightforward: audit the file, archive resolved items to a separate document, and recover roughly a third of the working context budget.

A second optimization eliminated a surprisingly costly friction point. The golden master previously lived as a local file, requiring a manual download-edit-upload cycle for every update. Migrating it to Google Docs allowed Claude to read directly from Drive and output diffs, collapsing a multi-step workflow into a single interaction.

The higher-order lesson: in AI-assisted development, your prompt infrastructure is production infrastructure. It accumulates debt. It needs refactoring. It has performance characteristics you should measure and optimize.

Failure Modes Worth Naming

Three failure patterns from this sprint deserve explicit documentation because they are systemic, not incidental.

Credit exhaustion as a workflow cliff. Cursor's monthly AI agent credits depleted mid-session. The fallback—having Claude generate full replacement files for manual testing and deployment—works, but the productivity delta is severe. The mental model: treat AI tool credit limits as a hard resource constraint in sprint planning, not a surprise you discover at the worst possible moment.

Context fragmentation at session scale. Twelve independent chat sessions in a single day created compounding overhead. Each new session required re-establishing context. Continuation prompts and session wrap-ups consumed an increasing share of productive capacity. There is a real, measurable cost curve to session proliferation, and it compounds non-linearly.

Dark theme rendering assumptions. Multiple prompts and credits were spent attempting to create card depth using lighter backgrounds and CSS box-shadows—standard patterns that completely failed against dark-on-dark color schemes. The working solution was amber border treatments. The lesson is narrow but generalizable: AI code generation inherits the biases of its training data, which skews heavily toward light-theme design patterns. Explicitly specifying your rendering constraints saves more time than iterating through failures.

Scoped Batching as a Prompt Strategy

The UI overhaul succeeded because of a deliberate batching strategy: global systemic changes first (typography, color tokens, spacing scale), then screen-by-screen refinements. The inverse—making local changes across screens before establishing global rules—produces visual inconsistencies that require a second pass to reconcile. In prompt-driven development, the sequence of your instructions is an architectural decision.

Key Takeaways