Introduce Parameter Object
Intent
Section titled “Intent”Find a group of arguments that keep appearing together across several functions, and replace them with a single object that holds them all. The cluster gets a name, the relationship between the values becomes explicit, and every function that took the loose arguments now takes one tidy parameter.
The smell
Section titled “The smell”This cures the Long Parameter List and its cousin the Data Clump — the same two or three values passed side by side, in the same order, to function after function. A start and end that are really a date range. A latitude and longitude that are really a location. When values travel as a pack, that pack is a concept the code has not named yet. Worse, a long bare list invites the classic mistake of passing the arguments in the wrong order.
Before → After
Section titled “Before → After”A flight booking that drags four loosely-related arguments around. After, they become a single Trip object.
// Beforefunction bookFlight( from: string, to: string, startDate: string, endDate: string,): string { return `Flight ${from}->${to} from ${startDate} to ${endDate}`;}
const summary = bookFlight("BKK", "NRT", "2026-07-01", "2026-07-09");
// Afterinterface Trip { from: string; to: string; startDate: string; endDate: string;}
function bookFlight(trip: Trip): string { return `Flight ${trip.from}->${trip.to} from ${trip.startDate} to ${trip.endDate}`;}
const summary = bookFlight({ from: "BKK", to: "NRT", startDate: "2026-07-01", endDate: "2026-07-09",});# Beforedef book_flight(origin, destination, start_date, end_date): return f"Flight {origin}->{destination} from {start_date} to {end_date}"
summary = book_flight("BKK", "NRT", "2026-07-01", "2026-07-09")
# Afterfrom dataclasses import dataclass
@dataclassclass Trip: origin: str destination: str start_date: str end_date: str
def book_flight(trip): return ( f"Flight {trip.origin}->{trip.destination} " f"from {trip.start_date} to {trip.end_date}" )
summary = book_flight(Trip("BKK", "NRT", "2026-07-01", "2026-07-09"))// Beforefunc BookFlight(from, to, startDate, endDate string) string { return fmt.Sprintf("Flight %s->%s from %s to %s", from, to, startDate, endDate)}
summary := BookFlight("BKK", "NRT", "2026-07-01", "2026-07-09")
// Aftertype Trip struct { From string To string StartDate string EndDate string}
func BookFlight(t Trip) string { return fmt.Sprintf("Flight %s->%s from %s to %s", t.From, t.To, t.StartDate, t.EndDate)}
summary := BookFlight(Trip{ From: "BKK", To: "NRT", StartDate: "2026-07-01", EndDate: "2026-07-09",})// Beforefn book_flight(from: &str, to: &str, start_date: &str, end_date: &str) -> String { format!("Flight {from}->{to} from {start_date} to {end_date}")}
let summary = book_flight("BKK", "NRT", "2026-07-01", "2026-07-09");
// Afterstruct Trip { from: String, to: String, start_date: String, end_date: String,}
fn book_flight(trip: &Trip) -> String { format!( "Flight {}->{} from {} to {}", trip.from, trip.to, trip.start_date, trip.end_date )}
let trip = Trip { from: "BKK".to_string(), to: "NRT".to_string(), start_date: "2026-07-01".to_string(), end_date: "2026-07-09".to_string(),};let summary = book_flight(&trip);flowchart LR
subgraph Before["Before"]
A["bookFlight(<br/>from, to,<br/>startDate, endDate)"]
end
subgraph After["After"]
B["bookFlight(trip)"]
B --> C["Trip<br/>{ from, to,<br/>startDate, endDate }"]
end
Before -.->|"Introduce Parameter Object"| After Mechanics
Section titled “Mechanics”- Identify the clump — the arguments that always appear together. Pick a name for the concept they represent.
- Create a new structure (a class, struct, record, or interface) with a field for each member of the clump.
- Add the new object as a parameter to one target function, alongside the existing arguments at first. Run your tests.
- Inside the body, start reading from the object’s fields instead of the loose parameters, one field at a time, testing as you go.
- Once the body reads only from the object, remove the now-unused loose parameters from the signature, then update that function’s callers to build and pass the object.
- Repeat for the other functions that share the same clump, so they all take the object.
- Run your tests after each function. Watch for new behaviour to emerge: once the data lives together, logic that operated on it often wants to move onto the object too.
When to use / trade-offs
Section titled “When to use / trade-offs”Introduce a parameter object when the same values are passed together repeatedly, when a parameter list has grown long enough that callers get the order wrong, or when you sense an unnamed concept hiding inside the argument list. The object documents the relationship and gives behaviour that belongs to the group a natural home.
The trade-off is an extra type to define and a small amount of ceremony to construct it at each call. For a one-off two-argument call that never recurs, that ceremony may not pay for itself. The win grows with reuse: the more functions share the clump, the more the object earns its keep — and the more it becomes a magnet for related methods.