I was recently watching a talk by Dan North. At one point he briefly talks about a project he worked on. For this project, the Ant build script was generated with an XSLT transform, producing a build script about 2 million lines long. Even though that is clearly horrible, my first fleeting thought was, Huh, I would have never thought to do that. That's pretty clever..
Dan North's Simplicity, the Way of the Unusual Architect, at the time of the story
That's pretty clever, is absolutely the wrong thought to have. I cannot fathom the monumental, wasted effort maintaining such a system. But I am also sure it started as someone's clever idea. There would be plenty out there who would say, Well, that project went wrong, but the idea itself isn't bad. It's pretty darn clever, really.
"Cleverness" like that always seems promising at first. It is a novel view of a problem at hand. Because clever ideas are novel in completely unexpected ways, they have unexpected consequences. Once in place, they become entrenched in the structure. Just implementing those ideas was an undertaking because of their cleverness. Throwing that all away is not an option. Keeping them working under evolving circumstances requires layering on additional cleverness. It all looks like a precarious game of Jenga. There are quirks to build around. There are blocks never to be touched.
Brian Kernighan said, Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it? Those of us who are not clever enough are trapped. We plod through the complexity just trying not to break anything. Most importantly, we will all end up not being clever enough eventually.
We all seem to admire cleverness, despite its dark side. From a young age, we are praised for our cleverness. It becomes an admirable trait. Cleverness "on the computer" might even be why we do what we do. That's why we momentarily admire horrifying things, like XSLT transforming Ant scripts. Cleverness is not intelligence, though. We can be smart without doing clever things.
Smart people like to be challenged. Being clever is one sort of challenge, but there are others. A better challenge is to be dull. Think of the most boring, straightforward way of doing something. Make it just flexible enough, but don't over engineer. If it is simple enough for a brand new software developer to understand, you've achieved your goal. When you produce software that dull, you produce software that is maintainable, software that is able to evolve.
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