Code smellBloaters
Long Parameter List
A function takes so many parameters that callers mix up their order and every new option changes every call site.
- Also known as
- Too Many Parameters
Signs
- The signature has more than four or five parameters, especially several of the same type in a row.
- Call sites pass
null,undefinedorfalsefor most arguments to reach the one they care about. - Boolean flags appear in the middle of the list:
render(doc, true, false, true). - The caller pulls several fields off one object only to pass them in separately.
- Adding an option means touching every caller, even those that never use it.
Why it happens
Parameters are the honest alternative to global state, so developers add them. Then each new requirement adds one more. Sometimes several algorithms were merged into one function and the flags pick between them. Sometimes the function was decoupled from an object by unpacking that object's fields at every call site.
Example
A report export function that grew a flag per feature request.
function exportReport(
reportId: string,
from: Date,
to: Date,
format: "csv" | "pdf",
includeCharts: boolean,
timezone: string,
locale: string,
recipientEmail: string | null,
compress: boolean,
): Promise<Blob> {
/* ... */
}
exportReport(r.id, r.from, r.to, "pdf", true, user.timezone, user.locale, null, false);Grouping the date range, taking the user as a whole, and splitting the "deliver" concern out leaves a short signature.
interface DateRange { from: Date; to: Date }
interface ExportOptions {
format: "csv" | "pdf";
includeCharts?: boolean;
compress?: boolean;
}
function exportReport(reportId: string, range: DateRange, viewer: User, options: ExportOptions): Promise<Blob> {
const { timezone, locale } = viewer;
/* ... */
}
async function emailReport(blob: Blob, to: string): Promise<void> { /* ... */ }
const pdf = await exportReport(r.id, r.range, user, { format: "pdf", includeCharts: true });Treatment
- If the function can get a value itself from an object it already has access to, use Replace Parameter with Method Call.
- If the caller unpacks several fields from one object, use Preserve Whole Object.
- If several parameters always appear together and form a concept (range, address, coordinates), use Introduce Parameter Object.
- If a boolean or enum parameter switches between distinct behaviors, use Replace Parameter with Explicit Methods:
exportCsv()andexportPdf(). - If the remaining parameters are optional tuning knobs, collect them in an options object with defaults. In languages with named arguments (Python, Kotlin, C#), keyword arguments solve the readability half of the problem.
Payoff
- Call sites become readable without an IDE showing parameter names.
- Swapped-argument bugs disappear when distinct concepts have distinct types.
- New optional settings no longer ripple through every caller.
- Hidden concepts (
DateRange) get a name and can grow behavior, such ascontains(date).
When to ignore
- Passing a whole object would create a dependency you deliberately avoided, for example passing a full
Userinto a low-level formatting module. A few primitives keep the module decoupled. - Pure math or geometry functions where the parameters are the natural inputs:
lerp(a, b, t),clamp(value, min, max). - Constructors used with dependency injection, where each parameter is a distinct collaborator. A long list there points to Large Class, not to the list itself.
Agent checklist
- Each grouped parameter object represents a real concept with a name a domain expert would recognize, not a bag called
Params. - Boolean flags that select behavior are replaced by separate functions or an enum, not hidden inside an options object.
- Every call site is updated in the same change, or an overload or adapter keeps old callers working with a deprecation note.
- Defaults for optional settings are defined once, at the function, not repeated at call sites.
- Tests pass with no change to their expected outputs.
Treatments
- Replace Parameter with Method CallStop passing a value the method can obtain itself; have the method call the query directly and drop the parameter.
- Preserve Whole ObjectPass the object itself instead of several values pulled out of it, so the method signature stays stable as needs change.
- Introduce Parameter ObjectReplace a group of parameters that always travel together with one object type, then move behavior on that group into it.
- Replace Parameter with Explicit MethodsSplit a method that branches on a flag or enum argument into one clearly named method per value.
See also
- Data ClumpsThe same group of variables travels together through fields and parameters, signaling a missing object that should own them.
- Primitive ObsessionDomain concepts like money, emails, IDs or statuses are passed around as raw strings and numbers, so their rules get re-checked everywhere.
- Long MethodA function that does several jobs in a row and has grown too long to read, name or test as one unit.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.