Replace Parameter with Query
Intent
Section titled “Intent”If a function can derive a parameter’s value from the data it already receives, that parameter is dead weight. Every caller has to compute and pass it, and they can disagree. Remove it, and let the function ask for the value itself — a query — so there is one source of truth and one fewer thing to get wrong at the call site.
The smell
Section titled “The smell”A parameter is always passed the same way, computed from another argument the function already has. You see callers run the same little calculation before every call, then hand the result in. Worse, two callers compute it slightly differently, so the function behaves inconsistently depending on who calls it. The parameter offers flexibility nobody actually wants.
Before → After
Section titled “Before → After”finalPrice takes a quantity and a discount, but the discount is wholly determined by the quantity. Callers keep computing it. We remove the parameter and derive the discount inside.
// Beforefunction finalPrice(quantity: number, discount: number): number { const base = quantity * 5; return base - base * discount;}
const d = quantity > 100 ? 0.1 : 0;const price = finalPrice(quantity, d);
// Afterfunction finalPrice(quantity: number): number { const base = quantity * 5; return base - base * discountFor(quantity);}
function discountFor(quantity: number): number { return quantity > 100 ? 0.1 : 0;}
const price = finalPrice(quantity);# Beforedef final_price(quantity, discount): base = quantity * 5 return base - base * discount
d = 0.1 if quantity > 100 else 0price = final_price(quantity, d)
# Afterdef final_price(quantity): base = quantity * 5 return base - base * discount_for(quantity)
def discount_for(quantity): return 0.1 if quantity > 100 else 0
price = final_price(quantity)// Beforefunc FinalPrice(quantity int, discount float64) float64 { base := float64(quantity) * 5 return base - base*discount}
d := 0.0if quantity > 100 { d = 0.1}price := FinalPrice(quantity, d)
// Afterfunc FinalPrice(quantity int) float64 { base := float64(quantity) * 5 return base - base*discountFor(quantity)}
func discountFor(quantity int) float64 { if quantity > 100 { return 0.1 } return 0}
price := FinalPrice(quantity)// Beforefn final_price(quantity: i32, discount: f64) -> f64 { let base = quantity as f64 * 5.0; base - base * discount}
let d = if quantity > 100 { 0.1 } else { 0.0 };let price = final_price(quantity, d);
// Afterfn final_price(quantity: i32) -> f64 { let base = quantity as f64 * 5.0; base - base * discount_for(quantity)}
fn discount_for(quantity: i32) -> f64 { if quantity > 100 { 0.1 } else { 0.0 }}
let price = final_price(quantity);flowchart LR
subgraph Before["Before"]
A["finalPrice(qty, discount)"] --> B["caller computes discount<br/>from qty first"]
end
subgraph After["After"]
C["finalPrice(qty)"] --> D["derives discount<br/>internally"]
end
Before -.->|"Replace Parameter with Query"| After Mechanics
Section titled “Mechanics”- Confirm the parameter can be computed entirely from data the function already has — other parameters, fields, or a reachable query. If it depends on anything external, stop: removing it would smuggle in a hidden dependency.
- Extract the derivation into a query, if it is not already one.
- Inside the function, replace each use of the parameter with a call to that query.
- Remove the parameter from the signature, then delete the now-dead computation from every caller. Run your tests after each caller.
When to use / trade-offs
Section titled “When to use / trade-offs”Use this when callers all compute the argument the same way, when the value is fully determined by data the function can reach, or when you want to remove the risk of a caller passing an inconsistent value. Fewer parameters means a simpler, harder-to-misuse interface.
The inverse is Replace Query with Parameter. Sometimes deriving a value inside the function creates a hidden dependency — the function reaches out to global state, a clock, or a singleton — making it harder to test and reason about. There you do the opposite: lift the derivation out, accept the value as a parameter, and let the caller supply it. That trades a leaner signature for an explicit, injectable dependency. Choose by what you value more here: a small interface, or a function with no hidden reach.