Clean Code. Whew! 1253
I’ve been working on this book for several years now. After a flurry of effort (you might have noticed I’ve been quiet lately) I’m very pleased to say that I’m done with the writing and am preparing the manuscript for production. See The Prentice Hall Listing
Table of Contents Clean Code 1 There Will Be Code 1 Bad Code 2 The Total Cost of Owning a Mess. 3 Schools of Thought. 11 We are Authors. 12 The Boy Scout Rule 13 Prequel and Principles 14 Conclusion 14 Bibliography 15 Meaningful Names by Tim Ottinger 17 Introduction 17 Use Intention-revealing Names 17 Avoid Disinformation 19 Make Meaningful Distinctions 20 Use Pronounceable Names 21 Use Searchable Names 22 Avoid Encodings 23 Avoid Mental Mapping 24 Class Names 25 Method Names 25 Don't Be Cute 25 Pick One Word Per Concept 26 Don't Pun 26 Use Solution Domain Names 27 Use Problem Domain Names 27 Add Meaningful Context 27 Don't add Gratuitous Context 29 Final Words ... 30 Functions 31 Small! 34 Do one thing. 35 One level of abstraction per function. 36 Switch Statements. 37 Use descriptive names. 39 Function Arguments. 39 Have no side-effects. 43 Command Query Separation 44 Prefer exceptions to returning error codes. 45 Don't Repeat Yourself. 47 Structured Programming 48 How do you write functions like this? 48 Conclusion 49 SetupTeardownIncluder 49 Bibliography 52 Comments 53 Comments do not make up for bad code. 55 Explain yourself in code. 55 Good Comments 55 Bad Comments 59 Example 71 Bibliography 74 Formatting 75 The Purpose of Formatting 76 Vertical Formatting 76 Horizontal Formatting 84 Team Rules 89 Uncle Bob's Formatting Rules. 90 Objects and Data Structures 93 Data Abstraction 93 Data/Object anti-symmetry. 95 The Law of Demeter 97 Data Transfer Objects 99 Conclusion 101 Bibliography 101 Error Handling by Michael Feathers 103 Use Exceptions Rather than Return Codes 103 Write Your Try-Catch-Finally Statement First 105 Use Unchecked Exceptions 106 Provide Context with Exceptions 107 Define Exception Classes In Terms of a Caller's Needs. 107 Define the Normal Flow 109 Don't Return Null 110 Don't Pass Null 111 Conclusion 112 Bibliography 112 Boundaries by James Grenning 113 Bibliography 119 Unit Tests 121 The Three Laws of TDD 122 Keeping Tests Clean 123 Clean Tests 124 One Assert per Test 129 F.I.R.S.T. 132 Conclusion 132 Bibliography 133 Classes 135 Class Organization 135 Classes should be Small! 136 Organizing for Change 146 Bibliography 150 Systems By Dean Wampler 151 How would you build a city? 151 Separate constructing a system from using it 152 Scaling Up 155 Java Proxies 158 Pure Java AOP Frameworks 160 AspectJ Aspects 163 Test-drive the system architecture 164 Optimize decision making 165 Use standards wisely, when they add demonstrable value 165 Systems need Domain-Specific Languages 166 Conclusion 166 Bibliography 167 Emergence By Jeff Langr 169 Getting Clean via Emergent Design 169 Simple Design Rule 1: Runs all the tests 170 Simple Design Rules 2-4: Refactoring 170 No Duplication 170 Expressive 173 Minimal Classes and Methods 174 Conclusion 174 Bibliography 174 Concurrency by Brett Schuchert 175 Why Concurrency? 176 Challenges 177 Concurrency Defense Principles 178 Know Your Library 180 Know Your Execution Models 181 Beware Dependencies between Syncrhonized Methods 182 Keep Synchronized Sections Small 183 Writing Correct Shut-Down Code is Hard 183 Testing Threaded Code 184 Conclusion 188 Bibliography 189 Successive Refinement 191 Args Implementation 192 Args: the rough draft. 198 String Arguments 212 Conclusion 246 JUnit Internals 249 Conclusion 262 Refactoring SerialDate 263 Conclusion 280 Bibliography 281 Smells and Heuristics 283 Comments 283 Environment 284 Functions 285 General 285 Java 304 Names 306 Tests 310 Conclusion 311 Bibliography 312 Concurrency II by Brett Schuchert 313 Client/Server Example 313 Possible Paths of Execution 317 Knowing Your Library 322 Dependencies between methods can break concurrent code 325 Increasing Throughput 329 Deadlock 331 Testing Multi-Threaded Code 335 Tool Support for Testing Thread-Based Code 337 Conclusion 338 Tutorial: Full Code Examples 339 org.jfree.date.SerialDate 345 Cross References of Heuristics 406
Clean Code. Whew! 1253
I’ve been working on this book for several years now. After a flurry of effort (you might have noticed I’ve been quiet lately) I’m very pleased to say that I’m done with the writing and am preparing the manuscript for production. See The Prentice Hall Listing
Table of Contents Clean Code 1 There Will Be Code 1 Bad Code 2 The Total Cost of Owning a Mess. 3 Schools of Thought. 11 We are Authors. 12 The Boy Scout Rule 13 Prequel and Principles 14 Conclusion 14 Bibliography 15 Meaningful Names by Tim Ottinger 17 Introduction 17 Use Intention-revealing Names 17 Avoid Disinformation 19 Make Meaningful Distinctions 20 Use Pronounceable Names 21 Use Searchable Names 22 Avoid Encodings 23 Avoid Mental Mapping 24 Class Names 25 Method Names 25 Don't Be Cute 25 Pick One Word Per Concept 26 Don't Pun 26 Use Solution Domain Names 27 Use Problem Domain Names 27 Add Meaningful Context 27 Don't add Gratuitous Context 29 Final Words ... 30 Functions 31 Small! 34 Do one thing. 35 One level of abstraction per function. 36 Switch Statements. 37 Use descriptive names. 39 Function Arguments. 39 Have no side-effects. 43 Command Query Separation 44 Prefer exceptions to returning error codes. 45 Don't Repeat Yourself. 47 Structured Programming 48 How do you write functions like this? 48 Conclusion 49 SetupTeardownIncluder 49 Bibliography 52 Comments 53 Comments do not make up for bad code. 55 Explain yourself in code. 55 Good Comments 55 Bad Comments 59 Example 71 Bibliography 74 Formatting 75 The Purpose of Formatting 76 Vertical Formatting 76 Horizontal Formatting 84 Team Rules 89 Uncle Bob's Formatting Rules. 90 Objects and Data Structures 93 Data Abstraction 93 Data/Object anti-symmetry. 95 The Law of Demeter 97 Data Transfer Objects 99 Conclusion 101 Bibliography 101 Error Handling by Michael Feathers 103 Use Exceptions Rather than Return Codes 103 Write Your Try-Catch-Finally Statement First 105 Use Unchecked Exceptions 106 Provide Context with Exceptions 107 Define Exception Classes In Terms of a Caller's Needs. 107 Define the Normal Flow 109 Don't Return Null 110 Don't Pass Null 111 Conclusion 112 Bibliography 112 Boundaries by James Grenning 113 Bibliography 119 Unit Tests 121 The Three Laws of TDD 122 Keeping Tests Clean 123 Clean Tests 124 One Assert per Test 129 F.I.R.S.T. 132 Conclusion 132 Bibliography 133 Classes 135 Class Organization 135 Classes should be Small! 136 Organizing for Change 146 Bibliography 150 Systems By Dean Wampler 151 How would you build a city? 151 Separate constructing a system from using it 152 Scaling Up 155 Java Proxies 158 Pure Java AOP Frameworks 160 AspectJ Aspects 163 Test-drive the system architecture 164 Optimize decision making 165 Use standards wisely, when they add demonstrable value 165 Systems need Domain-Specific Languages 166 Conclusion 166 Bibliography 167 Emergence By Jeff Langr 169 Getting Clean via Emergent Design 169 Simple Design Rule 1: Runs all the tests 170 Simple Design Rules 2-4: Refactoring 170 No Duplication 170 Expressive 173 Minimal Classes and Methods 174 Conclusion 174 Bibliography 174 Concurrency by Brett Schuchert 175 Why Concurrency? 176 Challenges 177 Concurrency Defense Principles 178 Know Your Library 180 Know Your Execution Models 181 Beware Dependencies between Syncrhonized Methods 182 Keep Synchronized Sections Small 183 Writing Correct Shut-Down Code is Hard 183 Testing Threaded Code 184 Conclusion 188 Bibliography 189 Successive Refinement 191 Args Implementation 192 Args: the rough draft. 198 String Arguments 212 Conclusion 246 JUnit Internals 249 Conclusion 262 Refactoring SerialDate 263 Conclusion 280 Bibliography 281 Smells and Heuristics 283 Comments 283 Environment 284 Functions 285 General 285 Java 304 Names 306 Tests 310 Conclusion 311 Bibliography 312 Concurrency II by Brett Schuchert 313 Client/Server Example 313 Possible Paths of Execution 317 Knowing Your Library 322 Dependencies between methods can break concurrent code 325 Increasing Throughput 329 Deadlock 331 Testing Multi-Threaded Code 335 Tool Support for Testing Thread-Based Code 337 Conclusion 338 Tutorial: Full Code Examples 339 org.jfree.date.SerialDate 345 Cross References of Heuristics 406
Business software is Messy and Ugly. 71
I was at a client recently. They are a successful startup who have gone through a huge growth spurt. Their software grew rapidly, through a significant hack-and-slash program. Now they have a mess, and it is slowing them way down. Defects are high. Unintended consequences of change are high. Productivity is low.
I spent two days advising them how to adopt TDD and Clean Code techniques to improve their code-base and their situation. We discussed strategies for gradual clean up, and the notion that big refactoring projects and big redesign projects have a high risk of failure. We talked about ways to clean things up over time, while incrementally insinuating tests into the existing code base.
During the sessions they told me of a software manager who is famed for having said:
“There’s a clean way to do this, and a quick-and-dirty way to do this. I want you to do it the quick-and-dirty way.”
The attitude engendered by this statement has spread throughout the company and has become a significant part of their culture. If hack-and-slash is what management wants, then that’s what they get! I spent a long time with these folks countering that attitude and trying to engender an attitude of craftsmanship and professionalism.
The developers responded to my message with enthusiasm. They want to do a good job (of course!) They just didn’t know they were authorized to do good work. They thought they had to make messes. But I told them that the only way to get things done quickly, and keep getting things done quickly, is to create the cleanest code they can, to work as well as possible, and keep the quality very high. I told them that quick-and-dirty is an oxymoron. Dirty always means slow.
On the last day of my visit the infamous manager (now the CTO) stopped into our conference room. We talked over the issues. He was constantly trying to find a quick way out. He was manipulative and cajoling. “What if we did this?” or “What if we did that?” He’d set up straw man after straw man, trying to convince his folks that there was a time and place for good code, but this was not it.
I wanted to hit him.
Then he made the dumbest, most profoundly irresponsible statement I’ve (all too often) heard come out of a CTOs mouth. He said:
“Business software is messy and ugly.”
No, it’s not! The rules can be complicated, arbitrary, and ad-hoc; but the code does not need to be messy and ugly. Indeed, the more arbitrary, complex, and ad-hoc the business rules are, the cleaner the code needs to be. You cannot manage the mess of the rules if they are contained by another mess! The only way to get a handle on the messy rules is to express them in the cleanest and clearest code you can.
In the end, he backed down. At least while I was there. But I have no doubt he’ll continue his manipulations. I hope the developers have the will to resist.
One of the developers asked the question point blank: “What do you do when your managers tell you to make a mess?” I responded: “You don’t take it. Behave like a doctor who’s hospital administrator has just told him that hand-washing is too expensive, and he should stop doing it.”
Active Record vs Objects 100
Active Record is a well known data persistence pattern. It has been adopted by Rails, Hibernate, and many other ORM tools. It has proven it’s usefulness over and over again. And yet I have a philosophical problem with it.
public class Employee extends ActiveRecord {
private String name;
private String address;
...
}
We should be able to fetch a given employee from the database by using a call like:
Employee bob = Employee.findByName("Bob Martin");
We should also be able to modify that employee and save it as follows:
bob.setName("Robert C. Martin");
bob.save();
In short, every column of the Employee table becomes a field of the Employee class. There are static methods (or some magical reflection) on the ActiveRecord class that allow you to find instances. There are also methods that provide CRUD functions.
Even shorter: There is a 1:1 correspondence between tables and classes, columns and fields. (Or very nearly so).
It is this 1:1 correspondence that bothers me. Indeed, it bothers me about all ORM tools. Why? Because this mapping presumes that tables and objects are isomorphic.
The Difference between Objects and Data Structures
From the beginning of OO we learned that the data in an object should be hidden, and the public interface should be methods. In other words: objects export behavior, not data. An object has hidden data and exposed behavior.
Data structures, on the other hand, have exposed data, and no behavior. In languages like C++ and C# the struct
keyword is used to describe a data structure with public fields. If there are any methods, they are typically navigational. They don’t contain business rules.
Thus, data structures and objects are diametrically opposed. They are virtual opposites. One exposes behavior and hides data, the other exposes data and has no behavior. But that’s not the only thing that is opposite about them.
Algorithms that deal with objects have the luxury of not needing to know the kind of object they are dealing with. The old example: shape.draw();
makes the point. The caller has no idea what kind of shape is being drawn. Indeed, if I add new types of shapes, the algorithms that call draw()
are not aware of the change, and do not need to be rebuilt, retested, or redeployed. In short, algorithms that employ objects are immune to the addition of new types.
By the same token, if I add new methods to the shape class, then all derivatives of shape must be modified. So objects are not immune to the addition of new functions.
Now consider an algorithm that uses a data structure.
switch(s.type) {
case SQUARE: Shape.drawSquare((Square)s); break;
case CIRCLE: Shape.drawCircle((Circle)s); break;
}
We usually sneer at code like this because it is not OO. But that disparagement might be a bit over-confident. Consider what happens if we add a new set of functions, such as Shape.eraseXXX()
. None of the existing code is effected. Indeed, it does not need to be recompiled, retested, or redeployed. Algorithms that use data structures are immune to the addition of new functions.
By the same token if I add a new type of shape, I must find every algorithm and add the new shape to the corresponding switch statement. So algorithms that employ data structures are not immune to the addition of new types.
Again, note the almost diametrical opposition. Objects and Data structures convey nearly opposite immunities and vulnerabilities.
Good designers uses this opposition to construct systems that are appropriately immune to the various forces that impinge upon them. Those portions of the system that are likely to be subject to new types, should be oriented around objects. On the other hand, any part of the system that is likely to need new functions ought to be oriented around data structures. Indeed, much of good design is about how to mix and match the different vulnerabilities and immunities of the different styles.
Active Record Confusion
The problem I have with Active Record is that it creates confusion about these two very different styles of programming. A database table is a data structure. It has exposed data and no behavior. But an Active Record appears to be an object. It has “hidden” data, and exposed behavior. I put the word “hidden” in quotes because the data is, in fact, not hidden. Almost all ActiveRecord derivatives export the database columns through accessors and mutators. Indeed, the Active Record is meant to be used like a data structure.
On the other hand, many people put business rule methods in their Active Record classes; which makes them appear to be objects. This leads to a dilemma. On which side of the line does the Active Record really fall? Is it an object? Or is it a data structure?
This dilemma is the basis for the oft-cited impedance mismatch between relational databases and object oriented languages. Tables are data structures, not classes. Objects are encapsulated behavior, not database rows.
At this point you might be saying: “So what Uncle Bob? Active Record works great. So what’s the problem if I mix data structures and objects?” Good question.
Missed Opportunity
The problem is that Active Records are data structures. Putting business rule methods in them doesn’t turn them into true objects. In the end, the algorithms that employ Active Records are vulnerable to changes in schema, and changes in type. They are not immune to changes in type, the way algorithms that use objects are.
You can prove this to yourself by realizing how difficult it is to implement an polymorphic hierarchy in a relational database. It’s not impossible of course, but every trick for doing it is a hack. The end result is that few database schemae, and therefore few uses of Active Record, employ the kind of polymorphism that conveys the immunity of changes to type.
So applications built around ActiveRecord are applications built around data structures. And applications that are built around data structures are procedural—they are not object oriented. The opportunity we miss when we structure our applications around Active Record is the opportunity to use object oriented design.
No, I haven’t gone off the deep end.
I am not recommending against the use of Active Record. As I said in the first part of this blog I think the pattern is very useful. What I am advocating is a separation between the application and Active Record.
Active Record belongs in the layer that separates the database from the application. It makes a very convenient halfway-house between the hard data structures of database tables, and the behavior exposing objects in the application.
Applications should be designed and structured around objects, not data structures. Those objects should expose business behaviors, and hide any vestige of the database. The fact that we have Employee tables in the database, does not mean that we must have Employee classes in the application proper. We may have Active Records that hold Employee rows in the database interface layer, but by the time that information gets to the application, it may be in very different kinds of objects.
Conclusion
So, in the end, I am not against the use of Active Record. I just don’t want Active Record to be the organizing principle of the application. It makes a fine transport mechanism between the database and the application; but I don’t want the application knowing about Active Records. I want the application oriented around objects that expose behavior and hide data. I generally want the application immune to type changes; and I want to structure the application so that new features can be added by adding new types. (See: The Open Closed Principle)
Architecture is a Second Order Effect 61
We often argue that in order to achieve flexibility, maintainability, and reusability, it is important to have a good software architecture. This is certainly true. Without a well ordered architecture, software systems become masses of tangled modules and code. However, the effect of architecture on the ‘ilities is secondary to the effect that a good, fast, suite of tests has.
Why don’t we clean our code? When we see an ugly mass of code that we know is going to cause of problems, our first reaction is “This needs to be cleaned up.” Our second reaction is: “If I touch this code I’ll be spending the next two weeks trying to get it to work again.” We don’t clean code because we are afraid we’ll break it.
In this way bad code hooks itself into our systems and refuses to go away. Nothing stops clean code from going bad, but once it’s bad, we seldom have the time, energy, or nerve to clean it. In that sense, bad code is permanent.
What if you had a button? If you push this button a little light instantly turns red or green. Green means your system works. Red means it’s broken. If you had a button like this, you could make small changes to the system, and prove that they didn’t break anything. If you saw a batch of tangled messy code, you could start to clean it. You’d simply make a tiny improvement and then push the button. If the light was green you’d make the next tiny change, and the next, and the next.
I have a button like that! It’s called a test suite. I can run it any time I like, and within seconds it tells me, with very high certainty, that my system works.
Of course, to be effective, that test suite has to cover all the code in the system. And, indeed, that’s what I have. My code coverage tools tell me that 89% of the 45,000 lines in my system are covered by my test suite. (89% is pretty good number given that my coverage tool counts un-executable lines like interfaces.)
Can I be absolutely sure that the green light means that the system works? Certainly not. But given the coverage of my test suite, I am reasonably sure that the changes I make are not introducing any bugs. And that reasonable surety is enough for me to be fearless about making changes.
This fearlessness is something that needs to be experienced to understand. I feel no reluctance at all about cleaning up the code in my system. I frequently take whole classes and restructure them. I change the names of classes, variables, and functions on a whim. I extract super-classes and derivatives any time I like.
In short, the test suite makes it easy to make changes to my code. It makes my code flexible and easy to maintain.
So does my architecture of course. The design and structure of my system is very easy to deal with, and allows me to make changes without undue impact. The reason my architecture is so friendly, is that I’ve been fearless about changing it. And that’s because I have a test suite that runs in seconds, and that I trust!
So the clean architecture of my system is a result of on-going efforts supported by the test suite. I can keep the architecture clean and relevant because I have tests. I can improve the architecture when I see a better approach because I have tests. It is the tests that enable architectural improvement.
Yes, architecture enables flexibility, maintainability, and reusability; but test suites enable architecture. Architecture is a second order effect.