Tactics for Writing Solid Code

The principle of moving quality upstream illustrates the importance of integrating quality-focused engineering practices into daily development and integration rigor. By not applying quality practices such as testing earlier in the development cycle, teams are likely to accumulate a great deal of test work for each additional feature added, subsequently increasing the risk of delivering the software late and with a low degree of quality. For many organizations, this will be a significant cultural change in addition to a tactical one, but embracing this culture and these practices will ultimately lead to better engineering and delivery of solid code. As we progress through this book, we’ll explore both tactical and cultural recommendations for improving the overall quality of the software you and your team deliver. The rest of this chapter briefly describes these recommendations, presenting them thematically ("Focus on Design," for example), with the thematic sections’ subsections ("Class design and prototyping," for example) corresponding to the book’s remaining chapters.

Focus on Design

Design is perhaps the first line of defense against introducing poor quality into your software application. Traditional and agile methods agree that design is a critical step in the process of shipping high-quality software. There are numerous great design principles for which there are volumes of text written by incredibly smart people. As you might imagine, choosing a small set of design principles on which to focus this book was quite difficult. Fortunately, I have spent the past several years of my career working on software applications of various sizes, many of which were developed in managed code. The obvious choice was to focus on the design principles that have proven most useful in my experience.

Class design and prototyping

When developing an application, object-oriented programming begins with class design and prototyping. Generally, this starts with identifying participants, or "types," in the application and how they relate and interact with one another. The interaction and relationship model is translated into design documents, using Unified Modeling Language (UML). From there, a prototype is built, and the concepts, relationships, and interactions are tested for correctness and completeness. It is during this stage that it is most important to analyze the risk associated with the proposed design, prior to implementation.

Metaprogramming

Reducing complexity and improving the maintainability of software directly contributes to increasing its quality. By abstracting behaviors out of the code and into application metadata, the application design becomes inherently more robust, flexible, and adaptable. As a result, application and test code can be much more simplified. In managed code applications, the use of XML as a metaprogramming language is encouraged as a means to modify application behavior at run time. It is this design practice that ultimately provides lower-cost application maintenance and flexibility, especially in post-release, live production circumstances.

Performance

Designing well-performing applications could be considered a competitive feature of an application. Quality in software extends beyond just visible bugs to the overall end-user experience in using the application. This is especially true when building live services over the Web or smart clients that rely on Internet-based endpoints to function properly. Network latency, payload size, and even certain protocols can have negative impacts on online end-user experiences. As a core engineering tenet, performance considerations should be part of every design. Deferring these considerations until late in the development cycle can create significant code churn after performance bugs are discovered.

Scalability

As software evolves beyond the desktop to a server-based delivery model, scalability has become a critical element to high-quality software designs. Due to the nature of server-based applications, managing resources in your application has direct influence on both the way your application performs and the cost associated with scaling your application to meet user demands. Scale factors should always be considered as part of software design and in many instances will even force certain feature trade-offs. In addition to driving high operational costs, delaying scalability considerations beyond the design and coding phases of the development cycle could force design changes and subsequently increase code churn when bugs and general application health issues are discovered under increased load.

Security

Perhaps the most important of the core engineering tenets, and often the root of many quality problems, is security, and it should be a key design focus for all application developers. As software and users have become more connected, the threats against software have significantly increased in frequency and severity. Software designs need to give careful consideration to application threats and their appropriate countermeasures, through the use of threat modeling. Incorporating security into the entire development life cycle, and especially design, will ultimately lead to a safer and more secure experience for the users of your software.

Defend and Debug

Inasmuch as design is a critical element to developing solid code, knowing the pitfalls that hinder the ability to deliver bug-free code is equally important. Understanding typical programming mistakes can greatly reduce or eliminate a known set of potential issues. Beyond that, developers must employ defensive tactics when writing code that will help to reduce another, less obvious class of logic bugs. When proactively defending code is not enough, actively testing code will prove effective in eradicating these issues. Finally, any remaining bugs should be pursued by aggressively debugging. These principles are germane to all managed code developers who wish to proactively address quality issues in their code.

Memory management

Memory management in managed code is substantially different than native code. However, the difference is not just technical. It is also philosophical. For the most part, developers had ownership of memory management in native code. The state of memory was their responsibility. That changes in managed code. The developer must share responsibility with the Common Language Runtime—particularly the Garbage Collector. This requires the developer to delegate to the Garbage Collector is some circumstances, such as garbage collection.

Defensive programming techniques

Applying defensive techniques to software development helps to proactively limit the number of bugs being introduced into the application. These techniques can be as simple as conducting frequent code reviews and setting compiler options or as advanced as using pattern-based programming to leverage reliable and tested algorithms. Applying these practices in your software development process can greatly reduce the potential for logic bugs.

Debugging

Just as elements of the software development life cycle are iterative, so is the process of debugging. Most developers associate debugging with post-release bugs or runtime issues. However, proactively debugging code to ensure that it behaves as expected under all circumstances will greatly improve the release quality of the code. When bugs do creep into post-build or post-release code, though, a software developer with knowledge of a debugging tool set like Visual Studio 2008 should be well equipped to solve the problems.

Analyze and Test

Beyond implementing thoughtful designs and the application of development best practices, code must be exercised. This is fundamental to understanding how an application will perform when users begin interacting with it. While manual testing is a great method for discovering usability-centric issues with applications, programmatic unit testing and code coverage testing almost always find bugs early and help improve overall code quality. These processes should be incorporated during the development cycle so that code quality can be managed in real time during feature development, not after development is completed.

Code analysis, coverage, and testing

When code analysis, unit testing, and code coverage testing are applied within software development teams, the result is generally higher code quality, fewer regression bugs, and more stable application builds. Tools such as code analysis and unit test frameworks in Visual Studio 2008 provide software development teams the automated support to ensure the process is repeatable and predictable. Applying this level of automated rigor in the development process accrues value with every feature that gets added to the application. As software complexity increases, automated test execution and coverage keeps pace and helps maintain the quality of the application.

Improve Processes and Attitudes

Design, coding, debugging, and testing best practices are invaluable tools for improving software quality. Assembling these various practices may prove effective for some software development teams. In most cases, however, it is the processes and attitudes of the team that ultimately fuses with best practices to enable a culture of quality, where every team member shares the responsibility of releasing high-quality software.

Improving engineering processes

It is through testing and debugging that bugs are found and eradicated. Software developers learn from these mistakes and hopefully never repeat them, as they get immortalized and documented in great detail within bug-tracking systems. This process is relevant for software development methodologies as well. Successful teams often self-evaluate and make adjustments to their future processes in hopes of never repeating the mistakes of their past. Implementing changes that improve efficiencies in the engineering process ultimately improve the quality of the work being delivered. Practices like establishing milestone, check-in, and release criteria to improve quality are just some examples of how processes can be combined with practices to close the loop on how solid code can be achieved without heavyweight development processes.

Attitude is everything

Writing bug-free code is more than just adhering to a prescribed list of best practices and policies. It is also about the dynamics of software development teams, the interactions with customers and partners, and the moxie software developers bring to work every day to accomplish the incredibly difficult job they have. There are important human factors that affect us all every day as we develop software, and it is important to understand how to work well in these highly dynamic environments.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.29.119