Skip to main content

Template Method Pattern

Define the skeleton of an algorithm, deferring some steps to subclasses (or in FP: to injected functions).


The Problem​

You're building a resume generator. Every resume follows the same structure: Header β†’ Summary β†’ Experience β†’ Skills β†’ Education. But a developer's resume highlights TypeScript and system design, a designer's highlights Figma and design systems, and a manager's highlights team building and OKRs.

The naive approach:

function buildDeveloperResume() {
return [
devHeader(),
devSummary(),
devExperience(),
devSkills(),
devEducation(),
];
}

function buildDesignerResume() {
return [
designerHeader(),
designerSummary(),
designerExperience(),
designerSkills(),
designerEducation(),
];
}
// Same skeleton repeated for every profile

The structure is identical. Only the content of each step changes. Adding a new section means updating every function.


The Solution​

Define the skeleton once with default steps. Each profile only overrides what's different:

import { templateWithDefaults } from "@pithos/core/eidos/template/template";

const buildResume = templateWithDefaults(
(steps: ResumeSteps) =>
(): ResumeSection[] => [
steps.header(),
steps.summary(),
steps.experience(),
steps.skills(),
steps.education(),
],
DEFAULT_STEPS, // base implementation for all 5 steps
);

// Developer: override 3 steps, keep default header + education
const devResume = buildResume({
summary: () => ({ title: "Summary", content: ["Full-stack developer with 7+ years..."] }),
experience: () => ({ title: "Experience", content: ["Senior Engineer β€” Stripe..."] }),
skills: () => ({ title: "Skills", content: ["TypeScript Β· React Β· Node.js..."] }),
});

// Designer: override all 5 steps (different portfolio link, different education)
const designerResume = buildResume({
header: () => ({ title: "Alex Johnson", content: ["...", "portfolio.alexj.design"] }),
summary: () => ({ title: "Summary", content: ["Product designer with 6+ years..."] }),
experience: () => ({ title: "Experience", content: ["Senior Designer β€” Figma..."] }),
skills: () => ({ title: "Skills", content: ["Figma Β· Design Systems..."] }),
education: () => ({ title: "Education", content: ["M.F.A. Interaction Design β€” RISD"] }),
});

The skeleton (Header β†’ Summary β†’ Experience β†’ Skills β†’ Education) is defined once. Each profile only specifies what's different. templateWithDefaults merges the overrides with the defaults automatically.


Live Demo​

Switch between Developer, Designer, and Manager profiles. The template skeleton is always the same 5 steps in the same order. The step pipeline on the left shows which steps are overridden (amber) vs using defaults (gray). When you switch profiles, the steps execute sequentially to show the template in action.

Code
/**
* Resume builder using the Template Method pattern.
*
* The skeleton is fixed: Header β†’ Summary β†’ Experience β†’ Skills β†’ Education.
* Each profile overrides specific steps via `templateWithDefaults`.
*/

import { templateWithDefaults } from "@pithos/core/eidos/template/template";
import { DEFAULT_STEPS } from "@/data/profiles";
import { PROFILE_OVERRIDES } from "@/data/overrides";
import type { ProfileKey, ResumeData, ResumeSection, ResumeSteps } from "./types";

/** The skeleton β€” step order never changes */
const buildResume = templateWithDefaults(
(steps: ResumeSteps) =>
(): ResumeSection[] => [
steps.header(),
steps.summary(),
steps.experience(),
steps.hardSkills(),
steps.softSkills(),
steps.education(),
],
DEFAULT_STEPS,
);

/** Get the list of step keys that a profile overrides */
export function getOverrides(profile: ProfileKey): (keyof ResumeSteps)[] {
return Object.keys(PROFILE_OVERRIDES[profile]) as (keyof ResumeSteps)[];
}

/** Generate a resume for a given profile */
export function generateResume(profile: ProfileKey): ResumeData {
const overrides = PROFILE_OVERRIDES[profile];
const generator = buildResume(overrides);
return {
sections: generator(),
overrides: getOverrides(profile),
};
}
Result

Real-World Analogy​

A recipe template. "Make soup: 1) prep ingredients, 2) boil water, 3) add ingredients, 4) simmer, 5) serve." The template is the same for all soups. Each soup recipe just fills in what ingredients to prep and add.


When to Use It​

  • Multiple algorithms share the same structure
  • You want to enforce a sequence of steps
  • Some steps vary while others stay constant
  • You need sensible defaults with selective overrides

When NOT to Use It​

If every step is different for every variant, there's no shared skeleton. You just have different functions. Template Method shines when the structure is fixed and only the content varies.


API​