Story: constraining a story until there is no complexity left
December 11th, 2020
This is part of a larger Story: slicing stories
We kept coming up with constraints that made the story simpler and simpler to implement. Not just simpler in code, but also in complexity.
Beforehand it was already known to me that adding features to a program, increases the complexity exponentially per feature added, rather than linearly. Because every existing feature can cause problems for the new feature. So it's much easier to add the 3rd feature than it is to add the 10th feature. Not just 10 - 3 = 7 times easier, but more toward 10^2 - 3^2 = a lot times easier.
But through this exercise I learned that inside one story, every constraint added is like a feature itself in terms of complexity. And each angle of complexity, not constrained, can add that exponential complexity inside the story and outside with other functionality.
This is all very abstract, so I'll try to make it more clear with an example. Say the story we sliced was as follows. We have different configurations with data in each. But sometimes we want small changes per environment (eg testing, staging, production, preview,...) We don't want to copy the entire config, and change a small piece. We want a form of reuse. So we decide to add a story: "reuse config by inheritance in configs". That's a pretty simple/clear story. Everyone understood what it meant.
So it's easy enough to understand, there is some obvious complexity, but nothing too crazy it seems. So let's implement it, no need to discuss it further really, since everyone understands. But instead we decided to
Slice the story as small as possible
But so that it still provides customer value
We ended up with a bunch of constraints:
Only one 'parent' configs (aka no multiple inheritance) (aka 1 wide)
A parent cannot be a parent (aka 1 depth)
Dont merge data
So we discovered that there were quite a few axes of complexity. And they all interact with each other to create exponential complexity. Eg. Multiple inheritance makes the merging logic more complex. Infinite depth makes the merging more complex. Multiple inheritance + infinite depth makes merging more complex than the sum of the two. And this only considering those 3 'axes', within this one story! There still is other existing functionality and future functionality that we want to add.
So we ended up 'constraining the value out of it'. If we added all constraints, relaxing none, there was no customer value. So the next question was: which constraint, when removed, would provide the most value. And let's do that one first.
So we did that for the first story. Then we added a story for each constraint to be lifted. But we worked on them one by one, in order of highest value. And we noticed something amazing: not all constraints needed to be lifted! At a certain point, we added enough value/functionality that the rest was not urgent. (and probably never would be)
If we had gone with the "it's simple enough, everyone understands what to do" we would have built an over-engineered solution. This would cause the story itself to be exponentially more complex. And all future functionality would also have to pay the exponential extra price! Instead, every constraint we kept, avoided one level of complexity in the system. Now as well as in the future. And if ever needed, we could always lift a constraint later, if needed.
This post was referenced in: