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 locally

  • Validation 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


699_1x_shots_so.png

The key design decision is a shared contract surface:

  • packages/domain owns notification shapes, error envelopes, and shared validation

  • services/api implements behavior and tests once

  • apps/* consume the same contracts and focus on UI/framework glue

  • infra/sst centralizes 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

notification.ts (variants per app)

60–120

Schema repeated

Fields/enums drift across apps

API

notifications.tsservice.tsnotifications.test.ts

250–450

Validation + error envelope rebuilt per route-set

Status codes + error shapes differ

Next.js

NotificationBell.tsxnotifications.ts (client/hooks)

200–350

Polling, caching, unread derivations, reauth

UI state bugs, “unread” mismatches

SvelteKit

NotificationBell.sveltenotifications.ts (client/stores)

180–320

Same state machine re-expressed per framework

Edge cases missed (race/refresh/auth)

Infra

router.tsapi.tsapps.ts

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 curate to 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:

  1. System map: how the feature flows end-to-end (entry points, shared modules, state/DB boundaries).

  2. Canonical “memory entries”: the conventions that must not drift (error envelope, route contract, routing rules).

In this project, those live in:

  • docs/system-map.md

  • docs/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 + validation

  • API routes + tests (services/api) that emit the canonical error envelope

  • Next.js and SvelteKit UI glue (apps/*) that consume the same contract

  • Infra 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 locally

  • Validation 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


699_1x_shots_so.png

The key design decision is a shared contract surface:

  • packages/domain owns notification shapes, error envelopes, and shared validation

  • services/api implements behavior and tests once

  • apps/* consume the same contracts and focus on UI/framework glue

  • infra/sst centralizes 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

notification.ts (variants per app)

60–120

Schema repeated

Fields/enums drift across apps

API

notifications.tsservice.tsnotifications.test.ts

250–450

Validation + error envelope rebuilt per route-set

Status codes + error shapes differ

Next.js

NotificationBell.tsxnotifications.ts (client/hooks)

200–350

Polling, caching, unread derivations, reauth

UI state bugs, “unread” mismatches

SvelteKit

NotificationBell.sveltenotifications.ts (client/stores)

180–320

Same state machine re-expressed per framework

Edge cases missed (race/refresh/auth)

Infra

router.tsapi.tsapps.ts

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 curate to 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:

  1. System map: how the feature flows end-to-end (entry points, shared modules, state/DB boundaries).

  2. Canonical “memory entries”: the conventions that must not drift (error envelope, route contract, routing rules).

In this project, those live in:

  • docs/system-map.md

  • docs/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 + validation

  • API routes + tests (services/api) that emit the canonical error envelope

  • Next.js and SvelteKit UI glue (apps/*) that consume the same contract

  • Infra 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 locally

  • Validation 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


699_1x_shots_so.png

The key design decision is a shared contract surface:

  • packages/domain owns notification shapes, error envelopes, and shared validation

  • services/api implements behavior and tests once

  • apps/* consume the same contracts and focus on UI/framework glue

  • infra/sst centralizes 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

notification.ts (variants per app)

60–120

Schema repeated

Fields/enums drift across apps

API

notifications.tsservice.tsnotifications.test.ts

250–450

Validation + error envelope rebuilt per route-set

Status codes + error shapes differ

Next.js

NotificationBell.tsxnotifications.ts (client/hooks)

200–350

Polling, caching, unread derivations, reauth

UI state bugs, “unread” mismatches

SvelteKit

NotificationBell.sveltenotifications.ts (client/stores)

180–320

Same state machine re-expressed per framework

Edge cases missed (race/refresh/auth)

Infra

router.tsapi.tsapps.ts

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 curate to 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:

  1. System map: how the feature flows end-to-end (entry points, shared modules, state/DB boundaries).

  2. Canonical “memory entries”: the conventions that must not drift (error envelope, route contract, routing rules).

In this project, those live in:

  • docs/system-map.md

  • docs/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 + validation

  • API routes + tests (services/api) that emit the canonical error envelope

  • Next.js and SvelteKit UI glue (apps/*) that consume the same contract

  • Infra 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.