Replace Parameter with Method Call: Don't Tell the Shopkeeper His Own Prices
Learn the Replace Parameter with Method Call refactoring (Replace Parameter with Query in Fowler's 2nd edition) with a kirana shop story, TypeScript and C# examples, safe mechanics, and the testability fine print.
🛒 The Story of the Boy Who Read the Shopkeeper's Price List to Him
Meet Sandeep, a class seven student in Nagpur. Every Sunday his mother sends him to Verma Uncle's kirana shop with a list: one kg toor dal, half kg sugar, one packet tea.
The first Sunday, Sandeep does something funny. Before leaving home, he opens the shop's printed rate card from last month, copies prices onto his list, and at the counter announces: "Uncle, toor dal — 160 rupees per kg, so 160. Sugar — 45 per kg, so half is 22.50. Tea — 250 for the big packet. Total 432.50. Here is the money."
Verma Uncle peers over his spectacles, half amused, half offended. "Beta… whose shop is this?"
"Yours, Uncle."
"And whose price list is THAT?" He taps the laminated sheet hanging right beside him, today's rates in fresh marker. "Toor dal went up Tuesday. It is 170 now. Why are you telling me my own prices? You tell me what you want and how much. The prices are MY job — I read them from MY list, which is always the latest one."
Sandeep turns red. Of course. The shopkeeper owns the price list. It hangs at his elbow. It is always current. When Sandeep carries prices from home, three bad things happen: he does extra work before every trip; his copied prices can be stale (dal went up Tuesday!); and if his neighbour Bunty also shops this way, two boys now maintain two private copies of a list that already exists, perfectly, at the counter.
From that Sunday, Sandeep just says: "One kg toor dal, half kg sugar, one tea packet." Uncle glances at his own list, totals it, done. The information stayed where it lives.
Watch the two conversations side by side. In the first, Sandeep delivers information the counter already owns; in the second, he delivers only what he alone knows — the items and quantities his mother wrote:
Code makes Sandeep's first-Sunday mistake constantly. A caller computes a value — a discount level, a tax rate, today's date band — and passes it as a parameter to a method that could have looked it up itself, from the very same source. Every caller repeats the lookup; callers can pass stale or inconsistent values; and the signature grows. The cure is called Replace Parameter with Method Call — in Fowler's second edition, Replace Parameter with Query: delete the parameter and let the method ask for the value itself.
🤔 What is Replace Parameter with Method Call?
Replace Parameter with Method Call is the refactoring where you remove a parameter whose value the method can obtain on its own, and have the method call the source — a getter, a query, a lookup — inside its body instead.
The telltale shape: every call site performs the same little errand first —
const level = order.discountLevel(); // the errand
const price = order.discountedPrice(base, level);— and then hands over the result. But discountedPrice lives on the same object as discountLevel. The method was standing beside the laminated price list and politely waiting for callers to read it aloud.
A short naming history, since you will meet both names. In the first edition of Martin Fowler's Refactoring (1999) this is Replace Parameter with Method Call (the catalog page even carried the shorter alias Replace Parameter with Method); Refactoring Guru and SourceMaking still use that name. The second edition (2018) renamed it Replace Parameter with Query, filed beside its exact inverse, Replace Query with Parameter — because sometimes you deliberately go the other way, pulling a lookup out into a parameter to gain flexibility or testability. Keep both directions in mind; this is another of refactoring's two-way streets.
The judgement at the heart of this refactoring is a sorting question: for each parameter, who naturally owns this information? When Sandeep's team-of-one audited his Sunday announcements, the split looked like this:
More than half of what he said was the shopkeeper's own information echoed back. Every echoed value is a parameter this refactoring can delete.
Why do it?
- Shorter signature, fewer wrong calls. One less parameter means one less thing to pass, mis-order, or fake. The guiding rule: a parameter list should demand only what the method cannot honestly get for itself.
- No duplicated errands. The lookup happens once, inside, instead of being copy-pasted before every call.
- No stale or inconsistent values. Callers can no longer pass last month's discount level; the method always reads the live list.
- Self-sufficient methods are easier to call. New callers need zero ceremony — no preparation steps to learn.
One line to remember: never make callers deliver information to a method that the method can fetch from its own shelf. A parameter should carry what only the caller knows. Everything else is the receiver's job — like prices at the shopkeeper's own counter.
The one-sentence eligibility test, and it is strict: the value-computing code must not depend on anything that exists only inside the caller. If the errand uses the caller's own parameters or local variables, that code cannot move into the receiver, and the parameter must stay.
College corner: this refactoring is the procedural cousin of a broader principle — demand-driven (lazy) values beat pushed copies. Whenever a value is computed early and carried forward (pushed), every hop is a place where it can go stale; whenever it is fetched at the moment of use (pulled), there is exactly one read, as fresh as possible. You meet the same idea all over computer science: derived state in UI frameworks should be computed, not stored; database views recompute rather than duplicate; cache invalidation is famously hard precisely because caches are pushed copies of pull-able truth. A parameter whose value the receiver could query is a tiny, innocent-looking cache — with all of a cache's invalidation problems and none of its performance justification. Deleting it restores a single point of truth with a single read time. The professional flip side: when you do need a frozen-in-time value (a quoted price honoured for 24 hours), that is no longer derivable data — it is genuine caller-knowledge, and it has earned its parameter.
🚦 When do we need it?
Watch for these signs:
- The pre-call errand. A line (or three) before every call exists only to compute one argument, and the computation uses things the called method can also reach — the same object's fields, the same service, the same configuration. That errand belongs inside.
- Long Parameter List. This refactoring is one of the standard shorteners for the smell. Before bundling parameters or passing objects, simply delete the ones the method can derive. It is the cheapest fix on the menu — no new types, no new structure, just less.
- Copy-pasted derivations. Three different callers all write
const level = loyaltyYears > 2 ? 2 : 1;before calling. The same knowledge lives in three places — and one day someone updates two of them and forgets the third. - Inconsistency bugs. A bug report shows two screens displaying different totals because two callers computed the "same" tax band slightly differently. A parameter invites private arithmetic; an internal query has exactly one answer.
- Friends of Data Clumps. Sometimes the removable parameter travels in a gang —
(subtotal, discountLevel, taxRate)— where two members are derivable and one is real data. Remove the derivable ones with this refactoring; if a genuine clump remains, the siblings Preserve Whole Object and Introduce Parameter Object handle the rest. The three form a toolkit for the same disease, chosen by where each value naturally lives: on an object the caller holds → pass the object; computable by the receiver → pass nothing; loose values with no home → create one.
And when the answer is no — this list matters as much as the first:
- Only the caller knows. A user-typed quantity, a caller-specific override, a value built from the caller's own parameters — the receiver cannot fetch these. They are honest parameters; keep them.
- Fetching would create a new dependency. If the method must import a service, reach a global, or grab a singleton it never touched before, you are trading a visible parameter for hidden coupling. The visible thing is healthier. (When the receiver genuinely lacks context, the opposite move — Add Parameter — is the correct one.)
- Callers vary or cache the value on purpose. If some caller passes a different discount level (a manager override), or computed the value once and reuses it across a loop so all items get a consistent rate, the parameter is doing real work. Deleting it deletes a feature.
- The computation is expensive or side-effecting. Moving a slow query inside a method that runs in a tight loop multiplies its cost; moving a side effect changes observable behaviour. Measure and read before you lift.
- Testability would suffer badly. A parameter is trivially fakeable in a test; an internal call to something that reads clocks, databases, or network is not. This is the entire reason the inverse refactoring (Replace Query with Parameter) exists — to remove an awkward internal reference by hoisting it into a parameter. If your tests keep fighting the internal fetch, listen to the tests.
The decision in one flow:
👀 Before and after at a glance
A festival-season order class in TypeScript. Before — every caller reads the price list aloud:
class Order {
constructor(
private readonly quantity: number,
private readonly itemPrice: number,
private readonly customerLoyaltyYears: number,
) {}
discountLevel(): number {
return this.customerLoyaltyYears > 2 ? 2 : 1;
}
// BEFORE: the method asks callers to deliver values it owns
finalPrice(basePrice: number, discountLevel: number): number {
return discountLevel === 2 ? basePrice * 0.9 : basePrice * 0.95;
}
}
// every caller runs the same errand first:
const base = order.quantity * order.itemPrice; // hmm, also derivable...
const level = order.discountLevel(); // the errand
const payable = order.finalPrice(base, level);finalPrice lives on the same object as discountLevel. Both parameters are prices read aloud to the shopkeeper. After the refactoring:
class Order {
constructor(
private readonly quantity: number,
private readonly itemPrice: number,
private readonly customerLoyaltyYears: number,
) {}
private discountLevel(): number {
return this.customerLoyaltyYears > 2 ? 2 : 1;
}
private basePrice(): number {
return this.quantity * this.itemPrice;
}
// AFTER: self-sufficient — reads its own shelf
finalPrice(): number {
return this.discountLevel() === 2
? this.basePrice() * 0.9
: this.basePrice() * 0.95;
}
}
// callers shrink to one honest call:
const payable = order.finalPrice();Both parameters vanished, because both were derivable from the order itself — and discountLevel could even become private, since no outsider needs it anymore. The signature went from two parameters to zero; calling it wrongly is now impossible, because there is nothing left to pass wrongly.
The class diagram shows the queries folding inward — public errand-helpers become private shelf-reads:
🪜 Step-by-step, the safe way
The order of operations protects you: make the parameter unused first, prove nothing changed, and only then delete it. The codebase moves through these states:
- Check eligibility honestly. Can the method reach the same source the callers use — with no new dependency? Trace what the errand code touches. If it uses any caller-local variable or caller parameter, stop: this parameter must stay.
- If the errand is raw inline code, extract it into a query method first. Callers computing
loyaltyYears > 2 ? 2 : 1inline should first get a shared, nameddiscountLevel()via Extract Method. Now caller and receiver can both call one named source — and the duplication is already half dead. - Inside the method, replace uses of the parameter with the query call — but keep the parameter in the signature. This is the safety trick: the signature is unchanged, every caller still compiles, yet the parameter is now dead weight.
// Intermediate state: parameter still accepted, silently ignored
finalPrice(basePrice: number, discountLevel: number): number {
const level = this.discountLevel(); // internal fetch
return level === 2 ? basePrice * 0.9 : basePrice * 0.95;
}- Run the full test suite. If anything fails here, you have learned something precious: some caller was passing a value different from what the internal query returns — an override, a cached copy, a stale read. Investigate before proceeding. That difference is either a bug you just found or a feature you must not delete.
- Remove the parameter (the classic Remove Parameter step — IDEs automate it as part of Change Signature), and delete the now-pointless errand line at every caller. The compiler and unused-variable warnings escort you to each site.
- Repeat for the next derivable parameter (
basePrice, in our example), then run everything once more. Finally, tighten visibility: queries no outsider calls anymore can become private.
The intermediate "accepted but ignored" stage is your bug detector — do not skip it. If you delete the parameter in one stroke and some caller was passing a manager-override discount level, the override silently dies, and nobody learns until a customer complains at the counter. With the two-stage approach, the test run at step 4 catches the mismatch while both paths still exist. Also mind performance: if the internal query is costlier than the old field read and the method runs in a hot loop, measure before and after.
🏫 A bigger real-life example
Sandeep's school built a small fee-counter app. Look at how every caller must do the office's homework before asking for a bill:
class FeeCounter {
constructor(
private readonly feeTable: FeeTable, // the office's own rate list
private readonly calendar: SchoolCalendar, // the office's own calendar
) {}
lateFinePerDay(): number {
return this.feeTable.lateFineRate();
}
daysLate(dueDate: Date): number {
return this.calendar.schoolDaysBetween(dueDate, this.calendar.today());
}
// BEFORE: three parameters — and two of them are the office's own information
termBill(studentClass: string, fine: number, lateDays: number): number {
const tuition = this.feeTable.tuitionFor(studentClass);
return tuition + fine * lateDays;
}
}
// every caller, everywhere:
const fine = counter.lateFinePerDay(); // errand 1
const lateDays = counter.daysLate(dueDate); // errand 2
const bill = counter.termBill("7B", fine, lateDays);The fee table and calendar belong to the counter. Yet callers fetch the fine rate and the late days from the counter, only to hand them straight back. Worse: the parents' portal cached fine at login time, the office desk fetched it fresh — and during the April rate revision the two screens showed different bills for the same student. A real inconsistency bug, born from a needless parameter.
After the refactoring:
class FeeCounter {
constructor(
private readonly feeTable: FeeTable,
private readonly calendar: SchoolCalendar,
) {}
private lateFinePerDay(): number {
return this.feeTable.lateFineRate();
}
private daysLate(dueDate: Date): number {
return this.calendar.schoolDaysBetween(dueDate, this.calendar.today());
}
// AFTER: callers state only what THEY know — class and due date
termBill(studentClass: string, dueDate: Date): number {
const tuition = this.feeTable.tuitionFor(studentClass);
return tuition + this.lateFinePerDay() * this.daysLate(dueDate);
}
}
// every caller:
const bill = counter.termBill("7B", dueDate);Study which parameters survived: studentClass and dueDate stayed, because only the caller knows which student is standing at the counter. The fine rate and late-day count vanished, because the counter owns the rate list and the calendar. That split — caller-knowledge stays, receiver-knowledge goes — is the entire judgement of this refactoring in one example. And the April bug is structurally dead: there is exactly one place that reads the rate, at the moment of billing.
The full audit of termBill, parameter by parameter:
| Parameter | Who truly owns it? | Verdict | Reason |
|---|---|---|---|
studentClass | Caller — the person at the counter | Keep | The counter cannot guess which student is being billed |
fine | Receiver — lives in the counter's own feeTable | Delete | The counter reads its own rate list, always fresh |
lateDays | Receiver — derivable from calendar plus the due date | Delete | One internal computation replaces six caller copies |
dueDate | Caller — depends on which term, which student | Keep | Needed as input for the internal daysLate query |
The school app had six screens calling termBill, each with its own two errand lines. Count the ceremony before and after:
One honest cost appeared too: termBill now calls calendar.today() internally, which makes tests time-dependent. The team handled it by injecting a fake SchoolCalendar in tests — the dependency was already in the constructor, so no new coupling was created. Had the method been forced to grab a global clock, that would have been the signal to stop and keep a parameter instead.
College corner: that last paragraph is the doorway to a whole design topic — purity versus self-sufficiency. A function whose output depends only on its arguments is referentially transparent: trivially testable, cacheable, parallelizable. Every parameter you replace with an internal query moves the function away from that ideal, because its output now depends on ambient state (the fee table, the clock). This is not automatically bad — objects exist precisely to bundle state with behaviour — but it explains why the inverse refactoring, Replace Query with Parameter, sits right beside this one in Fowler's catalog. Functional-core/imperative-shell architectures resolve the tension structurally: keep a pure core where values arrive as parameters (maximally testable), and a thin shell where queries fetch live state (maximally convenient), with the shell calling the core. When you cannot decide which way to move a value, ask which layer the function belongs to — core functions earn parameters, shell methods earn queries.
💡 The same refactoring in C#
An electricity-bill calculator, before:
// BEFORE
public class ElectricityAccount
{
private readonly int _unitsConsumed;
private readonly Tariff _tariff; // the account's own tariff plan
public ElectricityAccount(int unitsConsumed, Tariff tariff)
{
_unitsConsumed = unitsConsumed;
_tariff = tariff;
}
public int SlabFor(int units) => units <= 100 ? 1 : units <= 300 ? 2 : 3;
public decimal MonthlyBill(int units, int slab)
=> units * _tariff.RatePerUnit(slab) + _tariff.FixedCharge(slab);
}
// caller — reading the meter's own dials back to it:
var units = account.UnitsConsumed;
var slab = account.SlabFor(units);
var bill = account.MonthlyBill(units, slab);After:
// AFTER
public class ElectricityAccount
{
private readonly int _unitsConsumed;
private readonly Tariff _tariff;
public ElectricityAccount(int unitsConsumed, Tariff tariff)
{
_unitsConsumed = unitsConsumed;
_tariff = tariff;
}
private int Slab() => _unitsConsumed <= 100 ? 1 : _unitsConsumed <= 300 ? 2 : 3;
public decimal MonthlyBill()
=> _unitsConsumed * _tariff.RatePerUnit(Slab()) + _tariff.FixedCharge(Slab());
}
// caller:
var bill = account.MonthlyBill();Two parameters became zero; SlabFor collapsed into a private Slab() reading the account's own field; and a caller can no longer produce the classic mismatch bug of passing units from one month and slab computed from another. In C#, properties make the final form especially tidy — MonthlyBill could even become a get-only property if it stays cheap and side-effect-free.
🛠️ IDE support
As with its siblings, no IDE performs the whole judgement-laden refactoring in one click, but every mechanical step is automated:
- IntelliJ IDEA / Rider / WebStorm — Extract Method turns the callers' inline errand into a shared query; Inline Parameter / Change Signature (Ctrl+F6) removes the dead parameter and updates every call site; Safe Delete confirms the old public query has no remaining outside callers before you make it private. Inspections such as "parameter is never used" flag the dead-weight stage for you automatically.
- ReSharper / Visual Studio (C#) — Change Signature removes the parameter solution-wide; the unused parameter analyzer highlights step 3's intermediate state; Inline Method helps fold trivial getters. ReSharper's Introduce Parameter is the exact inverse direction — handy when you decide a query must become a parameter again for testability.
- VS Code (TypeScript / C#) — Extract to method, rename, and reference search cover the ladder; the TypeScript compiler's unused-parameter warnings (
noUnusedParameters) act as the escort during cleanup. - A small workflow tip: run the IDE's Find Usages on the query method before starting. If you find a caller passing something other than the query's result, you have discovered an override path — the strongest possible signal to pause at step 1.
⚖️ Benefits and risks
| Aspect | Benefit | Risk / Cost |
|---|---|---|
| Signature length | Parameters the receiver can derive simply vanish; direct strike on Long Parameter List | — |
| Duplication | The errand is written once, inside, instead of before every call | — |
| Consistency | One live source of truth; stale or mismatched caller values become impossible | If a caller was deliberately passing a different value (override, cached batch rate), removal silently deletes that feature — the dead-weight stage exists to catch this |
| Coupling | No new types introduced; often coupling decreases at call sites | If the fetch requires a new dependency, global, or singleton inside the method, you traded a visible parameter for hidden coupling — do not |
| Performance | Often neutral | An expensive query moved inside a hot-loop method multiplies its cost; callers can no longer compute once and reuse |
| Testability | Fewer arguments to arrange in each test | The internal fetch may drag clocks, databases, or state into tests; a parameter was trivially fakeable. The inverse refactoring (Replace Query with Parameter) exists precisely for this — these two form their own small seesaw |
That last row deserves a sentence more. Like Parameterize Method and Replace Parameter with Explicit Methods, this refactoring has a named inverse, and mature codebases travel both directions: toward the query for convenience and consistency, back toward the parameter when a function must become pure, portable, or easy to test. Neither direction is virtue; fit is.
You can map any suspicious parameter on two axes — who owns the value, and how heavy the fetch would be:
Read the corners: discountLevel and basePrice sit deep in the delete quadrant — receiver-owned, cheap to fetch. The user-typed quantity is forever a parameter. The staff override is caller-knowledge by design. And the today() call sits high on the cost axis not because it is slow, but because it is stateful — time-dependent tests are a real cost, paid for with an injectable calendar.
🩺 Which smells does it cure?
| Smell | How Replace Parameter with Method Call helps |
|---|---|
| Long Parameter List | The primary target — derivable parameters are deleted outright, the cheapest shortening available |
| Duplicate Code | The copy-pasted pre-call errand at every call site collapses into one internal query |
| Data Clumps | Thins the clump: derivable members leave the gang, and what remains is easier to bundle or pass whole |
| Feature Envy | Callers stop poking around the receiver's data to prepare arguments; knowledge returns to its owner |
| Shotgun Surgery | A change in how the value is derived now touches one query, not every caller's private copy |
🧠 The whole idea in one mindmap
📝 Quick revision box
+========= REPLACE PARAMETER WITH METHOD CALL =========+
| (2nd ed: Replace Parameter with Query) |
| |
| SMELL : caller runs an errand first |
| level = order.discountLevel() |
| order.finalPrice(base, level) |
| ...but the method owns the same shelf! |
| |
| MOVE : method fetches the value itself |
| order.finalPrice() |
| |
| LADDER: 1 eligibility - no caller-only data, |
| no new dependency |
| 2 extract errand into a query |
| 3 body uses query; param kept but IGNORED |
| 4 full test run (catches overrides!) |
| 5 remove param + delete errand lines |
| |
| KEEP THE PARAM when: only caller knows it, |
| callers override/cache it, fetch is costly, |
| or tests need to fake it easily |
| |
| KIN : whole object in caller's hand? |
| -> Preserve Whole Object instead |
+======================================================+🏋️ Practice exercise
A cinema-booking class makes every caller do the theatre's homework — refactor it:
class TicketCounter {
constructor(
private readonly priceBoard: PriceBoard, // the theatre's own board
private readonly calendar: ShowCalendar,
) {}
isWeekend(showDate: Date): boolean {
return this.calendar.isWeekendOrHoliday(showDate);
}
baseRate(seatClass: "silver" | "gold" | "recliner"): number {
return this.priceBoard.rateFor(seatClass);
}
totalFare(
seatClass: "silver" | "gold" | "recliner",
seats: number,
rate: number, // derivable!
weekend: boolean, // derivable!
): number {
const surcharge = weekend ? 1.2 : 1.0;
return seats * rate * surcharge;
}
}
// caller:
const rate = counter.baseRate("gold");
const weekend = counter.isWeekend(showDate);
const fare = counter.totalFare("gold", 3, rate, weekend);Your tasks:
- For each of the four parameters of
totalFare, write one line: caller-knowledge or receiver-knowledge? (Careful:weekendis derivable, but only ifshowDateis available — what must happen to the signature for that?) - Apply the safe ladder: make
rateandweekendinternally fetched while still accepting them, run your tests, then remove them. Your final signature should betotalFare(seatClass, seats, showDate). - Explain in two sentences why
seatClassandseatsmust remain parameters forever. - Override hunt: one caller — the staff-discount screen — was passing
rate * 0.5instead ofbaseRate(...). Your step-4 test run catches it. Propose two designs that keep the discount working (hint: an explicitstaffFaremethod, or a discount policy the counter itself can query). - Testability question:
isWeekendconsults the calendar, and the calendar consults real holidays. Describe how you would testtotalFarefor a holiday show without waiting for an actual holiday — and say which refactoring you would reach for if the calendar were a hard-coded global instead of a constructor dependency. - Kin check: a teammate notes the caller holds a
Showobject containingseatClass,seats, andshowDate, and suggeststotalFare(show). Which refactoring is that, and would you combine it with this one? Justify briefly. - Chart it: place all four original parameters of
totalFareon the quadrant chart from Figure 10, plus the staff-override rate from task 4. Which quadrant does each occupy, and does your placement agree with the final signature from task 2?
Frequently asked questions
- What does Replace Parameter with Method Call do, in simple words?
- It removes a parameter that the method could fetch for itself. If every caller first computes a value — like a discount level — and then passes it in, but the method has access to the same source, delete the parameter and let the method call the source directly. Don't tell the shopkeeper a price he can read from his own price list.
- Is this the same as Fowler's Replace Parameter with Query?
- Yes. The first edition of Refactoring (1999) called it Replace Parameter with Method Call (sometimes Replace Parameter with Method); the second edition (2018) renamed it Replace Parameter with Query. Refactoring Guru and SourceMaking keep the older name. The mechanics are the same, and its exact inverse is Replace Query with Parameter.
- When must the parameter stay?
- When the value comes from caller-specific context the method cannot see, when fetching it inside would force the method to grab a new dependency or global, when callers deliberately pass different or cached values, or when the computation is expensive or has side effects that callers were controlling. A parameter the method cannot honestly derive is not redundant.
- Doesn't this hurt testability?
- It can — that is the honest fine print. A parameter is the easiest thing in the world to fake in a test; an internal call to getDiscountLevel() may drag in state you must set up first. If the internal source is itself simple and deterministic, the trade is fine. If it touches clocks, databases, or network, keeping the parameter (or injecting the dependency) is often the wiser design.
- How does it relate to Preserve Whole Object?
- Both shorten parameter lists, from different directions. Preserve Whole Object applies when arguments are fields pulled off one object the caller holds — pass the object. Replace Parameter with Method Call applies when the argument is something the METHOD itself can compute or look up — pass nothing. Ask: who already has natural access to this value? That answer picks the refactoring.
Further reading
Related Lessons
Preserve Whole Object: Show the Whole ID Card
Learn the Preserve Whole Object refactoring with a school ID card story, TypeScript and C# examples, safe step-by-step mechanics, and an honest look at the coupling cost of passing whole objects.
Parameterize Method: One Juice Recipe with a Size Input
Learn the Parameterize Method refactoring with a juice stall story, TypeScript and C# examples, safe step-by-step mechanics, and the seesaw rule that pairs it with Replace Parameter with Explicit Methods.
Replace Parameter with Explicit Methods: Name Boards Instead of Secret Codes
Learn the Replace Parameter with Explicit Methods refactoring with a bank counter story, TypeScript and Python examples, safe mechanics, and the seesaw rule that pairs it with Parameterize Method.
Long Parameter List: The Chai Order That Took Ten Instructions
Long Parameter List code smell made simple — why methods with too many arguments cause bugs, and how parameter objects make calls short, clear, and safe.