Skip to content
aipatterns.dev

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, undefined or false for 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.

exportReport.ts
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.

exportReport.ts
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() and exportPdf().
  • 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 as contains(date).

When to ignore

  • Passing a whole object would create a dependency you deliberately avoided, for example passing a full User into 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

See also

Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.