Skip to main content

Refactoring Catalog: Framework-Specific Recipes

Every ecosystem accumulates debt in its own dialect. React grows god components, Django grows fat models, Spring grows anemic services wired to everything, and .NET grows controllers that are secretly the whole application.

These are copy-ready before and after examples for the refactorings that come up most often in each stack, with the reasoning behind each change so you can adapt it rather than paste it.

This is one of three deep dives under Proven Techniques to Tackle Tech Debt. Start there if you are still deciding which technique fits your situation. The companion pages are characterization testing and dependency untangling.

How to Use This Catalog

A refactoring recipe is not a to-do list. Each guide below shows a real shape of bad code, the shape you want instead, and the specific transformation that gets you from one to the other. Read the "before" first and decide whether it actually describes your code - if it does not, skip the recipe. The most expensive refactoring is the one applied to a problem you did not have.

Pin the behavior

Before you move a line, get the current behavior under test. Refactoring without a safety net is just editing.

Change one shape

Apply exactly one recipe per pull request. Mixed refactorings are unreviewable and unrevertable.

Leave the door open

Stop at a state that is better than where you started, even if it is not the destination. Partial is fine; broken is not.

Framework-Specific Refactoring Guides

Detailed before/after examples for popular frameworks and languages

Different frameworks have different patterns for accumulating tech debt - and different strategies for cleaning it up. These guides provide concrete, copy-paste-ready examples for the most common refactoring scenarios in each ecosystem.

React Refactoring Guide React 19 TypeScript

React applications accumulate debt in predictable ways: god components, prop drilling, mixed concerns, and class component remnants. Here is how to systematically address each pattern.

Pattern 1: Extract Custom Hooks from God Components

God components mix data fetching, state management, business logic, and UI rendering. Extract reusable hooks to separate concerns.

Pattern 2: Replace Prop Drilling with Context

When props pass through 4+ component levels, it is time for Context or state management.

Python/Django Refactoring Guide Python 3.12+ Django

Django models often become god objects with business logic, validation, and database concerns all mixed together. Extract services to keep models focused on data representation.

Pattern: Extract Service Layer from Fat Models

Java/Spring Boot Refactoring Guide Java 25 LTS Spring Boot 4

Spring Boot applications often suffer from anemic domain models, service classes that are just transaction scripts, and tight coupling to frameworks. Adopt Clean Architecture patterns to make code more testable.

C#/.NET Refactoring Guide .NET 10 LTS C# 14

Legacy .NET applications often mix ASP.NET MVC controllers with Entity Framework and business logic. Modern .NET supports clean patterns with minimal boilerplate.

Related Resources

Frequently Asked Questions

Small enough that a reviewer can state the transformation in one sentence: "extracted the fetch logic into a hook", "moved order validation out of the model into a service". If you cannot write that sentence, the pull request is doing more than one thing and should be split. A refactoring pull request should also contain no behavior changes at all - if a bug fix sneaks in, reviewers stop trusting the diff and start reading every line, which is exactly what you were trying to avoid.

Yes, and they do not have to be good tests. Characterization tests that simply record what the code currently returns are enough to catch an accidental behavior change during a structural move. The point is not correctness, it is invariance: the output before the refactoring must match the output after. Once the structure is better, replacing those recorded expectations with real assertions is much easier. The companion page on characterization testing walks through building them for code that has no seams at all.

The underlying moves are framework-independent: separate data access from business rules, give each unit one reason to change, push side effects to the edges, and depend on abstractions rather than concrete infrastructure. The React hook extraction and the Django service layer are the same refactoring wearing different clothes. Read the closest example, identify which of those four moves it is performing, and apply that move using your own framework's idioms.

No. Refactoring is an investment in future changes to that code, so it only pays off where future changes will happen. Check the change history first: a file with no commits in two years is not costing you anything, no matter how ugly it is. Spend the effort on the files that appear in every second pull request. If the module is scheduled for replacement, the useful work is characterizing its behavior so the replacement has a specification, not tidying code that is about to be thrown away.

Encode the new shape as a rule the build enforces. A lint rule that forbids importing the data layer from a component, a maximum file length, a dependency-direction check, or an architecture test that fails when a layer reaches sideways. Documentation and code review will not hold the line, because both depend on someone remembering. If the structure you just created is worth the effort you spent, it is worth a check that fails the build when someone reverses it.

They are good at the mechanical part - extracting a function, renaming across a file, converting a class component - and poor at the judgment part, which is deciding where the boundary belongs. An assistant will happily split a component into six files that are all still coupled to the same global state, producing more files and no less debt. Use them to execute a boundary you have already chosen, keep the diff small enough to review line by line, and require that the characterization tests still pass before you accept it.

Not Sure Which Refactoring You Need?

Go back to the decision guide and match your situation to a technique before you touch the code.

Back to Proven Techniques