# Long Parameter List

> A function takes so many parameters that callers mix up their order and every new option changes every call site.

- kind: smell
- category: bloaters
- aliases: Too Many Parameters
- canonical: https://aipatterns.dev/refactoring/smells/long-parameter-list
- inspired-by: https://refactoring.guru/smells/long-parameter-list

## 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.

```ts title="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.

```ts title="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](https://aipatterns.dev/refactoring/techniques/replace-parameter-with-method-call.md).
- If the caller unpacks several fields from one object, use [Preserve Whole Object](https://aipatterns.dev/refactoring/techniques/preserve-whole-object.md).
- If several parameters always appear together and form a concept (range, address, coordinates), use [Introduce Parameter Object](https://aipatterns.dev/refactoring/techniques/introduce-parameter-object.md).
- If a boolean or enum parameter switches between distinct behaviors, use [Replace Parameter with Explicit Methods](https://aipatterns.dev/refactoring/techniques/replace-parameter-with-explicit-methods.md): `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](https://aipatterns.dev/refactoring/smells/large-class.md), 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.
