You have an idea for a new awesome feature for your product and start working on it. After weeks of hard work you integrate the feature into the main code base, fix some merge conflicts and incorporate feedback from other teams. Then, you get errors during deployment, causing issues for your operations team. Finally, after fixing those errors we get to the first release.
However, many customers are not happy because the feature does not work well with production data. You fix the issues for another week or two, repeat the cycle, and finally complete the feature for your customers.
What you’ve noticed is that you received important feedback too late, namely: deployment-issues, production-issues, and customer feedback. Receiving and improving on feedback is one of the core practices in DevOps. The State of Devops report identifies companies with shorter feedback cycles as higher performing.
To get feedback earlier, it is important to deliver smaller batches. This way, feedback will be more focused and quicker. For example, a large feature might have a few critical bugs only showing up in production. When deployed, the team is now under pressure to debug multiple new issues at once. If the features are instead delivered in small batches, it is easier to pinpoint what changes introduce a bug. Likewise, if customers can give feedback on a feature early, they can tell you early where it does not work for them.
Practising Smaller Tasks
Let’s practice creating smaller tasks with an example. We have an online store and envision this feature, so we create a task:
Clearance Sales: If our inventory has old stock, automatically create clearance sales on our website. Reduce the price depending on the age of the stock
Ok, how can we split this up? A good guideline is INVEST:
- Independent: Make the task as self-contained as possible, with no dependency on another task.
- Negotiable: The task is not a contract, it should leave space for discussion and changes after feedback.
- Valuable: It should provide value to the stakeholders.
- Estimable: We know enough about the task so we can roughly estimate its size. It shouldn’t be open-ended.
- Small: It should not become so big to become impossible to plan and prioritize. Ideally, the task can be completed in hours or a few days.
- Testable: Provide the necessary information to make testing the task possible.
Given these guidelines, think about how the feature can be split up into tasks. Here is my attempt:
- Create a prototype. Emulate the feature manually by adding a hard-coded clearance sale on the website.
Motivation: We can quickly see if customers are interested in the feature.
- A task to develop the database tables and queries to find the right items for a clearance sale.
Hardcode the price reduction.
Motivation: We can deploy this, ensuring the database changes do not cause issues in production. We also specified that the price reduction is initially hardcoded, reducing the task complexity.
- Provide REST endpoint for clearance sale.
Motivation: We can deploy this and test it in production without customers using it.
- Create UI for clearance sale, hide behind feature flag
Motivation: We might enable it for our team and some customers
- Release feature for everyone.
Now we have smaller batches of work to deploy the feature into production. We can notice bugs in production and have a smaller change log to find where the issue could be.
Avoid Planning Too Far Ahead
Now you might fall into the trap of creating tasks for months to come all for a large feature. That is working against the idea of incorporating the feedback. The early feedback might pivot your feature in another direction, so many future tasks become irrelevant. Therefore, it is better to have tasks focused on the most immediate needs and to avoid planning detailed tasks for things far into the future.
For example, after we deployed our prototype, customers wished that it is not a single sale, but an actual search filter. After discussing with the business, we decide to change the feature in that direction and update our tasks.
After we changed the feature in that direction and deployed it, we notice that a fixed price reduction is actually good enough. We never develop the dynamic price reduction we planned at the beginning.
Another Example: M1 Macbook
Ok, we are not an insider within Apple, but we want to point out that Apple is known for its big-bang releases like the M1 Macbook. However, even for the M1 there were incremental steps towards it over years.
- Fat-binaries from Power-PC/Intel transition
- Using ARM in the iPhone
- Porting more and more of the MacOS/iOS to ARM
- Buying semiconductor companies to build their own chips.
- Shipping LLVM-bitcode for Apple Watch
- Continuously improving their ARM chips, eventually reaching desktop-like performance in iPads/iPhones
- Finally, using their ARM chips in laptops
In each step, the Apple teams gathered feedback and improved their designs. Without these smaller stepping stones, we doubt that the M1 Macbook would be a successful release.
In software development we have the luxury that we can easily split up work into smaller tasks, completing these tasks and collecting feedback very early. That’s why we also provide Katas like ‘Decrease Issue Duration’ and ‘Execute Smaller Releases’ in DevSensei.