This seventh post in the Making Software Quality Visible series introduces the “Skills Development, Alignment, and Visibility” section and describes a few important coding skills.
I’ll update the full Making Software Quality Visible presentation as this series progresses. Feel free to send me feedback, thoughts, or questions via email or by posting them on the LinkedIn announcement corresponding to this post.
Skills Development, Alignment, and Visibility
The objective of individual skill development, team and organizational alignment, and visibility of quality work and its results is to help everyone make better choices.
Quality begins with individual choices.
Quality begins with the choices each of us make as individuals throughout our day.
Awareness of principles and practices improves our choices.
Awareness of sound quality principles and practices improves the quality of these choices.
Common language makes principles and practices visible—improving everyone’s choices.
Developing a common language makes these principles and practices visible, so we can show them to one another, helping raise everyone’s quality game.
Individual Skill Acquisition
Help individuals incorporate principles, practices, language
Therefore, helping individuals acquire new knowledge, language, and skills is essential to improving software quality.
- Training, documentation, internal media, mentorship, sharing examples
We can offer training, documentation, and other internal media to spread awareness. We can also offer direct mentorship or share examples from our own experience to help others learn.
While QA testers, project managers, executives, and other stakeholders should be included in this process, my specialty is focusing on developers. Straightforward programming errors comprise the vast majority of quality issues; they’re also generally the most preventable and easily fixable. Helping developers write high quality code and tests is the fastest, cheapest, and most sustainable way to catch, resolve, and even prevent most programming errors.
Here I’ll summarize a few key principles and practices to help developers do exactly that.
-
Testable code/architecture is maintainable—tests add design pressure, enable continuous refactoring; use code coverage as a tool, not a goal
Designing code for testability, given proper guidance on principles and techniques, adds design pressure that yields higher quality code in general. Having good tests then enables constant improvements to code quality through continuous refactoring, instead of stopping the world for complex, risky overhauls or rewrites. (See Precise definition of “refactoring” below.)Good tests also enable developers to use code coverage as a tool while refactoring, helping ensure new and improved code replaces the previous code.1
-
Stop copy/pasting code; send several small code reviews, not one big one
Two common habits that contribute to worse code quality are duplicating code and submitting large changes for review. These changes make code difficult to read, test, review, and understand, which hides bugs and makes them difficult to find and fix after they’ve shipped. Helping people write testable code also helps people break these costly bad habits (which I’ll speak more about in the next post). -
Tests should be designed to fail: naming and organization can clarify intent and cause of failure; use Arrange-Act-Assert (a.k.a. Given-When-Then)
The goal of testing isn’t to make sure tests always pass no matter what. The goal is to write tests that let us know, reliably and accurately, when our expectations of the code’s behavior differ from reality. Therefore, we should apply as much care to the design, naming, organization of our tests as we do to our production code. (I’ll speak more to the “It’s just test code” mindset in the next post.)Merely showing people the immediately graspable Arrange-Act-Assert (or Given-When-Then) pattern can be a profound revelation that changes their perspective forever.
-
Interfaces/seams enable composition, dependency breaking w/ test doubles
Of course, many of us start out in legacy code bases with few tests, if any. So we also need to teach how to make safe changes to existing code that enable us to begin improving code quality and adding tests. Michael Feathers’s Working Effectively with Legacy Code is the seminal tome on this subject, showing how to gently break dependencies to introduce seams. “Seams” are points at which we introduce abstract interfaces that enable test doubles to stand in for our dependencies, making tests faster and more reliable.I’ll cover Feathers’s definitions of “legacy code” and “seams” in the next post.
Precise definition of “refactoring”
Of course Martin Fowler is famous for popularizing the term “refactoring” thanks to his book Refactoring: Improving the Design of Existing Code. He defines the term specifically thus (on the refactoring.com page):
“Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior.
“Its heart is a series of small behavior preserving transformations. Each transformation (called a ‘refactoring’) does little, but a sequence of these transformations can produce a significant restructuring. Since each refactoring is small, it’s less likely to go wrong. The system is kept fully working after each refactoring, reducing the chances that a system can get seriously broken during the restructuring.”
The spirit of this is encapsulated by a famous tweet from Kent Beck:
“for each desired change, make the change easy (warning: this may be hard), then make the easy change”
Also note on the refactoring.com page that Martin specifically asserts that “Refactoring is a part of day-to-day programming” and goes on to describe how. In the book, Martin gives this advice to those who still feel they need to ask for permission before refactoring anything:
“Of course, many managers and customers don’t have the technical awareness to know how code base health impacts productivity. In these cases, I give my most controversial advice: Don’t tell!
“Subversive? I don’t think so. Software developers are professionals. Our job is to build effective software as rapidly as we can. My experience is that refactoring is a big aid to building software quickly.”
Coming up next
The next post will expand upon the footnotes regarding code duplication, large changes, and the “It’s just test code” excuse.
The post following that will expand upon the footnotes regarding legacy code and seams, as defined by Michael Feathers. It’ll also introduce “The most important design guideline” from Scott Meyers.
Footnotes
-
I learned about this specifically from Wolfgang Trumler, who I believe got it from Joshua Kerievsky’s book Refactoring to Patterns. ↩