Divergent Change: One Poor Clerk, Too Many Bosses
Learn the Divergent Change code smell with a school clerk story, simple definitions, TypeScript and C# examples, a clear comparison with Shotgun Surgery, and practice.
🏫 The story of Ramesh-ji, the school clerk
Meet Ramesh-ji. He is the only clerk at Saraswati Vidya School in Kanpur. He is a kind man with a small desk, one old computer, and a cup of tea that always goes cold. Why does the tea go cold? Because everyone in the school disturbs him, all day, for completely different reasons.
At 9 in the morning, the exam in-charge madam comes. "Ramesh-ji, the board has changed the marksheet format. Please update all the marksheets." Ramesh-ji nods and opens the marksheet file.
At 10, before he can finish, the accounts sir arrives. "Ramesh-ji, fee structure has changed. Late fine is now 50 rupees, not 20. Update the fee receipts." Ramesh-ji sighs, closes the marksheet file, and opens the fee register.
At 11, the sports sir comes running. "Ramesh-ji! Annual sports day! We need new trophy orders and certificates for the winners!" Ramesh-ji closes the fee register and opens the sports file.
At 12, the exam madam is back. "Ramesh-ji, did you finish the marksheets?" Of course he did not. He never gets to finish one job peacefully, because three different departments treat him as their personal helper.
By 2 in the afternoon, something worse happens. While hurriedly updating a fee receipt, Ramesh-ji writes a fee number on a marksheet by mistake — the papers were lying on the same desk, after all. The exam madam catches it the next day, and now everyone is upset with the most hardworking man in the school.
In the evening, Ramesh-ji writes one line in his small diary: "Today also, three bosses, one desk, zero finished jobs."
Watch his whole day in one picture — and watch his mood scores sink:
Now think carefully. Is Ramesh-ji a bad worker? No. He is hardworking and sincere. The problem is the design of the school office. Three different kinds of work — exams, accounts, and sports — all flow to one person. Every change in any department lands on the same desk.
If the school had three small desks — an exam clerk, an accounts clerk, and a sports clerk — each change would go to exactly one desk. Marksheet change? Exam desk only. Fee change? Accounts desk only. Nobody's tea would go cold, and a fee number could never land on a marksheet, because fee papers and marksheets would never sit on the same desk.
In code, we have classes that suffer exactly like Ramesh-ji. One class gets edited for exam reasons, fee reasons, and sports reasons. This smell has a name: Divergent Change.
🤔 What is this smell?
Divergent Change happens when one class is commonly changed in many different ways, for many different reasons.
The word "divergent" means "going in different directions". The changes that arrive at this class diverge — they come from completely different directions. A tax rule change, a database change, an email format change — all of them open the same file.
Martin Fowler describes this smell in his famous book Refactoring. His simple test is this: when you say things like "I have to change these three methods every time the database changes, and these four methods every time a new payment type is added," the class is suffering from Divergent Change. Two different kinds of future are pulling on the same class.
This smell belongs to a family called Change Preventers. These are smells that make change painful. The code does not stop you from changing it — but it makes every change slow, scary, and expensive. So, slowly, the team starts avoiding changes. That is the real damage.
There is a deep principle hiding here. The Single Responsibility Principle (SRP) says: a class should have only one reason to change. Divergent Change is what an SRP violation looks like in real, daily life. You do not see SRP breaking in a diagram — you feel it when the same poor class keeps appearing in every kind of pull request, like Ramesh-ji appearing in every department's complaint list.
College corner: Notice the exact wording of SRP. Uncle Bob does not say "a class should do only one thing" — he says it should have only one reason to change, and he later sharpened it to "a module should be responsible to one, and only one, actor." An actor is a group of people who request changes: the exam team, the accounts team, the sports team. Divergent Change is precisely the situation where multiple actors share one module. When you write your design documents in college, do not count functions to check SRP — count actors. One actor per class is the goal; Ramesh-ji had three.
Easy memory trick: Divergent Change = one Ramesh-ji, many bosses. One class, many reasons to change. The cure is to give each boss their own clerk — split the class so each reason has its own home.
One more important point. Divergent Change is hard to see in a single screenshot of code. It is a smell of history. A class might look only "a bit big" today. But if you open its git log and see commits like "fix tax rounding", "change email template", "switch to new database driver" — all on the same file — the smell becomes loud and clear.
Here is the whole idea of this smell as one map you can revise from:
🔍 How to spot it
You spot Divergent Change by watching what kinds of changes land on a class. Here is a simple checklist. If you tick three or more boxes, you very likely have this smell.
- When different kinds of requirements change, you always open the same class.
- The class's git history looks like the changelog of the whole product, not of one feature.
- The methods inside form separate "islands" — one group talks to the database, another formats text, another sends messages — and the islands share almost no fields.
- The class name is vague:
Manager,Processor,Handler,Service,Util. The name had to be vague to cover so many jobs. - The import list at the top of the file looks like a tour of the whole system — networking, storage, formatting, and math all together.
- You cannot describe the class in one sentence without using the word "and".
Here is the same idea as a quick reference table:
| Sign | What you observe | What it means |
|---|---|---|
| Magnet file | Unrelated pull requests keep touching one file | Many reasons to change live in one place |
| Method islands | Groups of methods that never share fields | Several hidden classes are trapped inside one |
| Vague name | OrderManager, StudentHelper, DataService | The name covers too many jobs to be specific |
| Import zoo | DB, HTTP, email, and formatting imports in one file | The class talks to every corner of the system |
| Re-test fear | A small fee change forces re-running exam tests | Responsibilities are tangled, not separated |
| The "and" test | "It calculates fees AND prints marksheets AND..." | More than one responsibility, guaranteed |
A practical tip for real projects: run git log --oneline -- path/to/File.ts and read the commit messages. If the messages belong to three different departments of your product, you have found your Ramesh-ji.
You can even draw the evidence. Take the last twenty commits that touched the suspect class and sort them by the kind of reason behind each one. A healthy class produces a boring chart — one giant slice. A Ramesh-ji class produces a colourful pizza:
If your pie has one dominant slice, relax — the class has one master. If it looks like the one above, with three or four healthy slices of unrelated reasons, the class is serving too many masters.
⚠️ Why it is a problem
You might ask: "So what if one class does many things? The code still runs!" True. But remember — Change Preventers do not break the program. They break the team's speed and confidence. Here is the cost, step by step.
1. Every edit is risky. To change the fee logic, you must scroll past marksheet code and sports code. Your eyes get tired. One wrong line, and you break exams while fixing fees. Unrelated things sitting together means unrelated things breaking together — exactly like the fee number that landed on a marksheet on Ramesh-ji's crowded desk.
2. Merge conflicts everywhere. In a team, the exam developer and the accounts developer both edit the same file in the same week. Git cries. Time is wasted resolving conflicts between changes that had no business meeting each other.
3. Tests become slow and fat. You cannot test the fee calculation alone, because it is tangled with database code and email code. So tests need a database, a mail server, everything. Slow tests get skipped. Skipped tests mean bugs.
4. Nothing can be reused. The nice marksheet formatting logic cannot be used in another project, because it is glued to the fee logic and the trophy logic in the same class.
5. New developers suffer. To safely change one small thing, a newcomer must understand the whole 800-line class. The learning cost of a tiny change becomes huge.
Notice the last box: the team avoids changes. That is the quiet, final cost. When changing a file feels dangerous, people stop improving it. The class rots, grows, and becomes the famous "nobody touches that file" of your project.
Cost number 2 — the merge conflict — deserves its own picture, because it is the cost college students feel first when they do their first group project. Two teammates, two completely unrelated tickets, one shared class:
College corner: This is where the textbook words coupling and cohesion become real. Cohesion measures how strongly the parts inside one module belong together. A Divergent Change class has low cohesion: its exam methods, fee methods, and sports methods are strangers sharing a flat. And because every consumer of any of those jobs must depend on the whole class, the class also drags in high coupling — the fee module's callers are accidentally coupled to exam code they never use. The classic design goal "high cohesion, low coupling" is just Ramesh-ji's story in formal clothes: give each job its own desk (cohesion), so departments stop bumping into each other (coupling).
💻 A real-life code example
Let us write Ramesh-ji's desk as code. Here is the school's software, with everything dumped into one class — exactly how it grows in real projects, one "small" method at a time.
// SchoolClerk.ts — the poor class everyone disturbs
class SchoolClerk {
// ----- Reason to change #1: EXAM rules -----
prepareMarksheet(student: Student): string {
const total = student.marks.reduce((sum, m) => sum + m.score, 0);
const percent = (total / (student.marks.length * 100)) * 100;
const grade = percent >= 90 ? "A+" : percent >= 75 ? "A" : "B";
return `MARKSHEET\n${student.name}\nPercent: ${percent.toFixed(1)}\nGrade: ${grade}`;
}
// ----- Reason to change #2: FEE rules -----
generateFeeReceipt(student: Student, paidOn: Date): string {
const baseFee = 1200;
const dueDate = new Date(paidOn.getFullYear(), paidOn.getMonth(), 10);
const lateFine = paidOn > dueDate ? 50 : 0; // accounts sir changes this often!
return `RECEIPT\n${student.name}\nFee: ${baseFee}\nFine: ${lateFine}\nTotal: ${baseFee + lateFine}`;
}
// ----- Reason to change #3: SPORTS events -----
orderTrophies(eventName: string, winners: Student[]): string[] {
return winners.map(
(w, rank) => `Trophy: ${eventName} - Rank ${rank + 1} - ${w.name}`
);
}
// ----- Reason to change #4: STORAGE -----
saveRecord(student: Student): void {
// talks to the database — changes when the school changes its software vendor
db.execute("INSERT INTO students ...", student);
}
}Look at the comments. Four different departments own four different parts of this class. Exam madam will ask you to change prepareMarksheet. Accounts sir owns generateFeeReceipt. Sports sir owns orderTrophies. And the IT vendor owns saveRecord. Four bosses, one class. That is textbook Divergent Change.
Here is the same situation as a class diagram — before the cure on the left side of your imagination, after the cure on the right. The overloaded clerk delegates nothing; the healthy office has one small desk per reason:
The danger is real: suppose accounts sir says "fine is now 75 rupees". You open this file, change one number — and your editor's auto-format accidentally touches the marksheet method too. Now your one-line fee change needs the exam tests to run as well, "just in case". Everything is connected because everything lives together.
🛠️ Cleaning it up, step by step
The cure is to split the class along its reasons for change. Each reason gets its own small class. The main tool is Extract Class, helped by Move Method and Move Field. Do it gradually — one responsibility at a time, with tests running after each step.
Step 1 — List the reasons. Read the class and ask of every method: "Who asks me to change this?" Write the answers down. For SchoolClerk we get: exam team, accounts, sports, IT vendor. Four reasons. So we aim for four homes.
Step 2 — Extract the first responsibility. Pick one cluster — say, exams — and use Extract Class to give it a home of its own:
// ExamOffice.ts — owned by the exam team only
class ExamOffice {
prepareMarksheet(student: Student): string {
const total = student.marks.reduce((sum, m) => sum + m.score, 0);
const percent = (total / (student.marks.length * 100)) * 100;
const grade = this.gradeFor(percent);
return `MARKSHEET\n${student.name}\nPercent: ${percent.toFixed(1)}\nGrade: ${grade}`;
}
private gradeFor(percent: number): string {
return percent >= 90 ? "A+" : percent >= 75 ? "A" : "B";
}
}Then, inside SchoolClerk, the old method simply delegates: prepareMarksheet(s) just returns this.examOffice.prepareMarksheet(s). Run the tests. Everything still works, but the exam logic now has its own room.
Step 3 — Repeat for each remaining reason. Use Move Method to carry the fee logic into an AccountsDesk, the trophy logic into a SportsDesk, and the saving logic into a StudentRepository. If a method drags some data with it (like the base fee amount), use Move Field so the data travels with its behavior.
// After the full split: each class has exactly ONE reason to change
class AccountsDesk {
private readonly baseFee = 1200;
private readonly lateFine = 50;
generateFeeReceipt(student: Student, paidOn: Date): string { /* fee logic only */ }
}
class SportsDesk {
orderTrophies(eventName: string, winners: Student[]): string[] { /* sports only */ }
}
class StudentRepository {
saveRecord(student: Student): void { /* storage only */ }
}Step 4 — Decide the fate of the old class. Sometimes the original class becomes a thin coordinator that simply holds the four desks together — that is fine. Sometimes it becomes empty, and you delete it. Either ending is a happy ending.
Step 5 — Apply the one-sentence test. Describe each new class in one sentence. "AccountsDesk calculates fees and fines." "SportsDesk prepares trophy orders." No "and" joining unrelated jobs? You are done.
The whole life of the class — from healthy, to overloaded, to cured — looks like this:
And the payoff is measurable. Count how many unrelated reasons can force an edit on each class, before and after the split:
Now watch what happens next month. Accounts sir changes the fine to 75 rupees. You open AccountsDesk, change one number, run only the fee tests, and go home on time. The exam code was never even opened. That is the whole reward.
College corner: John Ousterhout, in A Philosophy of Software Design, gives this pain a precise name: change amplification — when a simple change requires modifications in many places, or forces you to understand far more code than the change itself deserves. Divergent Change amplifies understanding: a one-line fee edit demands that you understand (or at least carefully step around) 800 lines of exam and sports code. After the split, the understanding needed shrinks to exactly the size of the change. Reducing change amplification is one of the most practical definitions of "good design" you will ever get.
Do not over-split! If you make twenty tiny classes that always change together, you have created the opposite smell — Shotgun Surgery. Split by reason for change, not by number of methods. Two methods that always change together belong together.
🧪 The same smell in C# and Python
The smell looks identical in C#. Here is a compact version — one OrderProcessor that changes for pricing reasons, storage reasons, and email reasons:
// Before: three reasons to change, one class
public class OrderProcessor
{
public decimal CalculateTotal(Order order) // changes when tax rules change
{
var subtotal = order.Lines.Sum(l => l.Price * l.Qty);
return subtotal + subtotal * 0.18m; // GST
}
public void Save(Order order) // changes when the database changes
{
using var conn = new SqlConnection(_cs);
conn.Execute("INSERT INTO Orders ...", order);
}
public void SendConfirmation(Order order) // changes when email design changes
{
_smtp.Send(order.Email, "Your order", RenderHtml(order));
}
}After splitting along the reasons for change:
// After: one reason per class
public class PricingCalculator { public decimal CalculateTotal(Order o) { /* tax only */ } }
public class OrderRepository { public void Save(Order o) { /* storage only */ } }
public class OrderNotifier { public void SendConfirmation(Order o) { /* email only */ } }A GST change now touches only PricingCalculator, and only its tests need to run. The database team and the email team never collide with the tax team again.
Python students, do not feel left out — dynamic typing hides nothing from this smell. A Django or Flask project grows the exact same Ramesh-ji, usually named utils.py or helpers.py:
# student_service.py — Before: one class, three bosses
class StudentService:
def prepare_marksheet(self, student): # exam team owns this
percent = sum(m.score for m in student.marks) / len(student.marks)
grade = "A+" if percent >= 90 else "A" if percent >= 75 else "B"
return f"MARKSHEET\n{student.name}\n{percent:.1f}% — {grade}"
def fee_receipt(self, student, paid_on): # accounts owns this
fine = 50 if paid_on.day > 10 else 0
return f"RECEIPT\n{student.name}\nTotal: {1200 + fine}"
def order_trophies(self, event, winners): # sports owns this
return [f"Trophy: {event} - Rank {i + 1} - {w.name}"
for i, w in enumerate(winners)]
# After: one small class per reason — ExamOffice, AccountsDesk, SportsDeskThe language changes; the diagnosis question does not: who asks for each method to change? If the answers differ, the class must split.
⚖️ Divergent Change vs Shotgun Surgery
This is the most important comparison in the whole Change Preventers family, and students mix these two up all the time. Here is the clean way to remember: they are mirror opposites.
| Question | Divergent Change | Shotgun Surgery |
|---|---|---|
| Shape of the pain | ONE class, MANY reasons to change | ONE reason, MANY classes to change |
| Story version | One clerk disturbed by every department | One address change, ten offices to visit |
| What is wrong | The class is overloaded with jobs | The job is smeared across the codebase |
| You notice it when | The same file appears in every kind of PR | A "small" change becomes a ten-file hunt |
| The cure direction | Split — break the class apart | Gather — collect the pieces into one home |
| Main refactorings | Extract Class, Move Method | Move Method, Move Field, Inline Class |
| Principle violated | Single Responsibility (too many reasons) | Single ownership (no single home for a concept) |
| Danger of over-curing | Over-splitting creates Shotgun Surgery | Over-gathering creates Divergent Change |
The diagnostic question is beautifully symmetric. Ask two things:
- For one class, how many kinds of change hit it? Many → Divergent Change → split.
- For one kind of change, how many classes do I edit? Many → Shotgun Surgery → gather.
You can place any suspicious situation on a simple map. The horizontal axis asks how many classes a change touches; the vertical axis asks how many reasons touch one class. Healthy code sits in the calm corner; each smell owns its own corner; and yes, truly unlucky codebases manage both at once:
Our SchoolClerk sits high on the left: one class, many reasons — Divergent Change territory. An address format smeared across ten files sits low on the right — Shotgun Surgery territory. A healthy PricingEngine that changes for pricing reasons only, and changes alone, sits in the calm bottom-left corner.
And note the warning in the table's last row. The two cures push in opposite directions. If you split too enthusiastically, related code gets scattered and every change becomes a multi-file hunt — you have traded one smell for its mirror. The goal is balance: each class owns one responsibility, and each responsibility lives in one class.
🕵️ Where this smell hides in real projects
Divergent Change loves certain hiding spots. Researchers and practitioners (Fowler's Refactoring, refactoring.guru, and many code-quality blogs) keep finding it in the same places:
- "God" service classes.
UserService,OrderManager,AppHelper— classes whose vague names invited everyone to dump code there. In many codebases, one such class quietly handles validation, formatting, persistence, and notifications. - Controllers in web apps. A controller that validates input, applies business rules, talks to the database, and shapes the JSON response changes for four different reasons. Framework tutorials often teach this shape, so it spreads.
- Utility classes.
Utils.tsandHelpers.csare Divergent Change by design — a drawer where every department throws its odd socks. - Configuration and startup files. One giant startup class that wires logging, database, security, and caching changes whenever any of those areas changes.
- Long-living legacy classes. The oldest classes in a system attract code by gravity. The path of least resistance — "just add one more method here" — repeated for five years, produces a perfect Ramesh-ji.
A practical detection trick used in real teams: tooling that ranks files by number of distinct authors and number of commits. Files high on both lists are usually Divergent Change suspects, because many people changing one file for many tickets is exactly the smell's signature.
😌 When it is okay to ignore
Good engineers are honest: not every smelly class deserves surgery today. Here is the honest table.
| Situation | Ignore it? | Why |
|---|---|---|
| The class is rarely edited (twice in two years) | Yes | The smell is theoretical; splitting buys nothing real |
| Early prototype, requirements still fuzzy | Yes, for now | Boundaries are not visible yet; premature splits will be wrong splits |
| The "different" responsibilities always change together | Yes | They are really one responsibility; splitting creates artificial seams |
| Small script or throwaway tool | Yes | Indirection costs more than it saves in tiny code |
| The file appears in unrelated PRs every sprint | No — fix it | The smell is active and charging you interest every week |
| Two teams keep merge-conflicting on the same class | No — fix it | The split also splits team ownership, ending the conflicts |
The rule of thumb: let the edit history decide. Code smells are hints, not laws. A class with many responsibilities that never changes is an academic problem. A class that keeps appearing in unrelated commits is an expensive, active problem — fix that one first.
💊 Which refactorings cure it
| Refactoring | What it does here | When to reach for it |
|---|---|---|
| Extract Class | Pulls one whole responsibility into a new, focused class | The primary cure — use it once per reason-to-change |
| Move Method | Carries a single method to the class where its reason lives | When a method clearly belongs to an already-extracted home |
| Move Field | Moves data next to the behavior that uses it | When extracted methods keep reaching back for old fields |
| Extract Function | Splits a multi-job method before class-level surgery | When the tangle is inside individual methods too |
| Extract Superclass / Subclass | Organizes split classes that share common behavior | When the new classes turn out to be natural siblings |
Work in small, safe steps: extract one responsibility, run the tests, commit, repeat. Never do the whole split in one giant risky change — that would be ironic for a smell whose whole lesson is about making change safe.
📦 Quick revision box
+--------------------------------------------------------------+
| DIVERGENT CHANGE — QUICK REVISION |
+--------------------------------------------------------------+
| Story : One school clerk, disturbed by EVERY department |
| Smell : ONE class changed for MANY different reasons |
| Family : Change Preventers |
| Breaks : Single Responsibility Principle (SRP) |
| Spot it : Same file in unrelated PRs; vague name; method |
| islands; import zoo; fails the one-sentence test |
| Costs : Risky edits, merge conflicts, slow tests, |
| no reuse, team afraid to change the file |
| Cure : SPLIT the class -> Extract Class, Move Method, |
| Move Field (one responsibility per class) |
| Opposite : Shotgun Surgery (one reason, many classes) |
| Memory : Divergent = one desk, many bosses -> SPLIT |
| Ignore : If the class is stable and rarely edited |
+--------------------------------------------------------------+✏️ Practice exercise
Time to be the school principal who fixes the office!
Exercise 1 — Spot the reasons. Below is a class from a small shop's software. List every reason to change you can find, and name who (which "department") owns each reason.
class ShopAssistant {
calculateBill(items: Item[]): number {
const total = items.reduce((s, i) => s + i.price * i.qty, 0);
return total > 1000 ? total * 0.95 : total; // 5% festival discount
}
printLabel(item: Item): string {
return `${item.name} — Rs.${item.price} (MRP incl. all taxes)`;
}
reorderStock(item: Item): void {
if (item.qty < 5) supplierApi.placeOrder(item.name, 50);
}
sendOfferSms(customer: Customer): void {
smsGateway.send(customer.phone, "Festival sale! 5% off above Rs.1000");
}
}Exercise 2 — Plan the split. For each reason you found, propose a new class name (one sentence describing its single job). You should end with three or four small classes.
Exercise 3 — Do one extraction. Actually perform Extract Class for the billing responsibility only. Make ShopAssistant delegate to your new class so all old callers still work.
Exercise 4 — Draw the pie. Open any project you have written (even a college assignment) and pick its biggest class. Sort its methods by who would ask for each to change. Sketch the pie chart from Figure 3 for your class. One big slice? Healthy. A colourful pizza? You know what to do.
Exercise 5 — The diagnosis drill. For each situation below, say whether it is Divergent Change, Shotgun Surgery, or neither:
- Renaming a status value forces edits in 11 files.
- The
InvoiceManagerclass is edited for tax changes, PDF layout changes, and email changes. - A 40-line class has not been touched in 18 months.
- Every discount rule change means editing
PricingEngine— and onlyPricingEngine.
(Answers to think about: 1 is Shotgun Surgery — one reason, many files. 2 is Divergent Change — one class, many reasons. 3 is no active smell — stable code earns no surgery. 4 is healthy design — that is exactly how it should feel!)
If you can explain to a friend why number 2 needs splitting but number 1 needs gathering, you have truly understood the Change Preventers family. Next, read about the mirror smell: Shotgun Surgery.
Frequently asked questions
- What is Divergent Change in one line?
- Divergent Change is when one class keeps getting edited for many different, unrelated reasons — exam reasons today, fee reasons tomorrow, sports reasons next week. The class has too many jobs, so every kind of change lands on it.
- How is Divergent Change different from Shotgun Surgery?
- They are exact opposites. Divergent Change is ONE class changed for MANY reasons — the fix is to split the class. Shotgun Surgery is ONE reason that forces changes in MANY classes — the fix is to gather the scattered code into one place.
- Which refactoring fixes Divergent Change?
- Extract Class is the main cure. You pull each separate responsibility out into its own small class. Move Method and Move Field help you carry the methods and data to their new homes.
- How is Divergent Change related to the Single Responsibility Principle?
- Divergent Change is what SRP violation feels like in daily work. SRP says a class should have only one reason to change. A class with Divergent Change has many reasons to change, so it breaks SRP by definition.
- Is every big class a case of Divergent Change?
- No. A class can be big but still have one clear job. Divergent Change is about reasons for change, not size. Check the edit history: if unrelated requests keep landing on the same class, the smell is real. If the class is stable and rarely edited, leave it alone.
Further reading
Related Lessons
Shotgun Surgery: One Small Change, Ten Offices to Visit
Learn the Shotgun Surgery code smell with an address-change story, simple definitions, TypeScript and C# examples, a clear comparison with Divergent Change, and practice.
Parallel Inheritance Hierarchies: Every Sweet Needs Its Shadow Box
Learn the Parallel Inheritance Hierarchies code smell with a sweet-shop story, mirrored class trees explained simply, TypeScript and C# examples, fixes, and practice.
Large Class: The School Bag That Carries Everything
Understand the Large Class code smell — why god classes grow, how to spot low cohesion, and how Extract Class splits them into small, focused classes.
Extract Class: Give an Overworked Class a Helping Partner
Learn the Extract Class refactoring with a fun school office story. Split one overloaded class into two focused classes, each with a single clear job to do.