From 60 Repos to One: How We Simplified Development with a Monorepo at Lynes

Tech

We moved from 60+ repos to a monorepo. The result: fewer bugs, faster development, simpler code reviews, and a clear structure that makes the team both more efficient and happier.

From 60 Repos to One: How We Simplified Development with a Monorepo at Lynes
From 60 Repos to One: How We Simplified Development with a Monorepo at Lynes

At Lynes, we’re building communication and productivity tools with a modern stack: Node.js + TypeScript on the backend and TypeScript on the frontend. Like many teams, we started small — a couple of services, a few shared utilities, and a handful of developers. But over time, things grew.

Before we knew it, we had:

  • 60+ separate Git repositories
  • 20+ shared modules (published to private npm)
  • A growing mess of version mismatches, duplicated effort, and broken contracts between services

So we made a big change. We moved everything into one monorepo.

And it’s been a game-changer.

Why Our Old Setup Was Holding Us Back

When each service had its own repo and shared modules were published independently, development slowed down — a lot. Some of the problems we ran into:

  • Version mismatches everywhere: One service would be on v1.2.0 of a shared module, another on v1.2.3. Something would break, and we’d be stuck debugging a ghost bug.
  • Publishing was painful: Fixing a bug in a shared module meant bumping the version, publishing to npm, updating the dependency in each repo, and testing manually. A lot of overhead.
  • Developers avoided shared changes: Because the process was tedious and risky, devs often hesitated to touch shared code — even when they knew something was wrong.
  • Disjointed work: Adding a new feature that touched multiple services often meant creating 5–10 pull requests across different repos. Coordinating and reviewing those was a nightmare.

Moving to a Monorepo (Without Fancy Tools)

We didn’t use anything like Nx or Turborepo. Instead, we:

  • Created a single GitHub repo
  • Migrated services into subfolders (one at a time, over about 6 months)
  • Wrote our own custom scripts for linting, testing, and CI workflows

The hardest part? Getting symlinks to play nicely across TypeScript, Jest, lint, and Docker. Each tool had its own quirks when dealing with symlinked files — from module resolution issues to mismatched path errors. But once we cracked that setup, everything else fell into place.

How It Works Today

Every service lives in a folder inside the monorepo.

We have folders like:

/services/service-a
/services/service-b
/sharedModules
/sharedTypes

During development, services reference shared modules and shared types using symlinks.

During CI or Docker build, our custom scripts:

  • Replace the symlinks by copying the actual files into the Docker build context
  • Use Git history to detect what shared modules changed
  • Scan source files to see which services use them
  • Run unit tests on only the affected services

This gives us the flexibility of symlinks locally and the stability of actual code copies in builds.

One Type to Rule Them All

Type safety is a huge win for us. We use the same TypeScript types across the backend and frontend.

These are stored in a shared folder, symlinked during development, and copied into each service at build time. So when a type changes:

  • Every service and frontend that uses it will instantly fail if something breaks
  • The developer gets immediate feedback before merging

This completely eliminates those subtle bugs where a service response changed but the frontend still assumed the old shape.

Pull Requests That Actually Make Sense

This part is huge: We can now make one pull request per feature.

Before, adding a new feature that touched multiple services could require 10+ pull requests across 10 different repos. Reviewing them in the right order, syncing approvals, and making sure it all worked together was exhausting.

Now:

  • All related changes go in one PR
  • Reviewers can see the full picture
  • Testing is much easier

It’s drastically improved code reviews and reduced bugs slipping through the cracks.

Shared Linting, Testing, and Rules

Another underrated win: we now have one unified linting and test config.

All services follow the same ESLint rules, TypeScript config, and Jest setup. It keeps our codebase consistent and saves tons of setup time for new services or developers.

No more guessing which service uses which rules or trying to remember which config file to update.

The Payoff

Here’s what we’ve gained from the move:

✅ Fewer bugs

✅ Shared code is actually used and maintained

✅ Type changes break early and visibly — not in production

✅ Faster development and testing

✅ Easier code reviews

✅ Happier devs

We don’t track hard metrics like bug counts or cycle time, but it’s obvious to everyone on the team that we’re moving faster with fewer headaches.

What We’d Do Differently

Honestly? We’d go monorepo from the start.

Back when we began, we had only 2–3 services and no TypeScript, so the multi-repo model made sense. But as soon as things grew and shared logic increased, the pain of split repos quickly outweighed any benefit.

If you’re starting a project now, and you even think you might share code across services, save yourself the trouble — start with a monorepo.

Final Thoughts: Should You Go Monorepo?

If you:

  • Have shared logic or types across services
  • Use TypeScript
  • Want consistent tooling
  • Are tired of managing 20+ repos

…then yes, we highly recommend it.

And you don’t need fancy tools or a huge team. With some custom scripts and clear structure, you can get 90% of the value without adding complexity.

Got questions or want to learn more about our setup at Lynes? Feel free to reach out — we’re always happy to share ideas!

Written by

David Erenger

David är en vass kodare som älskar långdistanslöpning, fokus och uthållighet präglar allt han gör.

Contact us

Collaboration tool or a phone system? Lynes is both.

Lynes is not just a great collaboration tool for your business. Or a awesome phone system. Lynes are both. It allows you to hold video meetings, receive calls, chat with colleagues and customers and share documents - all in the same workflow.

A selection of our customers

Svensk fastisghetsförmedling logoSvensk fastisghetsförmedling logo
Renta logo Renta logo
SwedolSwedol
FastighetsbyrånFastighetsbyrån
Linköping UniversitetLinköping Universitet
GeberitGeberit
Skåne MejerierSkåne Mejerier
No items found.
No items found.
No items found.

Interested? Talk with us today.

The form only appears on the live page

The form only appears on the live page