#3: Focus on Product Ideas, Not Requirements
Hello! Over the past two days, we discussed the problems with premature abstractions and how the YAGNI principle could help. Yet, software engineering is a question of balance. Nothing is ever black or white.
In #1: Premature Abstractions, we discussed how building premature abstractions or designing with speculative future needs in mind can often be a waste of time. Today, I want to introduce a principle to help us find a better balance between over-engineering and over-applying the YAGNI principle.
The main idea is that writing code that is aligned with the fundamental concepts of our product can make it more adaptable to future changes.
Imagine we are working at Starbucks as a software engineer, crafting their first online ordering system. Here’s a simple Java interface that we could start with:
interface Delivery {
public void deliverCoffee(List<Coffee> coffees);
}
This approach works well initially. Yet, what if Starbucks decides to add food alongside coffee as part of the deliveries?
One way to accommodate this change is to modify our existing interface by adding a new method:
interface Delivery {
public void deliverCoffee(List<Coffee> coffees);
public void deliverCoffeeAndFood(List<Coffee> coffees, List<Food> foods); // New method
}
Yet, as our requirement grows (ordering teas, juices, mugs, etc.), we might find ourselves adding more and more methods to this interface. This can quickly become cumbersome and lead to a codebase that becomes hard to maintain.
Perhaps the issue was to create this initial deliverCoffee
method. This method was based on a specific requirement we received: we want to deliver coffee, right? Thus, let’s create one method for that!
Instead, we can focus on the product idea itself. What does Starbucks want to achieve? Handling deliveries. So, instead of our initial deliverCoffee
method, let’s replace it with a more generic deliver
function, taking a DeliveryList
argument:
interface Delivery {
public void deliver(DeliveryList items);
}
class DeliveryList {
private List<Coffee> coffees;
// Other items can be added as needed
}
In this approach, DeliveryList
is a domain object—an object that represents a common idea in our domain. By focusing on the idea of delivery rather than a specific requirement, we create a design that is more adaptable to future changes.
Domain objects can be a useful generalization, allowing us to avoid applying the YAGNI principle too rigidly.
What do you think about this technique? Let’s discuss it in the comments.
Tomorrow, we will explore the concept of cognitive load. Stay tuned!