Skip to main content

Command Palette

Search for a command to run...

EOW Dev Log #1: Circular Dependency Nightmare & Colocation Wins πŸš€

Updated
β€’3 min read
EOW Dev Log #1: Circular Dependency Nightmare & Colocation Wins πŸš€
A
Software engineer and developer educator with over four years of experience building production-grade digital products for platforms serving millions of developers globally. Software engineer at Hashnode & Bug0

This week, I faced circular dependency issues head-on while working on my bootstrapped real estate startup project.

A deep dive into feature colocation architecture revealed some unexpected blind spots.

Here’s how it went:

πŸ“‚ What is Feature Colocation?

Feature colocation means grouping related files inside feature-specific folders rather than scattering them across different directories.

This keeps:

  • Business logic close to UI & API calls

  • Code more maintainable & scalable

After reading TkDodo’s article on Colocation (where he also referenced Kent C. Dodds’ take on colocation), I structured my project like this:

/features/property
  β”œβ”€β”€ server.ts
  β”œβ”€β”€ client.ts
  β”œβ”€β”€ consts.ts
  β”œβ”€β”€ model.ts
  β”œβ”€β”€ types.ts
  β”œβ”€β”€ components/
  β”œβ”€β”€ hooks/

All logic flows through these feature folders. Seemed perfect β€” until it wasn’t.

πŸ›‘ Preventing Circular Dependencies

To stay ahead of circular dependency issues from the start, I:

However… neither of them warned me. Not even once.

🚨 The Issue Appears

Everything seemed fine β€” until I pushed to GitHub after working on multiple features. πŸš€

  • The PR triggered a Vercel deployment…

  • Boom πŸ’₯ A build error: Invalid ESLint rule.

  • Turns out, my ESLint plugins config was wrong (I used an array instead of an object). πŸ˜…

πŸ” The Real Problem: Circular Dependencies

After fixing ESLint, I ran pnpm build locally… and guess what?

  • Over 100 files had circular dependency issues! πŸ˜΅β€πŸ’«

  • I knew circular deps existed in theory but never had to deal with them at this scale (100+ files).

Below is what the circular dependency error looks like after running pnpm build:

🧐 What Caused It?

My feature assets β€” like consts.ts, types.ts, model.ts β€” were cross-importing each other, causing a loop.

Example:

// consts.ts
import { SomeType } from "./types";

// types.ts
import { SOME_CONST } from "./consts";

// model.ts
import { SomeType } from "./types";
import { SOME_CONST } from "./consts";

This caused a self-referencing loop, breaking the build.

πŸ› οΈ The Fix

I enforced a strict import hierarchy:

types.ts ➑️ consts.ts ➑️ model.ts

// βœ… Define types first; they should not import from consts (types.ts)
// βœ… Constants can import types but not models (consts.ts)
// βœ… Models can import both types and constants (model.ts)
  1. 🚫 No mutual imports!

  2. πŸ’‘ Extract mutual imports into a dedicated module to break cycles.

  3. ➑️ Clear, one-directional data flow.

After implementing the above fixes, the project finally builds successfully locally βœ…

β€” And on Vercel

In total, I made changes to 147 files to resolve the circular dependency issue.

🎯 Lessons Learned

  • Feature colocation reduces mental load & makes refactoring easier.

  • Always validate ESLint rule configurations before trusting them

  • Prototype errors early to verify expected behavior.

  • Push more often β€” frequent commits & early deployments help catch issues sooner.

    • Vercel catches what local dev setups might miss β€” deploy early to uncover hidden problems.

πŸ’‘ Final Thoughts

Despite the struggles, colocation made my architecture cleaner & more scalable. I’d still choose it again for future projects.

Have you ever dealt with circular dependencies at scale? How did you fix them? πŸ‘€

Share you thoughts.