3 January, 2024
I was in conversation with a colleague the other day about the difference between a feature and a bug (and a third term, an enhancement), and I thought my answer might be worth sharing.
Let’s say we’re building a calculator. Our users expect this calculator to add, subtract, multiply, and divide, and so we build those features. One user reports that addition does something unexpected when they perform 100 + 100: they get 1,000.
We check this behaviour in our calculators and get the same result.
This is clearly a bug: we intended for addition to work for any numbers that can be input, and the correct answer is unambiguous. We fix this immediately.
Another user suggests we should build a function to calculate roots. They expect calculators to do this.
This is a new feature: we did not intend for the current calculator to calculate roots, but would like to meet our user’s expectation. We schedule in the work, and start to design the feature’s operation.
We ship the ability for our calculator to calculate square roots, but almost immediately receive a bug report: our user still cannot calculate roots! We investigate and the function is working as intended, but we then discover the user’s workflow involves cube roots almost exclusively! Our product design process missed this as we looked at the most common uses in competitors when deciding the simplest thing we could ship.
Is this a bug?
From the user’s perspective, the calculator still does not meet expectations, despite them telling us so and us doing work to address that.
From our perspective, the calculator is working as intended. The user still has outstanding unmet expectations, so to us this is a new feature (or perhaps an enhancement, which is just another word for a new feature on top of existing feature).
The answer: it’s a new feature, not a bug.
Bugs are where a system is intended to work a particular way by its creators, but doesn’t.
New features are where a system is working as intended by its creators, but not as expected by its users.
Users should only ever have to care about expectation, not intentions. Intentions are internal concerns: a user saying they can’t use a calculator that doesn’t calculate cube roots isn’t going to change their mind when told it’s not intended to do that. They’ll just find a product that does.
That said, users do often care about the difference, and it’s because of a perception of validity. In many cases, bugs are fixed quickly, while new features are implemented slowly. Users will often see a bug report as a way to get what they expect quickly, while new feature requests are a memory hole which is reviewed perhaps annually.
Getting your need seen as a bug is a proxy for your need being validated. This can lead to many arguments back and forth with users (never do this!) over whether something is really a bug.
The reason that bugs can be fixed quickly, of course, is because the correct behaviour is usually completely defined. That 100 + 100 should equal 200 is not up for debate or nuance. Cube roots might seem similarly obvious, but should we actually be supporting arbitrary roots? Do we have one button for each root? If not, how do we input the root? Do we support negative roots? What are the performance implications of large roots? And so on.
This is why features are slow and bugs are fast. But users shouldn’t care. They need cube roots, and will use whatever channels we give them to get them. Usually this is a support ticket. If it’s not a bug, that ticket will be closed and the user will feel ignored.
That said, there is a bug in the above story. Did you spot it?
It’s in the feature discovery process. The root calculation was developed without direct input from the user. If they had been involved, this wouldn’t have happened? Right?
Except they were involved! The actual story that inspired the above did involve a user being directly involved in developing the feature. The problem was trust and communication.
The user described their need, we wrote it down and decided how to solve it. We asked them to check it over and they skimmed it. The solution was very technical and so they just trusted that we were smart people and of course we’d build something that included their obvious needs. And so we went ahead and built an MVP, patting ourselves on the back for including our users.
Communication had failed to happen.
This was compounded by the fact the user didn’t actually use the feature for six months after it shipped, as it was only needed seasonally. So when they reported a bug everyone had moved on. It took considerable time to actually remind ourselves what our intentions even were!
(In case it’s not obvious, this feature was a little more complex than cube roots).
So what’s the solution? From our side, developers need to truly engage with users who request new features. We need to ensure their only tool is not a bug report, and we need to be empathetic when they insist it’s really a bug. We’ve trained them that only bugs get fixed.
When a new feature is developed, we need to ensure users know what they’re signing off on. We need to take them through the solution and ensure they understand it, and also need to understand the real need. The user above didn’t need square roots or cube roots, they needed arbitrary roots. MVP often leads us to playing whack a mole when users have specific needs, incrementally implementing features piecemeal and frustrating users instead of delighting them.
And users do benefit from understanding why there’s a difference. True bugs are not “important”, they’re just trivial to know what the fix should be. New features are actually more important, but they’re more complex and easy to get wrong. That’s why they take time.
And it’s why we, as developers, should be engaging with users and bringing them on the journey. It helps users see that this stuff is harder than they imagine.
So hard, in fact, that we developers should embrace and encourage all the help we can get.