I recently just finished reading Growing Object-Oriented Software, Guided by Tests (or GOOS for short) by Nat Pryce and Steve Freeman. I highly recommend every developer pick up a copy and give it a read. It is full of great advice for building a well-tested, high quality application. I wanted to pick out two particular gems I found towards the end of the book that I felt are really powerful.
[W]e like to have names for the relationships between objects as well as objects themselves. As the legends say, if we have something's true name, we can control it.
They are talking specifically about preferring to mock interfaces rather than concrete implementations, but this concept bubbles up generally throughout the book. They use interfaces extensively, even noting that their use is above average, to tease out concepts and relationships in the code. Many in the community feel that interfaces are just needless over-engineering and over-specifying of an object. Here, though, the authors turn that idea on it's head. Interfaces don't specify objects, they define relationships. By giving a name and a form to a relationship, we can control it, vary it, talk about it, and show it. It is something real in the system, not an implicit concept buried inside of our code.
To design is to communicate clearly by whatever means you can control or master.
Here is a quote the authors chose to emphasize the subject of the chapter, but the quote itself is very prescient. It frames design as a form of communication. We don't design in a vacuum. Our designs should tell the next person that reads it about our software. We can judge the quality of our design by how effectively it communicates our intent and our knowledge. Good design is not just full of patterns or abstractions, but is measured at the ability to convey ideas.
This quote, of course, goes far beyond just software design. We can look at the designed things of the world around us in the context of communicating with us. We can judge their design by how well they tell us what we need to know.
As software developers, we see errors every day. They manifest as exceptions or segfaults or error codes, telling us that our code has gotten into a state we didn't expect. Their appearance often portends bugs. Though we groan at an unexpected stack trace, we should see an error as a form of automated feedback . Feedback can be fast or slow. We can put the discovery of errors onto a timeline. Errors appear at many points over the lifetime of the code, starting at the moment it is compiled. The further to the right that an error appears, the longer it takes for the feedback to appear. Compile time Compile time is the earliest we can receive feedback about an error. The compiler automatically does a number of checks to make sure the code makes some semblance of sense. The most powerful tool for compile-time feedback is the type-checker. The type-checker makes sure that only values of the expected type are passed around to the places that expect them. This guards ...
Writing code is all about making assumptions. Sometimes those assumptions are explicit, but more often we make those assumptions implicitly. We make assumptions about what kinds of parameters we will receive. We make assumptions about what kinds of values methods and functions will return. We make assumptions about the global state. The correctness of our code relies on the correctness of these assumptions. Bad assumptions can wreck our applicatons. We assumed that method never returns null, but turns out it does. Now we have an unexpected NullPointerException crashing through our stack. We assume that we will always average at least one element. Turns out someone wants to average zero elements. Boom, division by zero, ArithmeticException . We make so many assumptions through our codebases, at least a few are going to be wrong. Really, there are many, many assumptions that are wrong. These are bugs. These bad assumptions are all of our bugs. Usually, our assumptions are i...
As hurricanes approach the shores of North America, we are barraged with maps of possible tracks those storms can take. Those tracks start pretty close together, but diverge widely the further into the future they predict. Simplified, those storm tracks form a cone of uncertainty . A storm in the Atlantic could slide into the Gulf of Mexico and land anywhere from the Texas-Mexico border to Panama City Beach, FL. It all depends on a myriad of tiny factors to turn the storm this way or that. As time progresses, the storm moves along, and the cone of uncertainty shrinks. Its eventual location becomes clearer to predict, and we can more effectively target resources. Cone of Uncertainty for Hurricane Ike, source Much like hurricanes, running code lives in a cone of uncertainty. A function or method call is a mysterious black box until we have a result. If something goes wrong, we must delve into this cone of code uncertainty to find the problem. We use log statements and de...
Comments
Post a Comment