Builder Design Pattern
Builder is a Creational Design Pattern that lets you construct complex objects step by step. Instead of creating huge constructors with endless parameters, the Builder pattern separates the construction process from the final representation of the object.
It is one of the most commonly used patterns in enterprise applications, SDKs, ORMs, query builders, UI frameworks, and configuration-heavy systems.
Real-World Analogy
Imagine ordering a burger.
You don’t tell the chef:
new Burger(true, true, false, true, 2, "large", true);Instead, you say:
- Add cheese
- Double patty
- Extra sauce
- Large fries
The chef builds the burger step by step.
That’s exactly what the Builder pattern does.
Problem
Suppose you have a User class.
class User
{
public function __construct(
string $name,
string $email,
string $password,
?string $phone = null,
?string $address = null,
?string $company = null,
?string $website = null,
bool $isAdmin = false,
bool $isVerified = false,
bool $newsletter = false
) {}
}Now imagine using it:
$user = new User(
"Hassan",
"hassan@example.com",
"secret",
null,
null,
"Acme Inc",
null,
false,
true,
true
);Problems:
- Constructor becomes unreadable
- Parameter order becomes dangerous
- Too many optional arguments
- Hard to maintain
- Hard to scale
- Easy to introduce bugs
This problem is commonly known as the Telescoping Constructor Problem.
Solution
The Builder pattern moves object construction into a separate builder class.
Instead of passing everything into a constructor, you build the object gradually.
$user = UserBuilder::make()
->name("Hassan")
->email("hassan@example.com")
->company("Acme Inc")
->verified()
->subscribedToNewsletter()
->build();Now the code is:
- Readable
- Flexible
- Self-documenting
- Easy to extend
- Safer
Structure
The Builder pattern usually contains these components:
| Component | Responsibility |
| ------------------- | ------------------------------ |
| Builder Interface | Defines building steps |
| Concrete Builder | Implements actual construction |
| Product | Final object being created |
| Director (Optional) | Controls build order |
| Client | Uses builder to create object |Basic UML Flow
Client
|
v
Director ------> Builder Interface
|
v
Concrete Builder
|
v
ProductSimple PHP Example
Product
class Burger
{
public array $items = [];
public function show(): void
{
echo implode(', ', $this->items);
}
}Builder Interface
interface BurgerBuilder
{
public function addBun(): self;
public function addPatty(): self;
public function addCheese(): self;
public function addSauce(): self;
public function build(): Burger;
}Concrete Builder
class ZingerBurgerBuilder implements BurgerBuilder
{
private Burger $burger;
public function __construct()
{
$this->burger = new Burger();
}
public function addBun(): self
{
$this->burger->items[] = "Sesame Bun";
return $this;
}
public function addPatty(): self
{
$this->burger->items[] = "Zinger Patty";
return $this;
}
public function addCheese(): self
{
$this->burger->items[] = "Cheddar Cheese";
return $this;
}
public function addSauce(): self
{
$this->burger->items[] = "Mayo Sauce";
return $this;
}
public function build(): Burger
{
return $this->burger;
}
}Client Code
$burger = (new ZingerBurgerBuilder())
->addBun()
->addPatty()
->addCheese()
->addSauce()
->build();
$burger->show();Director Class (Optional)
A Director defines predefined build flows.
class BurgerDirector
{
public function makeZingerMeal(BurgerBuilder $builder): Burger
{
return $builder
->addBun()
->addPatty()
->addCheese()
->addSauce()
->build();
}
}Usage:
$director = new BurgerDirector();
$burger = $director->makeZingerMeal(
new ZingerBurgerBuilder()
);Fluent Builder
Modern applications usually use Fluent Builders.
Get Syed Hassan Hashmi’s stories in your inbox
Join Medium for free to get updates from this writer.
This enables method chaining.
$user = UserBuilder::make()
->name("Hassan")
->email("hassan@example.com")
->isAdmin()
->verified()
->build();Laravel itself heavily uses fluent builders:
User::query()
->where('active', true)
->latest()
->limit(10)
->get();Query Builder is actually one of the most famous real-world Builder implementations.
Real Industry Examples
1. Laravel Query Builder
User::query()
->where('status', 'active')
->orderBy('created_at')
->limit(5)
->get();Every chained method progressively builds the SQL query.
2. HTTP Request Builders
Http::withHeaders([
'Authorization' => 'Bearer token'
])
->timeout(30)
->retry(3)
->post('/api/users');3. Docker Image Builders
Dockerfiles are essentially builder patterns.
FROM php:8.3
RUN composer install
COPY . /app
CMD ["php", "artisan", "serve"]Each instruction builds the final image step by step.
When to Use Builder Pattern
Use Builder when:
- Objects contain many optional parameters
- Object creation has multiple steps
- Constructors become too large
- You need immutable objects
- Different representations share the same construction logic
- You want fluent APIs
- You want cleaner object initialization
When NOT to Use Builder
Avoid Builder when:
- Object creation is simple
- Only 2–3 parameters exist
- Additional abstraction adds unnecessary complexity
- A simple factory is enough
Overengineering is still bad architecture.
Builder vs Factory
| Builder | Factory |
| ------------------------------------ | -------------------------- |
| Builds step by step | Creates object immediately |
| Focuses on construction process | Focuses on object type |
| Supports complex configuration | Simpler object creation |
| Usually fluent | Usually one method call |
| Can create different representations | Returns a ready object |Advantages
1. Cleaner Constructors
No more massive parameter lists.
2. Better Readability
->verified()
->isAdmin()
->withPhone()is much clearer than:
true, true, "123"3. Flexible Object Creation
You only configure what you need.
4. Easier Maintenance
Adding a new field doesn’t break existing constructor calls.
5. Supports Immutable Objects
Builder allows constructing immutable objects safely.
Disadvantages
1. More Classes
Builder introduces additional abstraction layers.
2. Increased Complexity
For small objects, Builder becomes unnecessary overhead.
3. Boilerplate Code
Without language support, builders can become repetitive.
Advanced Builder Example
A production-level builder usually contains:
- Validation
- Defaults
- Immutable objects
- Reset functionality
- Nested builders
Example:
$user = UserBuilder::make()
->name("Hassan")
->email("hassan@example.com")
->validate()
->build();Common Mistakes
1. Using Builder for Simple Objects
Not every class needs a builder.
2. God Builders
If the builder has 100 methods, your domain model is probably broken.
3. Skipping Validation
Builder should validate required fields before build.
if (!$this->email) {
throw new Exception("Email required");
}Builder Pattern in Modern Languages
Modern languages already support Builder-like features.
Examples:
- Named parameters
- Optional parameters
- Data classes
- Records
- Object literals
That’s why Builder is less necessary in some ecosystems.
However, in enterprise systems with large domain models, Builder is still heavily used.
Best Practices
Prefer Fluent APIs
->withEmail()
->withRole()
->verified()Validate Before Build
Never return incomplete objects.
Keep Builders Focused
One builder should build one responsibility.
Use Immutability
Builder works extremely well with immutable objects.
Final Thoughts
Builder is one of the most practical design patterns used in real-world applications.
It solves a very common engineering problem:
“How do we create complex objects without destroying readability and maintainability?”
When used correctly, Builder gives you:
- Cleaner APIs
- Better readability
- Flexible object construction
- Easier maintenance
- More expressive code
But like every design pattern, Builder is a tool — not a rule.
Good engineers don’t use patterns everywhere.
They use them where complexity actually exists.







