Discover the meaning behind code quality, including the functional requirements, usefulness, and maintainability and how it relates to communication.
- [Instructor] Writing computer code is a form of communication with many audiences. Computers interpret and execute the code which consists of instructions and rules. Humans, such as the code's author or other maintainers, read and write code for computers. In this case, the code describes the intent of the software. When read, the human should understand what, why, and how the software works. Needless to say, software engineers have a daunting task. They need to communicate intent and goals to both humans and computers.
Everyone and everything involved needs to understand each other. As you can imagine, things don't always go according to plan and mistakes happen. Of course, each audience handles errors in their own way. Humans, by our very nature, make mistakes and adapt in response. If you've ever watched a speech or a live music performance, you've seen a few little mistakes. Using context and experience, we unconsciously accept and correct mistakes and move on. The mistakes are forgotten and the music or the performance was remembered.
In comparison, computers literally interpret what we tell them to do. A mistake in syntactically correct instructions will be dutifully executed. For example, tell a computer to store an email address, then give it a picture of a fish. Unless there are validation rules saying that images aren't allowed, the computer will happily store the fish. This course's goal is to improve communication between software engineers and computers. In this chapter we're going to be defining core concepts and theory around both testing and code quality.
I'll introduce each abstractly, then we'll apply that knowledge throughout. We'll start with the esoteric question, what is code quality? Next, we'll take a look at coding standards and how to enforce them. We'll define unit testing, test-driven development, and behavior-driven development, then compare the differences. We'll take a look at the role of testing frameworks and why they're important. Finally, we'll see how assertion libraries verify how correct your programs are. So, what is code quality? Let's define what quality is first.
Joseph M. Juran's Quality Control Handbook defines quality with two specific meanings. Quality consists of those product features that meet the needs of customers and thereby provide product satisfaction. And quality consists of freedom from deficiencies. Code quality builds on those concepts in many different, but related ways. Quality code meets functional requirements, meaning it does what it's supposed to do. Let's use the example of email storage.
The requirements are the ability to store an email address. If the code written couldn't store an email address, then it would fail the requirements. If you could store a picture of a fish, that picture is not an email address and therefore the requirements are not met. Quality code is free from deficiencies, which means no failings or shortcomings. These can be broadly generalized in two ways, usefulness and maintainability. Let's explore each.
What is useful code? The more flexible and reusable the code is, the more useful it is. For example, you can write a function that takes a number, adds one to it, and returns the result. That may fulfill a functional requirement, but it's inflexible. We could improve upon it by adding any number to any number, which would be more useful. Of course, you should be using the programming language's built-in math library. That would be the most useful and flexible overall. What about maintainable code? There are three questions that can determine if code is maintainable or not.
The first is can you maintain the code? The next is can someone else maintain it without asking for help? Finally, can someone else read the code and understand the design and intent? If the answer to all three questions is yes, then the code is maintainable. Maintainable code is not an absolute. Instead, it evolves over the life cycle of the software. Code may start as unmaintainable and improve over time. On the other hand, sloppy and inconsistent changes may make it worse over time.
Engineers improve with experience, so revisiting old code will make you want to clean it up. The goal should be continual, gradual improvement. It's best to make a series of incremental improvements, rather than a radical overhaul, that way you reduce the risk of damaging perfectly functional code. We know the questions to ask to determine if code is maintainable. What is the most certain way to decide if code meets those standards? Peer review, where another person reads your code and documentation.
If they can understand the design and intent, then the code is maintainable. You can take steps to improve the maintainability of your code. For example, the use of consistent formatting and logical naming helps humans read. Clear and appropriate comments, along with functional documentation explicitly describes intent. Splitting your code into appropriate modules and atomic components makes the code reusable. Like watching a musician or athlete, the result of hard work and practice can look effortless.
Should every piece of code that you write be high quality? We should always strive for excellence, but be reasonable at the same time. Sometimes a one-off script to perform a single task can be necessary. You should still document what it's doing and why, chances are you may need to refer to again or reuse some of the functionality elsewhere. In summary, the phrase code quality has several meanings. Foremost, it should meet functional requirements. Quality code should be maintainable, by yourself and by others.
Finally, it should be useful in multiple places, even in different programs. There are many ways to measure and ensure code quality. The first and most basic is the use of a coding standard. What is a coding standard?
- What is code quality?
- Testing and code quality fundamentals
- Coding conventions and standards
- Creating and enforcing coding standards
- Unit, integration, and functional testing
- Test-driven development test specificatons
- Behavior-driven development test specifications
- Finding errors with linting
- Extending an ESLint shareable config
- Validating correctness with unit testing
- Replacing and inspecting with stubs, spies, and mocks
- Code coverage and why it matters
- Coverage with continuous integration