Small Classes!
More times than not I've come across classes that are greater than 500 lines long and developers would wonder how they would be able to change that class and ensure that it still kept its intended behavior. I would say that change is almost impossible with a class that big. Chapter 11 of Clean Code says that "the first rule of classes is that they should be small. The second rule of classes is that they should be smaller than that".
Object-oriented programming as I learned in college was a bit of a blur. I recall beginner-level programming classes taught in Java were a lot about moving robots within a virtual world, board games and such. Of course, we did learn about polymorphism, inheritance and abstraction, but there were other parts of OOP that we were missing out on, i.e. encapsulation, decoupling. The concepts and practices that make code maintainable are not trivial though they might seem obvious to some. I would've liked to have been taught those concepts rigorously in school, or at least been coached in the right direction instead of just dumping everything into a main method to get it to work.
How do we keep classes small then?
My first day at ThoughtWorks University, our trainers talked about the Single Responsibility Principle, which states that each object should only have one, and only one reason to change. How do you know if your class is doing more than one thing? We did an exercise at TWU which I think helped train our brains in the right direction: Write a brief description of the responsibility of the class without using the words "if", "and", "or" or "but". If you have to use any of those words, then it's a hint that the class might be doing one too many things.
Classes should also be cohesive. The more instance variables a method manipulates, the more cohesive that method is to its class. Which also means that we cannot have too many instance variables because that might mean too many methods and hence too many lines of code in a class. As part of a refactoring process, we would usually try to keep methods short, and in doing that we might need to extract code out into a separate method. But as highlighted in Clean Code, we might also find that extracting code into a method B forces us to pass down all the variables from method A. As soon as you find yourself doing that, the book advises us to promote all those variables to instance variables of the class. As you keep doing this, your class will start to lose cohesion. The solution? Split the code out into other classes!
Depend on interfaces instead of concretions. Code will change because requirements will change. It's inevitable. So we have to protect code from that risk by coding against interfaces. Consider the following example. We have a website that is populated by content that is fed in manually by Content Producers. Their job is to filter through all content and mark some of them to be published on the website. The implementation of retrieving content can be done in many different ways, i.e through a content management system that interfaces a database, or it could be piped in through JMS, etc.
public interface ContentService {
Content retrieveContent();
}
If we don't have an interface for the service, our tests and code will have to change if content was migrated to another system. Coding against interfaces makes it easy to stub out retrieveContent() so we can test it without having it break every time the implementation changes.
Agility lowers the risk and the adversity towards change. Clean code ensures that we can isolate our code from external changes but make internal changes easier and less painful.