Gurmandeep SinghGurmandeep Singh

How I Let My Non-Tech PM Contribute Code Using Replit (And Kept Our Repos in Sync)

11 min read

Originally published on Medium.

How I Let My Non-Tech PM Contribute Code Using Replit (And Kept Our Repos in Sync)

Recently, something interesting happened at work.

My product manager, a completely non-technical person, started using Replit.

He loved the experience. He could describe what he wanted, make a change, and see it appear in a running application. No local setup. No long development cycle just to test a small idea.

Then one day, he said:

“I want to contribute to our web app. Like a developer.”

My first reaction was: okay, that sounds risky.

But after thinking about it, I saw the opportunity.

If my PM could make small UI changes, update text, tweak layouts, and experiment with ideas directly in Replit, it could help us move faster. He could show me what he meant instead of writing long tickets or explaining every idea through screenshots.

The challenge was not whether Replit could help him make changes.

The real challenge was this:

How could I let a non-technical PM contribute without bypassing code review, CI/CD, or deployment control?

Our Existing Setup

Our product was split into two parts:

  • A frontend application that consumed APIs
  • A backend application that served those APIs

Like many teams, we had two separate repositories:

frontend-repo
backend-repo

This setup already worked well for developers.

Each repository had its own codebase, workflow, and CI/CD pipeline. I knew where to make changes, how to review them, and how they moved to production.

I did not want to throw that away just because I wanted to give my PM a better way to experiment.

But Replit works best when it can see the full application in one workspace. For the Replit Agent to understand the project and run it properly, the frontend and backend needed to live together.

That meant we needed a monorepo.

Creating a Replit-Friendly Monorepo

I created a new repository in Bitbucket with a simple structure:

monorepo/
├── frontend/
│ └── all frontend code
└── backend/
 └── all backend code

I copied the frontend code into frontend/ and the backend code into backend/.

The monorepo was not meant to replace our existing frontend and backend repositories overnight.

It existed for one reason: to give Replit a single place where it could understand and run the complete application.

Once the monorepo was connected to Replit, the PM could work in one workspace instead of trying to understand two repositories, separate environments, and different application startup processes.

Letting Replit Run the Application

After connecting the monorepo, I asked Replit Agent to serve the frontend from the frontend/ folder and connect it to the backend.

To my surprise, it worked.

Replit Agent figured out how to serve the frontend and connect it to the backend API. After a little verification, the application was running inside Replit.

That was the moment the workflow started to feel useful.

The PM could open the project, make a small change, and see the result immediately in a working application.

But now we had a bigger problem.

The PM was working in the monorepo, while developers were still working in the original repositories.

Developers:
frontend-repo
backend-repo
 
PM:
monorepo

These repositories were separate.

If the PM made a change in Replit, developers would not automatically see it in the frontend or backend repository.

And if developers pushed changes to the original repositories, the PM would not automatically see those updates in Replit.

We needed a bridge between them.

One Dedicated Branch for PM Changes

I did not want PM changes going directly into the monorepo’s main branch.

So I created one dedicated branch for PM changes:

pm-changes

The PM did not need to worry about Git branches, pull requests, merge conflicts, or pipelines. He simply worked in Replit and made changes to the application.

Those changes went into the pm-changes branch.

Developers remained responsible for the engineering workflow:

  • Keeping pm-changes updated with the latest changes from the monorepo’s main branch
  • Resolving conflicts when they happened
  • Reviewing the PM’s changes
  • Creating or updating the pull request into main
  • Merging the changes after review

This setup worked because we had one PM and one important rule:

He worked on only one task at a time.

He did not start another task until the current change had gone through review, been deployed, and reached production.

That kept the shared pm-changes branch manageable. There were no overlapping PM tasks, no confusing handoffs, and fewer chances for conflicts.

Keeping the PM Branch in Sync

Developers continued merging changes into the monorepo’s main branch while the PM worked in Replit.

That meant pm-changes could become outdated.

Before merging the PM’s work, a developer brought the latest changes from main into pm-changes.

If there were merge conflicts, the developer resolved them manually.

The process looked like this:

PM makes changes in Replit
 ↓
Changes go to pm-changes
 ↓
Developer brings latest monorepo main into pm-changes
 ↓
Developer resolves conflicts, if needed
 ↓
Developer creates or updates PR:
pm-changes → monorepo main
 ↓
Developer reviews and merges

I did not try to automate conflict resolution.

For example, the PM might update a header component in Replit while a developer refactors that same component in the frontend repository. A pipeline cannot reliably decide which version represents the correct product and engineering decision.

Automation can move code.

Developers still need to decide how conflicting changes should be merged.

Syncing Monorepo Changes to the Original Repositories

Once the PM’s change was reviewed and merged into the monorepo’s main branch, the next step was syncing it back to the original repositories.

The monorepo contained two main folders:

frontend/
backend/

Whenever the monorepo’s main branch received new changes, a pipeline checked which folders were affected.

  • If the change was inside frontend/, it synced the change to the frontend repository.
  • If the change was inside backend/, it synced the change to the backend repository.
  • If both folders changed, it synced both repositories.

At first, the obvious approach was to create a new pull request for every sync.

That would work, but it would quickly become noisy.

A small Replit change could create a new PR. After a few tasks, developers could end up with many small sync pull requests across both repositories.

So I used fixed dedicated sync branches instead.

Fixed Sync Branches Instead of PR Spam

I created two long-lived branches:

sync/monorepo-to-frontend
sync/monorepo-to-backend

Whenever the monorepo’s main branch received new changes, the pipeline checked which folder had changed.

If *frontend/ *changed, the pipeline updated:

sync/monorepo-to-frontend

If backend/ changed, it updated:

sync/monorepo-to-backend

If a pull request was already open, the pipeline pushed new changes to the same sync branch and updated the existing PR.

If there was no open pull request, it created one.

The workflow looked like this:

Monorepo main updated
 ↓
Pipeline checks changed folders
 ↓
Frontend changed?
 ↓
Update sync/monorepo-to-frontend
 ↓
Create or update frontend PR

The backend followed the same pattern.

This kept the process clean.

Instead of creating many random branches and pull requests, developers always knew where to look for pending synced changes.

sync/monorepo-to-frontend
sync/monorepo-to-backend

The automation stayed simple, and the review process remained predictable.

Syncing Developer Changes Back to the Monorepo

The reverse flow also mattered.

Developers continued working in the original frontend and backend repositories. Their changes needed to reach the monorepo so the PM could work with the latest code in Replit.

Otherwise, the PM could be editing an outdated version of the application.

So when a developer merged changes into the frontend repository, the frontend pipeline synced those changes into the monorepo’s frontend/ folder.

The same happened for backend changes.

Developer merges change into frontend/backend repository
 ↓
Pipeline detects the change
 ↓
Change is synced into the monorepo
 ↓
Replit pulls the latest changes
 ↓
PM sees the updated application

This kept the Replit workspace useful instead of allowing it to become a stale copy of the codebase.

Preventing Pipeline Loops

Bidirectional syncing introduces one major risk: pipeline loops.

Imagine this:

Developer pushes to frontend repository
 ↓
Pipeline syncs the change to monorepo
 ↓
Monorepo pipeline detects the change
 ↓
It tries to sync the change back to frontend repository
 ↓
Frontend pipeline detects another change
 ↓
The loop continues

That would create unnecessary builds, repeated commits, and a lot of confusion.

To prevent this, I used [skip ci] in commit messages created by automatic sync pipelines.

For example:

sync frontend changes [skip ci]

This told the CI system not to start another pipeline for that sync commit.

It was a small detail, but it prevented a potentially expensive and frustrating problem.

What Happened When Conflicts Occurred?

Conflicts could still happen.

Developers were working in the frontend and backend repositories, while the PM was working through Replit in the monorepo. Sometimes both sides could change the same file.

When that happened, the pipeline did not try to magically fix it.

Instead, a developer reviewed the changes and resolved the conflict manually.

Conflicts usually happened in one of these places:

  1. When updating pm-changes with the latest monorepo main branch
  2. When syncing monorepo changes into the frontend or backend repository
  3. When reviewing a pull request before merging

This was the right balance for us.

Automation handled the repetitive syncing work.

Humans handled the decisions.

The Final Workflow

Here is the full workflow:

PM works on one task in Replit
 ↓
Changes go to pm-changes in the monorepo
 ↓
Developer updates pm-changes with the latest monorepo main
 ↓
Developer resolves conflicts, if needed
 ↓
Developer reviews and merges pm-changes into monorepo main
 ↓
Pipeline checks which folders changed
 ↓
Pipeline updates fixed sync branches in frontend/backend repositories
 ↓
Pipeline creates or updates downstream pull requests
 ↓
Developers review and merge the changes
 ↓
Changes are deployed to production
 ↓
PM starts the next task

This gave us a controlled way to let a non-technical PM contribute without giving up code review, safety, or deployment control.

What I Learned

Replit can be useful for non-developers

Replit made it possible for a non-technical PM to contribute small changes without setting up a full local development environment.

That was powerful.

The PM could experiment with an idea, see it in the application, and communicate the intended result much more clearly.

A simple monorepo was enough

For this use case, I did not need complex monorepo tooling.

A simple structure was enough:

frontend/
backend/

The goal was not to rebuild our entire architecture.

The goal was to create one Replit-friendly workspace where the PM could work with the complete application.

CI/CD became the bridge

The pipelines became the bridge between our existing repositories and the Replit-friendly monorepo.

They kept the repositories synchronized while allowing the development team to continue using its existing workflow.

Pull requests were still essential

Even though the PM’s changes started in Replit, they still went through pull requests and developer review.

That gave us visibility, accountability, and control.

The PM could contribute quickly. Developers still protected quality and production stability.

Fixed sync branches reduced noise

Using fixed sync branches prevented pull-request spam.

Instead of creating many temporary branches, developers always knew where synced changes would appear.

That made the workflow easier to review and easier to maintain.

Automation needed safety checks

This setup only worked because I added safeguards:

  • Dedicated branches
  • Path-based sync logic
  • Fixed sync branches
  • Pull-request reviews
  • Manual conflict resolution
  • [skip ci] protections
  • One PM task at a time

Without those controls, bidirectional syncing could become difficult to manage.

Final Thoughts

This setup was not about turning my product manager into a full-time developer.

It was about giving him a safe way to contribute small changes, experiment with product ideas, and communicate more clearly with engineering.

Replit made that possible.

The monorepo gave Replit a complete application to work with.

CI/CD pipelines kept the monorepo, frontend repository, and backend repository synchronized.

And pull requests ensured that developers remained responsible for review, safety, and deployment.

Was the setup perfect? No.

Could conflicts happen? Yes.

Did bidirectional syncing add complexity? Definitely.

But for one PM working on one task at a time, it became a practical solution.

The biggest win was not that the PM could make code changes.

The biggest win was that he could turn a product idea into something visible, testable, and reviewable, without slowing down the engineering team.

Sometimes the best engineering solutions are not only about code.

They are about making collaboration easier.