# Pull Up Constructor Body

> Move initialization code shared by subclass constructors into a superclass constructor and call it with super().

- kind: technique
- category: dealing-with-generalization
- canonical: https://aipatterns.dev/refactoring/techniques/pull-up-constructor-body
- inspired-by: https://refactoring.guru/pull-up-constructor-body

## Problem

Subclass constructors repeat the same assignments and validation for fields that belong to the superclass. Constructors cannot be inherited, so the duplication cannot be removed with [Pull Up Method](https://aipatterns.dev/refactoring/techniques/pull-up-method.md).

## Solution

**Create (or extend) a superclass constructor with the shared code, and call it from each subclass constructor with `super(...)`.**

## Before

```ts
abstract class Job {
  protected id!: string;
  protected tenantId!: string;
  protected attempts!: number;
  abstract run(): Promise<void>;
}

class InvoiceEmailJob extends Job {
  constructor(tenantId: string, private invoiceId: string) {
    super();
    if (!tenantId) throw new Error("tenantId required");
    this.id = crypto.randomUUID();
    this.tenantId = tenantId;
    this.attempts = 0;
  }
  async run() { await sendInvoiceEmail(this.tenantId, this.invoiceId); }
}

class UsageRollupJob extends Job {
  constructor(tenantId: string, private day: string) {
    super();
    if (!tenantId) throw new Error("tenantId required");
    this.id = crypto.randomUUID();
    this.tenantId = tenantId;
    this.attempts = 0;
  }
  async run() { await rollupUsage(this.tenantId, this.day); }
}
```

## After

```ts
abstract class Job {
  protected readonly id = crypto.randomUUID();
  protected attempts = 0;

  constructor(protected readonly tenantId: string) {
    if (!tenantId) throw new Error("tenantId required");
  }
  abstract run(): Promise<void>;
}

class InvoiceEmailJob extends Job {
  constructor(tenantId: string, private invoiceId: string) {
    super(tenantId);
  }
  async run() { await sendInvoiceEmail(this.tenantId, this.invoiceId); }
}

class UsageRollupJob extends Job {
  constructor(tenantId: string, private day: string) {
    super(tenantId);
  }
  async run() { await rollupUsage(this.tenantId, this.day); }
}
```

## Why

- Validation of superclass fields happens in one place, so no subclass can skip it.
- The superclass owns its own invariants, and the definite-assignment `!` hacks disappear.

## Steps

1. Identify statements that are identical in every subclass constructor and touch only superclass fields.
2. Add a superclass constructor that takes the needed values, or extend the existing one. Run the tests.
3. In one subclass, pass the values to `super(...)` and delete the duplicated statements. Run the tests. Repeat for each subclass.
4. Move shared code that runs *after* subclass-specific setup into a superclass method that each subclass constructor calls at the end, if it must stay last.

## Watch out

- Order matters. In TypeScript, Java and C#, `super(...)` runs before subclass field initializers. Shared code that depends on subclass fields cannot move into the superclass constructor.
- Do not call overridable methods from the superclass constructor. The subclass override runs before its own fields are initialized.
- If constructors are getting complicated, consider [Replace Constructor with Factory Method](https://aipatterns.dev/refactoring/techniques/replace-constructor-with-factory-method.md) to centralize creation instead.
- There is no named inverse. To push initialization back down, inline the superclass constructor into each subclass.

## Related

- Fixes [Duplicate Code](https://aipatterns.dev/refactoring/smells/duplicate-code.md) in constructors.
- Usually done with [Pull Up Field](https://aipatterns.dev/refactoring/techniques/pull-up-field.md); the constructor analog of [Pull Up Method](https://aipatterns.dev/refactoring/techniques/pull-up-method.md).

## Agent checklist

- [ ] Moved statements touch only superclass fields.
- [ ] No moved statement depends on subclass state that is set later.
- [ ] The superclass constructor calls no overridable methods.
- [ ] Every subclass passes the same values to `super` that it used to assign.
