feature slicing

April 26, 2026

John Smith

Stop Building Layers — Start Thinking In Feature Slicing

You ship a small feature. It should take a day. Three days later, you’re still hopping between controllersservicesrepositoriesmappersdtos, and validators. You touched 12 files to change one button. Sound familiar?

That’s layer-based architecture doing its job — unfortunately. Layers organize code by technical role. Feature slicing organizes code by business value. When you stop building layers and start thinking in feature slicing, you stop paying the “where does this go?” tax on every single change.

This shift isn’t just cleaner code. It’s faster delivery, safer refactors, happier onboarding, and codebases that actually scale with your team. Let’s break down why layers fail at scale, what feature slicing looks like in practice, and how to migrate without burning everything down.

ALSO READ: Finding Your Voice: A Slam Poetry Sample To Spark Inspiration

What Is Feature Slicing Really?

Feature slicing is an architecture style where you group code by user-facing features, not by technical type. Instead of a horizontal layer cake, you get vertical slices that own everything they need to work.

Layer-based structure looks like this:

Code

/src  /controllers    UserController.ts    PaymentController.ts  /services    UserService.ts    PaymentService.ts  /repositories    UserRepository.ts    PaymentRepository.ts  /models    User.ts    Payment.ts

8 lines hidden

Feature-sliced structure looks like this:

Code

/src  /features    /user-profile      UserProfilePage.tsx      updateAvatar.ts      userProfileApi.ts      userProfile.types.ts      userProfile.test.ts    /checkout      CheckoutPage.tsx      calculateTax.ts      paymentGateway.ts      checkout.types.ts      checkout.test.ts  /shared    Button.tsx    formatCurrency.ts

12 lines hidden

In feature slicing, the user-profile folder has its UI, business logic, API calls, types, and tests. If you delete the feature, you delete one folder. No hunting across 6 directories to clean up dead code.

The core idea: high cohesion, low coupling. Code that changes together, lives together.

Layers vs Slices: The Mindset Shift

Layer-Based ThinkingFeature-Slice Thinking
“Where does a Repository go?”“Where does User Onboarding go?”
Organized by how it worksOrganized by what it does for the user
Changes span many foldersChanges stay in one folder
Reuse through shared layersReuse through explicit shared modules
Easy to start, hard to maintainSlightly more thought upfront, scales gracefully
“Framework-first” structure“Product-first” structure

Layers optimize for the first 3 months. Feature slices optimize for years 2 through 5.

Why Layers Break Down At Scale

Layers feel logical when you start. MVC, Clean Architecture, Hexagonal — they all push you toward technical separation. But here’s what happens at 100k+ LOC:

The Ripple Edit Problem

To add one field to user signup, you touch:

  1. Migration file
  2. Model/Entity
  3. DTO
  4. Validator
  5. Repository query
  6. Service logic
  7. Controller
  8. Frontend form
  9. Frontend types
  10. Tests for 3 layers

That’s 10 files, 3 merge conflicts, and 2 hours of context switching. Feature slicing puts steps 2-9 in one folder. Your brain stays in “signup” mode instead of “backend → frontend → database” mode.

False Reuse Kills You

Layers encourage UserService to become a god object. “Oh, the checkout feature needs user data? Just inject UserService.” Now UserService has 27 methods and checkout breaks when you change the profile page.

Feature slices flip this: duplication is preferred over the wrong abstraction. It’s okay if both checkout and user-profile format a user’s name. When requirements diverge, you won’t unravel a shared service.

Onboarding Becomes Archaeology

New dev: “Where do I add Stripe webhook handling?”
Senior dev: “So you’ll need to add a route in routes/webhooks.ts, then a controller method, which calls WebhookService, which uses StripeClient from infrastructure, and emits an event that PaymentService listens to…”

With slices: “Everything for webhooks is in features/stripe-webhooks. Start there.”

Dead Code Hides In Layers

Delete a feature in a layered app and you’ll miss 3 utility functions in services/utils, 1 mapper, and a DTO. They sit there for 4 years. Feature slices make dead code obvious: if the folder is gone, the feature is gone.

The Core Principles Of Feature Slicing

You can’t just rename folders and call it a day. Real feature slicing follows a few rules:

Vertical Before Horizontal

A slice should be runnable and testable on its own. If you had to demo just the checkout feature, you could. That means each slice owns its:

  • UI components
  • State management
  • API clients
  • Business rules
  • Types/interfaces
  • Tests

Horizontal layers still exist, but they’re thin and at the bottom. Think shared/date.ts not services/.

Dependencies Point Inward

Features can depend on shared, but shared cannot depend on features. Features shouldn’t depend on each other either. If checkout needs something from user-profile, that thing probably belongs in shared or needs to be duplicated.

This stops spaghetti. Your dependency graph becomes a simple tree, not a tangled web.

Public API Per Slice

Each feature exposes only what it must. In TypeScript, that’s an index.ts that exports public functions. Everything else is implementation detail. This lets you refactor the inside of a slice without breaking others.

Code

/features/search  index.ts           <-- only public API  SearchBar.tsx      <-- not exported  searchEngine.ts    <-- not exported  useSearch.ts       <-- exported via index.ts

Start With User Stories, Not Nouns

Don’t make a user slice. Make user-registrationuser-settingsuser-invite. Nouns become bloated. Verbs stay focused. A feature slice should map to something your PM would put in Jira.

How To Migrate From Layers To Slices Without Rewriting

You don’t need a “big bang” rewrite. That’s how projects die. Use the Strangler Fig Pattern:

Pick One New Feature

Your next ticket: “Add gift cards”. Do NOT put it in controllers/GiftCardController. Create features/gift-cards/ and build the whole thing there. Prove it works.

Extract One Old Feature

Find a self-contained part of your app. “Password Reset” is perfect. Move its controller, service, and related code into features/password-reset. Update imports. Ship it. No behavior change, just moving files.

Establish Your Shared Kernel

As you move code, you’ll find true reusables: ButtonapiClientformatDate. These go in /shared. Be ruthless. If only 2 features use it, duplicate it for now. You can extract later when pattern emerges.

Set Lint Rules

Use ESLint or dependency-cruiser to ban cross-feature imports. features/checkout importing from features/cart should fail CI. This prevents backsliding into layers.

The 80/20 Rule

At 80% migrated, the old layers are ghost towns. Delete them. You’re now feature-sliced. This process can take 3-6 months on a large app, but you get value from week one.

Common Objections To Feature Slicing — And Why They’re Wrong

But I’ll have duplicate code!
Yes. And you’ll have zero-coupling. Duplication is cheaper than the wrong abstraction. When admin-user-list and public-profile both need getUserById, duplicate it. When admin needs to include deleted users and public doesn’t, you’ll be grateful they’re separate.

What about true cross-cutting concerns like auth?
Those live in shared or core. Authentication, logging, config — these aren’t features. They’re infrastructure. The rule: if every feature needs it, it’s shared. If 3 features need it, question it.

This won’t work for my backend microservice
It works better. A microservice IS a feature slice at the system level. Inside that service, slice again. order-service can have /features/create-order/features/cancel-order. Same principles, different scale.

My team will create 200 tiny folders
Good. 200 folders that map to your product are easier to navigate than 5 folders that map to your framework. Use code search. Your IDE is built for this.

Feature Slicing In Different Stacks

The concept is language-agnostic. Here’s what it looks like practically:

React/Next.js

Code

/app  /(features)    /dashboard      page.tsx      components/      hooks/useMetrics.ts      api.ts  /shared    ui/Button.tsx    lib/apiClient.ts

5 lines hidden

Backend: Node/Express

Code

/src  /features    /create-post      createPost.route.ts      createPost.service.ts      createPost.schema.ts      createPost.test.ts  /shared    middleware/auth.ts    db/prisma.ts

5 lines hidden

Mobile: Flutter

Code

/lib  /features    /onboarding      onboarding_screen.dart      onboarding_bloc.dart      onboarding_repo.dart  /shared    widgets/    utils/

4 lines hidden

The folder names change, the thinking doesn’t.

When NOT To Use Feature Slicing

Feature slicing isn’t dogma. Avoid it when:

You’re prototyping: For a 2-week MVP, layers are faster. You don’t know your features yet.

Your app is truly CRUD: If you’re just a database admin panel with 12 identical tables, layers or codegen make sense.

Team is under 3 people: The overhead of deciding slice boundaries isn’t worth it until coordination pain appears.

    If you have >50k LOC, >5 devs, or >2 years of planned life, start slicing.

    Conclusion

    Layered architecture asks What type of code is this? Feature slicing asks What user problem does this solve?

    The first question gets you a tidy codebase on day one. The second question gets you a codebase you can still change in year three.

    Stop organizing code by its technical shape. Start organizing by the features your customers pay for. Your future self, your new hires, and your deploy frequency will thank you.

    The next time you create a services folder, pause. Ask: “Is this a real feature?” If not, you’re building layers. If yes, give it a slice and let it own its destiny.

    FAQs

    What is feature slicing in software development?

    Feature slicing is an architectural approach where code is organized by business features rather than technical layers. Each slice contains all the UI, logic, data access, and tests needed for one specific feature, making changes isolated and development faster.

    Is feature slicing the same as microservices?

    No, but they’re related ideas. Microservices split features across different deployable apps. Feature slicing splits features into folders within one app. You can use feature slicing inside a monolith or inside each microservice.

    How big should a feature slice be?

    A slice should be small enough that one dev can understand it in 15 minutes, but big enough to deliver user value on its own. If it takes more than a sprint to build, consider splitting it. “User Management” is too big. “Invite Teammate” is perfect.

    Can I mix layers and feature slices?

    Yes, during migration. The goal is to have thin horizontal layers for truly shared code, like shared/db or shared/auth, while all business logic lives in vertical feature slices. The anti-pattern is having both services/ and features/ own business logic.

    Does feature slicing work for small teams?

    It helps most as teams grow, but even solo devs benefit after 3-4 months. The main win is reducing cognitive load: you only think about one feature at a time. For a 1-month project, it may be overkill. For anything you’ll maintain, start slicing early.

    ALSO READ: A Regretting You Summary That Finally Does The Book Justice

    Leave a Comment