Skip to content

Replace Parameter with Query

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.

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.

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.

// Before
function 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);
// After
function 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);
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
A derivable parameter is removed and computed inside
  1. 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.
  2. Extract the derivation into a query, if it is not already one.
  3. Inside the function, replace each use of the parameter with a call to that query.
  4. Remove the parameter from the signature, then delete the now-dead computation from every caller. Run your tests after each caller.

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.

When is a parameter a good candidate for Replace Parameter with Query?
What risk does removing such a parameter eliminate?
What is the inverse refactoring and why use it?
When should you NOT replace a parameter with a query?