"Scrum doesn't work for us.", "Agile is just a buzzword.", "There are better ways out there, we're just not given time to look them up."
In this article, I will share six practical ways we found to improve our team's efficiency and deliver more value to our product. From setting clear Sprint Goals to measuring the value of our User Stories, and how we kept our team motivated and focused on what matters. So grab a coffee, get comfortable, and let's dive in!
1. Sprint Goals
One of the first steps we took was to define a vision of what our part of the domain was mainly responsible for. A sentence that summarizes what role that specific part of the software plays concerning the other parts.
Plus, we understood that only sitting inside a team and waiting for the demand to come to us wouldn't be enough. Working on sprint after sprint without questioning why we were working on these features or fixes was creating a feeling of not being responsible for the final product or quality. The concept of waking up, going to work, doing our job for two weeks, and repeating, wasn't just enough.
If some people in the team had personal objectives for the product or the quality, they were hardly being shared with other team members. Not because of disagreements, but just due to the lack of common interest. That could be considered a problem where people end up pulling the same rope in different directions. Naturally, not all goals would be achieved, and the ones who "failed" in achieving their objectives, could likely grow a certain level of frustration causing them to stop caring whether improvements are being made or quality is met.
The introduction of Sprint Goals helped us to understand what kind of problem were we solving on that specific sprint. Once all the individuals in the team agreed on that common objective, all the uncertainty and ambiguity go away, and everyone knows what impact they are contributing to.
From an analytical perspective, it became easier to track the progress of the team when looking back at the previous sprints, and have a better vision of what has been accomplished within a given period. It provided resources for us to verify whether we were deviating from a longer-term goal, whether we were focusing too much on features instead of decreasing technical debts or simply reporting to other departments.
2. Measuring Value
Although for goals to be effective, they must be short, meaningful, and specific, they alone wouldn't be sufficient to tell us where to start or how to define priorities in our backlog.
Yes, there are always external factors when it comes to priority, like the customer's desire or other departments' input, but the priority had to make sense for our team as well. Or else, we'd be back to the same meaningless loop of work. It would be too convenient for us to outsource the priority management for our team and blindly accept whatever comes, but if everything becomes urgent or equally important, we'd succumb to the pressure and the first element of the delivery to be sacrificed would be the quality, let aside the team member's mental health.
Even if we don't consider priority an issue, the concept of meaningful work would be hard to achieve without clearly pointing out the value of what the Sprint Goals or User Stories were aiming for. Identifying value didn't only bring more clarity but also increased the sense of responsibility and ownership in the team.
Tracking value will be able to help us to measure the team's output more efficiently. It is preferable to claim that by the end of the Product Increment, we improved how the user gets notified by the system, for example, instead of saying that we simply delivered 20 User Stories.
In the beginning, identifying value in User Stories or Sprints is a hard task. Even telling what comes first, the value, or the User Story, is debatable. There are several values to be identified, like improving the user experience by shortening or increasing the steps of a form inside a capability, providing time-saving, or enhancing the information flow across the system's components. Equivalent to almost everything in life, it gets easier with practice. The best way to identify and classify value is to talk about it. This should be the first thing to be considered when planning it, and the last to be certified when delivering it.
3. Vertical slices
For value to be seen, measured, and tracked, it must be contained within working software. Reports, documentation, diagrams, or presentations are all useless if the value of the delivery is not visible in the product itself.
It means that if we wanted to deliver value, we had to deliver pieces of working software. We'd need to avoid that common practice of dividing the work in ways that couldn't be seen by the user, like developing all the tables of a database in one story, or build just the front end of a dummy form, or modifying a business rule in the middle of a service without describing what should be expected to be seen on the surface of the software.
To deliver small pieces of working software that could represent value for the user or product, we'd need to do vertical cuts: Interface, logic, and finally data. This simple formula can be applied to any kind of interaction with the software like the UI, the events, or cron jobs.
Thanks to the practice of identifying software behaviors based on the expected input and output described inside the User Story, we were able to easily split a large scope of a story into multiple smaller stories.
We learned that it is ok to provide multiple deliveries over and over the same feature, and if any requirement changes, it wouldn't affect the other requirements. Our stores are now isolated and independent. Should a new urgent requirement arrive or be discovered in the middle of the sprint, the scope could be easily rearranged by pushing lesser priorities outside of the sprint.
Incremental deliveries and resiliency to requirements change were finally achieved, thanks to slicing the stories vertically.
4. Cross-functional
At this moment, we had a good separation of concerns, and focus on what is important and what impact we'll be causing with our solution or delivery. The following step happened almost naturally.
When team members know what is desired to be achieved, they don't care about roles and who is responsible for what. Everybody was responsible for achieving what was agreed upon as a team once those objectives were being cooked indoors, and it didn't feel like they were just following orders. A sense of mutual collaboration started to rise within the team.
People stopped caring whether they were responsible for defining the requirements. They needed good requirements to achieve their goal so everyone contributed to that. No one cared who was going to test the changes or new features because they knew what to expect by the end of the sprint.
By adopting a cross-functional mindset, we could help each other at every stage of the process, catching details or scenarios that one person alone would have missed, and having a smooth transition from one stage to another. Handovers from refinement to development, or from development to testing, were no longer necessary. The team members cut the communication short because everyone was there when decisions were being made.
Cross-functionality also set us free from becoming hostages of our abilities or knowledge. When one individual in the team is not available, goes out on vacation, or sick leave, or simply decides to leave the team, impacts are minimum because others have the capacity to do what that person was doing although it was not their strength. Of course, only developers can write code, but in productive software where most of the changes and features are set on top of what already exists, writing the code becomes just a fraction of all the activities a developer could be performing in the team. These are the activities that can be covered by a cross-functional mindset.
5. One-piece flow
Based on my experience with developing software for more than 15 years, I can say for sure that the easiest way of distributing tasks and assignments is dividing and conquering. A team has 5 developers, and a sprint starts with 5 User Stories, so the most natural way of getting started is for each developer to take one User Story for himself and vanish for the rest of the sprint. They will be reporting their status at the Daily meeting, of course, and in the best-case scenario, no one has any trouble during their implementation, and they deliver all 5 User Stories at the same time.
The only problem is that in reality, it never goes that way. Our team was no exception to that. It is very common for User Stories to depend on each other. Not all developers have the same level of experience, and if they do, sometimes a developer gets stuck, and until assistance comes, this one developer would need to either join some other ongoing User Story or wait for help. If the assistance comes just on time, the ones providing their help would have to stop working on their tasks anyways. Inevitably, at least one User Story would need to wait. Another very popular piece of reality is that developers procrastinate. We need breaks, and this is natural. Some people can focus on their tasks for longer, some shorter. But while we are taking these little breaks, no one will continue our work as long as one developer is working alone on one task.
Despite the problems above, there are two issues that in my opinion are the worse. By the time all developers finish their User Stories by the end of the Sprint, the team has now 5 Stories to test at the same time, at the end of the Sprint. This raises the pressure dramatically. Every end of Sprint was a mix of chaos and anxiety, with everyone too worried about finishing the work and very little time left for getting ready for the upcoming Sprint. The second problem was the knowledge gap in the medium to long term. A developer who worked on a single Story or feature was the only one who knew how that part of the code was written, so every time a problem related to that part showed up, it was always that one developer assigned to it. At first, this dependency can be seen with keen eyes, given that one developer was the specialist on that topic and he could solve problems or extend it quicker than anyone else. However, in reality, that developer was becoming a hostage of each part that only he could work with. In case this developer gets sick, leaves on vacation, or simply wants to move on to new topics, he can't. Long handovers are necessary and seldom fully provided.
Adapting our way of working together, breaking the User Stories into subtasks, and assigning as many people as we could to one Story at a time, was not easy. I could say it was one of the hardest changes for all the team members. But it paid off very well. Handovers became obsolete, code reviews got shorter, stuck developers had immediate help with their subtasks while the Story implementation didn't have to wait, Sprint ends had usually one Story left to be closed releasing all the pressure and turning the attention of the team to the upcoming work, and the knowledge gap was eradicated.
Summing up to the fact that we are cross-functional, whereas developers started working on the implementation, QAs can start automating the test cases, and BAs can start preparing the testing data and the environment configuration for the scenarios. An entire team working together as a unit, Story after Story.
6. Switch focus to refinement
Unless we're talking about an entirely new project, green field, which was not our case, the biggest share of work should not be the implementation.
It is a productive software, with technical debts, hidden features, and millions of records, running for many years. Other than some people might think, we want to write as little code as possible. The less code, the smaller the software to maintain. Simple math.
Rushing into implementation without fully understanding the requirements or designing the solution, following a try-and-fail strategy, was costing us nearly the same amount of time as if we were taking proper time for preparing our Stories and subtasks.
It took very few Sprints for the team to notice that writing the test cases along with the requirements and making sure that everyone knew what to do before they started the work, tests became faster and more precise, and less code was written. Quality improved and even if some rework or refactoring was necessary, they were solved equally fast.
These steps wouldn't work if
As always, there is a catch. We were only able to implement each one of these steps after we tackled fear and trusted each other. Psychological safety might be a topic for the next article, but it was essential for our success. I tried some of the same methods with other teams which were not ready for taking these steps. People need to be open to change, and eager to improve things.
They are not easy to implement, people need to study the topics before they simply implement them, and caveats will appear. However, the pros overcome the cons.
If you read until here, thanks for your consideration. I hope that by sharing our experience with you, you can apply these concepts to your team too.