Article
Stop Rewriting the Same Feature in Every Frontend Using OpenCode
Shipping a “simple” feature in a multi-frontend stack rarely stays simple. The same endpoint gets rebuilt for different runtime constraints, the same state machine gets reimplemented in two UI frameworks, and the same deployment wiring gets copied with small edits until drift becomes the default.
In this article, we are going to see how using ByteRover with OpenCode solves it by generating complete, convention-aligned implementations from a single request and a shared, durable context pack.

The Problem: Static Context Causes Feature Drift
In a representative setup with a serverless API and two frontends (Next.js + SvelteKit), the same feature typically lands in four places. Each implementation is “reasonable” in isolation, but the system as a whole drifts because the contract and conventions are not enforced from one place.
Common failure modes:
API contract drift: one frontend expects
unreadCount, the other recomputes locallyValidation mismatch: one implementation allows empty messages; another rejects
Error format mismatch: different envelopes break shared error handling
Deployment drift: a path/subdomain update gets applied in one place only
AI agents amplify this problem when the repository’s most important constraints live in people’s heads, Slack threads, tickets, or “that one PR”. Every new session starts from a blank slate unless context is retrievable and structured.
The Goal: Persistent Feature Context With Zero Friction
The goal is to make future agent sessions deterministic:
Contracts are explicit (routes, types, envelopes)
Conventions are enforced (naming, error mapping, auth policy)
Infra rules don’t fork per app (routing, regions, deploy ordering)
ByteRover provides the durable memory layer. OpenCode retrieves that memory before it writes code.
What We Will Build
Canonical feature: User Notifications
Create notifications (system events and user-to-user events)
List notifications with pagination
Mark as read
Unread count for a header “bell” UI
Consistent API envelope, validation, and auth
Deployment routing for both frontends plus the API
Reference architecture
The key design decision is a shared contract surface:
packages/domainowns notification shapes, error envelopes, and shared validationservices/apiimplements behavior and tests onceapps/*consume the same contracts and focus on UI/framework glueinfra/sstcentralizes routing so deployments do not fork per frontend
Prerequisites
ByteRover CLI (installed & authenticated)
OpenCode connected to ByteRover
Your normal toolchain for tests/build/deploy (Bun/Node, SST, etc.)
Step 1: The Baseline - Ship Notifications the “Normal” Way
Without a shared memory layer, teams typically rebuild the same feature logic across layers.
Layer | Example touchpoints (where edits happen) | Typical LOC / app | Duplicated concern | Drift symptom (what breaks) |
|---|---|---|---|---|
Domain |
| 60–120 | Schema repeated | Fields/enums drift across apps |
API |
| 250–450 | Validation + error envelope rebuilt per route-set | Status codes + error shapes differ |
Next.js |
| 200–350 | Polling, caching, unread derivations, reauth | UI state bugs, “unread” mismatches |
SvelteKit |
| 180–320 | Same state machine re-expressed per framework | Edge cases missed (race/refresh/auth) |
Infra |
| 80–160 | Routing rules copied/patched per app | Paths/subdomains diverge per deploy |
For a copy/paste-friendly version of this table plus canonical context snippets, see docs/duplication-ledger.md.
Step 2: Initialize the Project With ByteRover
Run the initialization command in your project root:
This generates a ByteRover config that acts as the contract between your repository and the agent: where to find context, what to index, and what should be treated as “canonical memory”.
Step 3: Curate a Context Pack (Once)
Instead of manually writing tribal-knowledge docs, delegate discovery to the agent and store the result in the repo.
In a new OpenCode/Claude Code session, use a prompt like:
First read the repo. Then run
brv curateto infer the architecture and conventions from code alone. Identify entry points, execution paths, shared contracts, and deployment rules. Write concise markdown docs that can be indexed by ByteRover. Do not change product behavior yet.
The output should include two kinds of artifacts:
System map: how the feature flows end-to-end (entry points, shared modules, state/DB boundaries).
Canonical “memory entries”: the conventions that must not drift (error envelope, route contract, routing rules).
In this project, those live in:
docs/system-map.mddocs/duplication-ledger.md
Example memory entries (stored once, retrieved forever):
ByteRover: api.errors
envelope: ok: { data: "<payload>", requestId: "<id>" } error: error: { code: "<code>", message: "<msg>", details: "<optional>" } requestId: "<id>" mapping: VALIDATION_ERROR: 400 UNAUTHORIZED: 401 FORBIDDEN: 403 NOT_FOUND: 404 CONFLICT: 409 INTERNAL: 500
ByteRover: notifications.contract
routes: - method: GET path: /v1/notifications query: { cursor?: string, limit?: number } returns: NotificationPage - method: POST path: /v1/notifications/read body: { notificationIds: string[] } returns: { updated: number } types: Notification: fields
ByteRover: infra.routing
rules: - apiPrefix: /v1/* target: api - app: web-next host: app.example.com - app: web-svelte host
With this context in ByteRover, the agent does not invent an error envelope, does not guess route naming, and does not “creatively” restructure deployment rules.
Step 4: Sync the Knowledge to ByteRover
Once curation is done, push the updated docs so they become queryable memory for future sessions:
Step 5: Execute the Context-Aware Implementation
Now you can request the feature end-to-end without re-pasting your repo into every prompt.
brv query "Implement User Notifications end-to-end (domain, API, both UIs, infra). Follow api.errors, notifications.contract, infra.routing. Generate API tests. Keep the API envelope and status code mapping canonical. Update rollout/runbook docs if any deploy ordering constraints change."Because the agent is context-aware, it generates a consistent set of artifacts across layers:
Shared domain contract (
packages/domain) as the single source of truth for types + validationAPI routes + tests (
services/api) that emit the canonical error envelopeNext.js and SvelteKit UI glue (
apps/*) that consume the same contractInfra routing (
infra/sst) applied once and reused across apps/stages
Step 6: Verify (And Capture Evidence)
Validate behavior and keep the proof close to the code.
This repo includes scripts/capture-evidence.sh to capture command output into evidence/ with timestamps and (optionally) git metadata:
scripts/capture-evidence.sh tests -- bun test scripts/capture-evidence.sh api-list -- curl -sS
Step 7: Lock In the Learnings
The code is shipped, but the job isn’t done until the “why” is saved:
Document non-obvious constraints (auth policy, idempotency, deploy ordering)
Record rejected approaches if they matter (what broke and why)
Push updated memory so future sessions inherit the constraint set
Step 8: The Payoff Deterministic Future Work
Months later, when you add notification preferences or batch operations, you don’t want drift to re-enter the system. Start with a query:
brv query "What are the canonical rules for notifications routes, error envelope, and infra routing?"ByteRover surfaces the contracts and constraints you curated, and the agent implements changes as an informed continuation rather than a fresh guess.
Conclusion: Fixing the Feature Drift Loop
Multi-frontend teams do not lose time because building features is hard. Time is lost because the same feature is rebuilt in different dialects , UI frameworks, serverless runtimes, and deployment layers, without a shared memory layer.
OpenCode turns one request into a complete, tested implementation. ByteRover keeps contracts and conventions stable so generated code matches the expected shape across stacks. The result is faster delivery, fewer drift bugs, and dramatically less time spent rewriting the same feature in every frontend.
The Problem: Static Context Causes Feature Drift
In a representative setup with a serverless API and two frontends (Next.js + SvelteKit), the same feature typically lands in four places. Each implementation is “reasonable” in isolation, but the system as a whole drifts because the contract and conventions are not enforced from one place.
Common failure modes:
API contract drift: one frontend expects
unreadCount, the other recomputes locallyValidation mismatch: one implementation allows empty messages; another rejects
Error format mismatch: different envelopes break shared error handling
Deployment drift: a path/subdomain update gets applied in one place only
AI agents amplify this problem when the repository’s most important constraints live in people’s heads, Slack threads, tickets, or “that one PR”. Every new session starts from a blank slate unless context is retrievable and structured.
The Goal: Persistent Feature Context With Zero Friction
The goal is to make future agent sessions deterministic:
Contracts are explicit (routes, types, envelopes)
Conventions are enforced (naming, error mapping, auth policy)
Infra rules don’t fork per app (routing, regions, deploy ordering)
ByteRover provides the durable memory layer. OpenCode retrieves that memory before it writes code.
What We Will Build
Canonical feature: User Notifications
Create notifications (system events and user-to-user events)
List notifications with pagination
Mark as read
Unread count for a header “bell” UI
Consistent API envelope, validation, and auth
Deployment routing for both frontends plus the API
Reference architecture
The key design decision is a shared contract surface:
packages/domainowns notification shapes, error envelopes, and shared validationservices/apiimplements behavior and tests onceapps/*consume the same contracts and focus on UI/framework glueinfra/sstcentralizes routing so deployments do not fork per frontend
Prerequisites
ByteRover CLI (installed & authenticated)
OpenCode connected to ByteRover
Your normal toolchain for tests/build/deploy (Bun/Node, SST, etc.)
Step 1: The Baseline - Ship Notifications the “Normal” Way
Without a shared memory layer, teams typically rebuild the same feature logic across layers.
Layer | Example touchpoints (where edits happen) | Typical LOC / app | Duplicated concern | Drift symptom (what breaks) |
|---|---|---|---|---|
Domain |
| 60–120 | Schema repeated | Fields/enums drift across apps |
API |
| 250–450 | Validation + error envelope rebuilt per route-set | Status codes + error shapes differ |
Next.js |
| 200–350 | Polling, caching, unread derivations, reauth | UI state bugs, “unread” mismatches |
SvelteKit |
| 180–320 | Same state machine re-expressed per framework | Edge cases missed (race/refresh/auth) |
Infra |
| 80–160 | Routing rules copied/patched per app | Paths/subdomains diverge per deploy |
For a copy/paste-friendly version of this table plus canonical context snippets, see docs/duplication-ledger.md.
Step 2: Initialize the Project With ByteRover
Run the initialization command in your project root:
This generates a ByteRover config that acts as the contract between your repository and the agent: where to find context, what to index, and what should be treated as “canonical memory”.
Step 3: Curate a Context Pack (Once)
Instead of manually writing tribal-knowledge docs, delegate discovery to the agent and store the result in the repo.
In a new OpenCode/Claude Code session, use a prompt like:
First read the repo. Then run
brv curateto infer the architecture and conventions from code alone. Identify entry points, execution paths, shared contracts, and deployment rules. Write concise markdown docs that can be indexed by ByteRover. Do not change product behavior yet.
The output should include two kinds of artifacts:
System map: how the feature flows end-to-end (entry points, shared modules, state/DB boundaries).
Canonical “memory entries”: the conventions that must not drift (error envelope, route contract, routing rules).
In this project, those live in:
docs/system-map.mddocs/duplication-ledger.md
Example memory entries (stored once, retrieved forever):
ByteRover: api.errors
envelope: ok: { data: "<payload>", requestId: "<id>" } error: error: { code: "<code>", message: "<msg>", details: "<optional>" } requestId: "<id>" mapping: VALIDATION_ERROR: 400 UNAUTHORIZED: 401 FORBIDDEN: 403 NOT_FOUND: 404 CONFLICT: 409 INTERNAL: 500
ByteRover: notifications.contract
routes: - method: GET path: /v1/notifications query: { cursor?: string, limit?: number } returns: NotificationPage - method: POST path: /v1/notifications/read body: { notificationIds: string[] } returns: { updated: number } types: Notification: fields
ByteRover: infra.routing
rules: - apiPrefix: /v1/* target: api - app: web-next host: app.example.com - app: web-svelte host
With this context in ByteRover, the agent does not invent an error envelope, does not guess route naming, and does not “creatively” restructure deployment rules.
Step 4: Sync the Knowledge to ByteRover
Once curation is done, push the updated docs so they become queryable memory for future sessions:
Step 5: Execute the Context-Aware Implementation
Now you can request the feature end-to-end without re-pasting your repo into every prompt.
brv query "Implement User Notifications end-to-end (domain, API, both UIs, infra). Follow api.errors, notifications.contract, infra.routing. Generate API tests. Keep the API envelope and status code mapping canonical. Update rollout/runbook docs if any deploy ordering constraints change."Because the agent is context-aware, it generates a consistent set of artifacts across layers:
Shared domain contract (
packages/domain) as the single source of truth for types + validationAPI routes + tests (
services/api) that emit the canonical error envelopeNext.js and SvelteKit UI glue (
apps/*) that consume the same contractInfra routing (
infra/sst) applied once and reused across apps/stages
Step 6: Verify (And Capture Evidence)
Validate behavior and keep the proof close to the code.
This repo includes scripts/capture-evidence.sh to capture command output into evidence/ with timestamps and (optionally) git metadata:
scripts/capture-evidence.sh tests -- bun test scripts/capture-evidence.sh api-list -- curl -sS
Step 7: Lock In the Learnings
The code is shipped, but the job isn’t done until the “why” is saved:
Document non-obvious constraints (auth policy, idempotency, deploy ordering)
Record rejected approaches if they matter (what broke and why)
Push updated memory so future sessions inherit the constraint set
Step 8: The Payoff Deterministic Future Work
Months later, when you add notification preferences or batch operations, you don’t want drift to re-enter the system. Start with a query:
brv query "What are the canonical rules for notifications routes, error envelope, and infra routing?"ByteRover surfaces the contracts and constraints you curated, and the agent implements changes as an informed continuation rather than a fresh guess.
Conclusion: Fixing the Feature Drift Loop
Multi-frontend teams do not lose time because building features is hard. Time is lost because the same feature is rebuilt in different dialects , UI frameworks, serverless runtimes, and deployment layers, without a shared memory layer.
OpenCode turns one request into a complete, tested implementation. ByteRover keeps contracts and conventions stable so generated code matches the expected shape across stacks. The result is faster delivery, fewer drift bugs, and dramatically less time spent rewriting the same feature in every frontend.
The Problem: Static Context Causes Feature Drift
In a representative setup with a serverless API and two frontends (Next.js + SvelteKit), the same feature typically lands in four places. Each implementation is “reasonable” in isolation, but the system as a whole drifts because the contract and conventions are not enforced from one place.
Common failure modes:
API contract drift: one frontend expects
unreadCount, the other recomputes locallyValidation mismatch: one implementation allows empty messages; another rejects
Error format mismatch: different envelopes break shared error handling
Deployment drift: a path/subdomain update gets applied in one place only
AI agents amplify this problem when the repository’s most important constraints live in people’s heads, Slack threads, tickets, or “that one PR”. Every new session starts from a blank slate unless context is retrievable and structured.
The Goal: Persistent Feature Context With Zero Friction
The goal is to make future agent sessions deterministic:
Contracts are explicit (routes, types, envelopes)
Conventions are enforced (naming, error mapping, auth policy)
Infra rules don’t fork per app (routing, regions, deploy ordering)
ByteRover provides the durable memory layer. OpenCode retrieves that memory before it writes code.
What We Will Build
Canonical feature: User Notifications
Create notifications (system events and user-to-user events)
List notifications with pagination
Mark as read
Unread count for a header “bell” UI
Consistent API envelope, validation, and auth
Deployment routing for both frontends plus the API
Reference architecture
The key design decision is a shared contract surface:
packages/domainowns notification shapes, error envelopes, and shared validationservices/apiimplements behavior and tests onceapps/*consume the same contracts and focus on UI/framework glueinfra/sstcentralizes routing so deployments do not fork per frontend
Prerequisites
ByteRover CLI (installed & authenticated)
OpenCode connected to ByteRover
Your normal toolchain for tests/build/deploy (Bun/Node, SST, etc.)
Step 1: The Baseline - Ship Notifications the “Normal” Way
Without a shared memory layer, teams typically rebuild the same feature logic across layers.
Layer | Example touchpoints (where edits happen) | Typical LOC / app | Duplicated concern | Drift symptom (what breaks) |
|---|---|---|---|---|
Domain |
| 60–120 | Schema repeated | Fields/enums drift across apps |
API |
| 250–450 | Validation + error envelope rebuilt per route-set | Status codes + error shapes differ |
Next.js |
| 200–350 | Polling, caching, unread derivations, reauth | UI state bugs, “unread” mismatches |
SvelteKit |
| 180–320 | Same state machine re-expressed per framework | Edge cases missed (race/refresh/auth) |
Infra |
| 80–160 | Routing rules copied/patched per app | Paths/subdomains diverge per deploy |
For a copy/paste-friendly version of this table plus canonical context snippets, see docs/duplication-ledger.md.
Step 2: Initialize the Project With ByteRover
Run the initialization command in your project root:
This generates a ByteRover config that acts as the contract between your repository and the agent: where to find context, what to index, and what should be treated as “canonical memory”.
Step 3: Curate a Context Pack (Once)
Instead of manually writing tribal-knowledge docs, delegate discovery to the agent and store the result in the repo.
In a new OpenCode/Claude Code session, use a prompt like:
First read the repo. Then run
brv curateto infer the architecture and conventions from code alone. Identify entry points, execution paths, shared contracts, and deployment rules. Write concise markdown docs that can be indexed by ByteRover. Do not change product behavior yet.
The output should include two kinds of artifacts:
System map: how the feature flows end-to-end (entry points, shared modules, state/DB boundaries).
Canonical “memory entries”: the conventions that must not drift (error envelope, route contract, routing rules).
In this project, those live in:
docs/system-map.mddocs/duplication-ledger.md
Example memory entries (stored once, retrieved forever):
ByteRover: api.errors
envelope: ok: { data: "<payload>", requestId: "<id>" } error: error: { code: "<code>", message: "<msg>", details: "<optional>" } requestId: "<id>" mapping: VALIDATION_ERROR: 400 UNAUTHORIZED: 401 FORBIDDEN: 403 NOT_FOUND: 404 CONFLICT: 409 INTERNAL: 500
ByteRover: notifications.contract
routes: - method: GET path: /v1/notifications query: { cursor?: string, limit?: number } returns: NotificationPage - method: POST path: /v1/notifications/read body: { notificationIds: string[] } returns: { updated: number } types: Notification: fields
ByteRover: infra.routing
rules: - apiPrefix: /v1/* target: api - app: web-next host: app.example.com - app: web-svelte host
With this context in ByteRover, the agent does not invent an error envelope, does not guess route naming, and does not “creatively” restructure deployment rules.
Step 4: Sync the Knowledge to ByteRover
Once curation is done, push the updated docs so they become queryable memory for future sessions:
Step 5: Execute the Context-Aware Implementation
Now you can request the feature end-to-end without re-pasting your repo into every prompt.
brv query "Implement User Notifications end-to-end (domain, API, both UIs, infra). Follow api.errors, notifications.contract, infra.routing. Generate API tests. Keep the API envelope and status code mapping canonical. Update rollout/runbook docs if any deploy ordering constraints change."Because the agent is context-aware, it generates a consistent set of artifacts across layers:
Shared domain contract (
packages/domain) as the single source of truth for types + validationAPI routes + tests (
services/api) that emit the canonical error envelopeNext.js and SvelteKit UI glue (
apps/*) that consume the same contractInfra routing (
infra/sst) applied once and reused across apps/stages
Step 6: Verify (And Capture Evidence)
Validate behavior and keep the proof close to the code.
This repo includes scripts/capture-evidence.sh to capture command output into evidence/ with timestamps and (optionally) git metadata:
scripts/capture-evidence.sh tests -- bun test scripts/capture-evidence.sh api-list -- curl -sS
Step 7: Lock In the Learnings
The code is shipped, but the job isn’t done until the “why” is saved:
Document non-obvious constraints (auth policy, idempotency, deploy ordering)
Record rejected approaches if they matter (what broke and why)
Push updated memory so future sessions inherit the constraint set
Step 8: The Payoff Deterministic Future Work
Months later, when you add notification preferences or batch operations, you don’t want drift to re-enter the system. Start with a query:
brv query "What are the canonical rules for notifications routes, error envelope, and infra routing?"ByteRover surfaces the contracts and constraints you curated, and the agent implements changes as an informed continuation rather than a fresh guess.
Conclusion: Fixing the Feature Drift Loop
Multi-frontend teams do not lose time because building features is hard. Time is lost because the same feature is rebuilt in different dialects , UI frameworks, serverless runtimes, and deployment layers, without a shared memory layer.
OpenCode turns one request into a complete, tested implementation. ByteRover keeps contracts and conventions stable so generated code matches the expected shape across stacks. The result is faster delivery, fewer drift bugs, and dramatically less time spent rewriting the same feature in every frontend.