Do You Know How It Works? — JavaScript Factory Functions

TL;DR: Factory Functions are functions that build and return objects based on input parameters — just like a real-world factory. Unlike creating objects manually, they solve key issues like code repetition, poor maintainability, lack of scalability, and unprotected data structures. In this post, we explore how Factory Functions work, what problems they solve, and how they can be used both on the backend and frontend to write clean, secure, and scalable code. Introduction What is a Factory Function, why do we need it, and where is it used? Is this just a fancy term for explaining a dry theory about something unnecessary — or is it a truly useful concept for JavaScript developers? Welcome to Episode 3 of the “Do You Know How It Works?” series! In this post, we’ll take a fun and practical approach to understanding Factory Functions — what they are, what problems they solve, and how they can seriously improve the way we structure our code. And don’t worry, this isn’t just theory — we’re going to build examples step-by-step and apply it in a real use case. In short, factory functions are just functions that build something and return what was built. That “something” could be an object, another function — anything. In this article, we’ll focus on objects as our primary example. “But Matheus, if I want to build an object, I can just use const obj = {...} and move on, right?” Sure — and sometimes that’ll be enough. But as your application grows, that approach can start causing problems. Especially when you're creating lots of similar objects across different places. To show you what I mean, let’s build a pet registration system for a pet shop. Along the way, we’ll explore the issues that come up with the “manual” approach. Problem 1 — Code Repetition const pet1 = { name: "Katze", ownerName: "Matheus", identify() { return `${this.name} belongs to ${this.ownerName}`; } }; const pet2 = { name: "Hund", ownerName: "Pedro", identify() { return `${this.name} belongs to ${this.ownerName}`; } }; const pet3 = { name: "Vogel", ownerNme: "Miguel", // Typo here identify() { return `${this.name} belongs to ${this.ownerName}`; } }; console.log(pet1.identify()); // Katze belongs to Matheus console.log(pet2.identify()); // Hund belongs to Pedro console.log(pet3.identify()); // Vogel belongs to undefined Houston, we have the first problem: Code repetition. If you’ve read the article about inheritance, you know where this is going: repeating logic is a practice practice considered terrible, condemnable, abominable, disastrous, horrible, catastrophic… as Cap would say: In this example, we’re duplicating the structure of the object and its method for each new pet. Every time I had to create a new pet, I had to write the entire object and repeat the logic. In the programming world, having to repeat logic multiple times in your code will cause a huge delay and make you more prone to errors. Notice that in the third pet, the attribute representing the owner's name was written as ownerNme and not ownerName. Seems like a silly mistake, but trust me — from experience — after 5–6 hours of straight coding, finding a typo is harder than finding a logic error. “But mate, if I use Copy+Paste, it’ll be quick to create the objects and I won’t mistype the attribute name.” Don't use useful resources to justify badly written code. In a large-scale codebase, you might not mistype the attribute, but copying and pasting 800 different pets won’t be fast at all, it’s absolute madness. Problem 1: Repeting code. Problem 2 — Maintainability Let’s say I want to rename the name property to petName, for better readability. Here’s what happens: const pet1 = { petName: "Katze", ownerName: "Matheus", identify() { return `${this.petName} belongs to ${this.ownerName}`; } }; const pet2 = { petName: "Hund", ownerName: "Pedro", identify() { return `${this.petName} belongs to ${this.ownerName}`; } }; const pet3 = { petName: "Vogel", ownerName: "Miguel", identify() { return `${this.petName} belongs to ${this.ownerName}`; } }; console.log(pet1.identify()); // Katze belongs to Matheus console.log(pet2.identify()); // Hund belongs to Pedro console.log(pet3.identify()); // Vogel belongs to Miguel And thus, the second problem is on our path: Maintaining the code. When I wanted to edit the objects, I had to find and change the attribute in each of them, and once again, I was at risk of making a mistake. Also, in a large-scale codebase, you may have many of these objects scattered throughout the application, in dozens or hundreds of places. “But Matheus, could just use find-and-replace in my IDE and change all name for petName.” Don't use useful resources to justify badly written code. Sure, your IDE may indeed find all the name and replace with petName, but what if other objects — like employees — also have a name

Apr 14, 2025 - 14:17
 0
Do You Know How It Works? — JavaScript Factory Functions

TL;DR:

Factory Functions are functions that build and return objects based on input parameters — just like a real-world factory. Unlike creating objects manually, they solve key issues like code repetition, poor maintainability, lack of scalability, and unprotected data structures. In this post, we explore how Factory Functions work, what problems they solve, and how they can be used both on the backend and frontend to write clean, secure, and scalable code.

Introduction

What is a Factory Function, why do we need it, and where is it used? Is this just a fancy term for explaining a dry theory about something unnecessary — or is it a truly useful concept for JavaScript developers?

Welcome to Episode 3 of the “Do You Know How It Works?” series!

In this post, we’ll take a fun and practical approach to understanding Factory Functions — what they are, what problems they solve, and how they can seriously improve the way we structure our code. And don’t worry, this isn’t just theory — we’re going to build examples step-by-step and apply it in a real use case.

In short, factory functions are just functions that build something and return what was built. That “something” could be an object, another function — anything.

In this article, we’ll focus on objects as our primary example.

“But Matheus, if I want to build an object, I can just use const obj = {...} and move on, right?”

Sure — and sometimes that’ll be enough. But as your application grows, that approach can start causing problems. Especially when you're creating lots of similar objects across different places.

To show you what I mean, let’s build a pet registration system for a pet shop. Along the way, we’ll explore the issues that come up with the “manual” approach.

Problem 1 — Code Repetition

const pet1 = {
  name: "Katze",
  ownerName: "Matheus",
  identify() {
    return `${this.name} belongs to ${this.ownerName}`;
  }
};

const pet2 = {
  name: "Hund",
  ownerName: "Pedro",
  identify() {
    return `${this.name} belongs to ${this.ownerName}`;
  }
};

const pet3 = {
  name: "Vogel",
  ownerNme: "Miguel", // Typo here
  identify() {
    return `${this.name} belongs to ${this.ownerName}`;
  }
};

console.log(pet1.identify()); // Katze belongs to Matheus
console.log(pet2.identify()); // Hund belongs to Pedro
console.log(pet3.identify()); // Vogel belongs to undefined

Houston, we have the first problem: Code repetition.

If you’ve read the article about inheritance, you know where this is going: repeating logic is a practice practice considered terrible, condemnable, abominable, disastrous, horrible, catastrophic… as Cap would say:

Captain America Meme

In this example, we’re duplicating the structure of the object and its method for each new pet. Every time I had to create a new pet, I had to write the entire object and repeat the logic. In the programming world, having to repeat logic multiple times in your code will cause a huge delay and make you more prone to errors. Notice that in the third pet, the attribute representing the owner's name was written as ownerNme and not ownerName. Seems like a silly mistake, but trust me — from experience — after 5–6 hours of straight coding, finding a typo is harder than finding a logic error.

“But mate, if I use Copy+Paste, it’ll be quick to create the objects and I won’t mistype the attribute name.”

Don't use useful resources to justify badly written code.

In a large-scale codebase, you might not mistype the attribute, but copying and pasting 800 different pets won’t be fast at all, it’s absolute madness.

Problem 1: Repeting code.

Problem 2 — Maintainability

Let’s say I want to rename the name property to petName, for better readability. Here’s what happens:

const pet1 = {
  petName: "Katze",
  ownerName: "Matheus",
  identify() {
    return `${this.petName} belongs to ${this.ownerName}`;
  }
};

const pet2 = {
  petName: "Hund",
  ownerName: "Pedro",
  identify() {
    return `${this.petName} belongs to ${this.ownerName}`;
  }
};

const pet3 = {
  petName: "Vogel",
  ownerName: "Miguel",
  identify() {
    return `${this.petName} belongs to ${this.ownerName}`;
  }
};

console.log(pet1.identify()); // Katze belongs to Matheus
console.log(pet2.identify()); // Hund belongs to Pedro
console.log(pet3.identify()); // Vogel belongs to Miguel

And thus, the second problem is on our path: Maintaining the code.
When I wanted to edit the objects, I had to find and change the attribute in each of them, and once again, I was at risk of making a mistake. Also, in a large-scale codebase, you may have many of these objects scattered throughout the application, in dozens or hundreds of places.

“But Matheus, could just use find-and-replace in my IDE and change all name for petName.”

Don't use useful resources to justify badly written code.
Sure, your IDE may indeed find all the name and replace with petName, but what if other objects — like employees — also have a name property?
Congratulations, your employees now have ✨petNames✨.

Problem 2: Code is hard to maintain.

Problem 3 — Scalability

Still in this line of modifying the object, now I want to add another parameter to my pet: the animal’s species. To do this, I’ll modify the object again.

const pet1 = {
  petName: "Katze",
  ownerName: "Matheus",
  species: "Cat",
  identify() {
    return `The ${this.species} ${this.petName} belongs to ${this.ownerName}`;
  }
};

const pet2 = {
  petName: "Hund",
  ownerName: "Pedro",
  species: "Dog",
  identify() {
    return `The ${this.species} ${this.petName} belongs to ${this.ownerName}`;
  }
};

const pet3 = {
  petName: "Vogel",
  ownerName: "Miguel",
  species: "Bird",
  identify() {
    return `The ${this.species} ${this.petName} belongs to ${this.ownerName}`;
  }
};


console.log(pet1.identify()); // The cat Katze belongs to Matheus
console.log(pet2.identify()); // The dog Hund belongs to Pedro
console.log(pet3.identify()); // The bird Vogel belongs to Miguel

Shine on us, oh mighty third problem: Scalability.
Again, to add this attribute, I had to go through each object and also modify each function inside those objects. So, in order to add features to my code, I had to go on a hunting adventure to find all objects in my codebase, and risk breaking something in the process.
A huge amount of work that could be avoided.

“But I…”

Dude, don't even start. You already know what I’ll say.