Organizing Code by Feature: How Colocation Makes Your Codebase Easier to Work With
Most developers have worked on a project where the files are split into folders like /components, /styles, /api, and /utils. At first, it looks organized. But over time, you find yourself jumping back and forth across the codebase just to change one thing. It gets harder to follow, and harder to onboard new people. There’s a better way: colocation. Colocation means keeping related files close together — not based on their type but based on what they belong to. If a component, its styles, its API logic, and its tests all relate to the same feature, then they should live in the same folder. This idea can be applied to both frontend and backend development, and it’s a small shift that makes a big difference. What does colocation mean in practice? In most frontend projects you would see this folder structure: /components /styles /api But with colocation you would do this: /checkout CheckoutForm.tsx checkout.api.ts checkout.styles.css CheckoutForm.test.tsx The benefit is simple: When you’re working on the checkout form, everything you need is right there. You don’t have to remember where the matching API call is or which style file affects the component. It’s all in one place. So, how does colocation work across the different aspects of development we touch every day? Let’s break it down. 1. Component + Styles Together Instead of separating .tsx (or .js) and .css files into different folders, keep them side by side. For example: /product ProductCard.tsx ProductCard.module.css Now, when you change the designs, the styles are right next to it. No more digging around in a /styles folder. If you’re using Tailwind or styled-components, the principle still applies — just keep style logic in the same file or folder. 2. Component + API Logic Together Say your component needs to fetch product data. Instead of importing a function from some distant api.ts, just colocate the API logic: /product ProductCard.tsx useProductData.ts Now anyone looking at the component can also see how data is fetched. You reduce the back-and-forth guessing of “where does this data come from?” 3. Component + Tests Together Tests are most useful when they’re easy to find and update. Instead of a top-level __tests__ folder, do this: /product ProductCard.tsx ProductCard.test.tsx When the test lives next to the component, you can’t pretend it doesn’t exist—so it actually gets updated. 4. Component + State Logic Together If your component has a local store or uses Zustand, Jotai, or Context, keep it nearby: /cart Cart.tsx cart.store.ts This helps everyone see how the component is wired, without having to jump into a central “store” folder that holds state for the whole app. Using Redux? You can still colocate! With Redux Toolkit, create a feature slice in the same folder: /cart Cart.tsx cartSlice.ts selectors.ts Then wire the slice into your root store (or dynamically inject it if your setup supports code-splitting). The key: if someone edits Cart.tsx, the related reducer logic is right there. Colocation Works on the Backend Too This isn’t just for frontend. The same idea helps on the backend — especially in large codebases or service-oriented setups. Here’s a typical backend project structure: /controllers /services /models It seems logical, but if you want to make a change to the billing feature, you’ll need to touch a file in each of these folders. That’s a lot of unnecessary switching. Instead, organize it like this: /features /billing billing.controller.ts billing.service.ts billing.model.ts Now billing is self-contained. Developers can focus on billing without needing to understand the entire app. This kind of structure also makes it easier to break features into microservices later, if needed. Final Thoughts Colocation is a simple concept: group code by feature, not by type. It reduces the amount of time spent hunting for files and makes projects more approachable. If your team is struggling with a growing codebase, try organizing by feature. It’s a change that can have a big impact on productivity and developer happiness. Discover how we apply this strategy at Ambitious Solutions to help teams deliver cleaner, faster, and more scalable software.