The software development life cycle is riddled with inefficiencies. As with any manufacturing process, these inefficiencies hamper productivity and ultimately erode an organization’s competitive advantage. We need to reassess the life cycle with the mindset that everything we do as a development team either adds value to the customer or creates waste. Our goal should be to optimize productivity: 100% waste-free production.
Improving software development productivity really boils down to the following four concepts (and this can lead to astonishing increases in productivity):
• Having a clear, actionable plan of attack
• Increasing code knowledge
• Reducing rework
• Reducing debugging
In our experience, we have found that applying the following concepts leads to astonishing increases in productivity.
Have a clear, actionable plan of attack
The more time a developer spends wondering which requirement or task to tackle next and how to address it, the less time he or she is actually performing productive, rewarding work—creative tasks that help the organization achieve its goals.
To keep developers focused on performing productive work, define tasks properly, have a system for distributing tasks, and assign tasks properly.
In software development, tasks are often quite nebulous: They are vaguely defined, and it is difficult for developers to determine where to start tackling them and how to proceed. To overcome this challenge, it’s essential to have someone (e.g., an architect or manager) with a good conceptual grasp of the application being developed. This person can take high-level requirements and translate them into more granular tasks that a developer can quickly understand and then implement.
When developers are assigned to work on smaller tasks (for example, work tasks that are scoped to be no greater than one day), as opposed to being assigned to participate in one large nebulous task, experience has shown that they become much more productive. Since the developers understand exactly where to start and what to do, they become much more efficient: Their tasks are attainable and progress is immediately recognized.
Ideally, developers should always have a prioritized list of tasks in front of them in their natural development environment. This reduces any guesswork as to what is expected and how to proceed.
Then there is assigning a task properly. If a task is assigned to a developer who is unfamiliar with the related code, the implementation time (and the risk of debugging and rework) will be significantly greater than if that same task were assigned to a developer who was intimately familiar with the related code.
Increase code knowledge
The speed at which a developer writes code has nothing to do with how fast the developer types. It’s all about how well he or she understands the code to be modified. When a developer is assigned a task, he or she needs to understand what the existing code is doing, how to change what it’s doing, and be able to foresee the impact of his or her changes. This requires code knowledge. Thus, expanding each developer’s code knowledge will broaden the range of tasks that you can assign each developer, as well as reduce the time required to complete each task. How do you achieve this?
One way is through peer code review. When one developer reviews another’s code, he or she learns about code complexities and connections. This ends up expanding the amount of code that each developer can work on and the speed at which tasks are completed. In addition to expanding the knowledge of the reviewers, this process also provides additional insight to the reviewee, who receives valuable feedback on how the code he or she wrote relates to the codebase as a whole.
Another way to increase code knowledge is through regression testing. If a developer is not familiar with the code and has to change it, his or her greatest concern is probably breaking something that’s already working. Regression test suites instantly alert developers to any unexpected impacts (for instance, if a modification changes or breaks some existing functionality in another part of the application). In addition to teaching developers about correlations between the modified code and the other parts of the codebase, this also increases productivity by giving developers the courage to improve and extend code without being stunted by fear.
Expanding each developer’s code knowledge also promotes productivity by preventing mistakes that lead to debugging and rework. The less familiar developers are with the code, the more prone they are to make mistakes that will slow productivity long after the implemented piece of code is first checked in.
Debugging is essentially the process of trying to fix what was implemented incorrectly. The more time developers spend fixing code, the less time they have to spend on more productive tasks. One way to minimize debugging is to establish an infrastructure that prevents defects from entering the codebase. This could include automated defect prevention practices such as static analysis and unit testing.
The true value of minimizing debugging through defect prevention is that it reduces the need for rework. In the long run, performing defect prevention practices that prevent entire classes of errors as code is being written is considerably more efficient than constantly chasing after defects one by one and then fixing each occurrence later in the development process, when it is more difficult, costly and time-consuming to do so.
Minimizing debugging is just one way to minimize rework. Other ways are related to requirements.
If architects and managers do not understand requirements from the start and, in turn, developers do not implement them correctly, the team will inevitably end up wasting tremendous amounts of time reworking the code. It is critical to implement requirements as the customer expects and the specification states. To achieve this, you need to ensure that each party truly understands the requirement before taking the next step.
To understand what this involves, consider this analogy: A developer’s work is much like that of a building contractor. First, the architect finds out what the customer wants and sets the requirements. From there, just as contractors implement a building plan, developers start writing code.
At this point, mistakes can stem from two key sources: The architect misunderstands what the customer wants, which leads to incorrect requirement specifications. Or the developer misunderstands requirement specifications and implements them incorrectly.
Prototypes are very effective at preventing rework related to architect/customer misunderstandings. A prototype gives customers a chance to visually review what the developers are building for them and immediately alert the team if the project seems to be heading in the wrong direction.
Developer/architect misunderstandings can often be flushed out through a policy that as developers write code for a new requirement, they also need to write a test case that verifies if the requirement is implemented correctly. This forces developers to look at requirements from two different angles—implementation and validation—which results in a thorough understanding of each requirement.
Productivity = Quality
Surprisingly, the impact of applying these equations extends beyond productivity into quality. Quality and productivity are inextricably intertwined. If you follow these equations, your application quality will increase because you will have established a much less error-prone development process as a result of having developers who truly understand the codebase they are working with and the implementation tasks they are asked to perform. And you’ll have an infrastructure for preventing many errors that would otherwise require debugging and rework.
Ultimately, a process that results in low-quality software can never be productive. Rework and debugging will undermine any gains made by delaying or discounting the role that quality plays in fostering long-term productivity.
Adam Kolawa is founder and CEO of Parasoft, which sells tools to improve software quality across the development life cycle.