design

Agile is Clean Code

22 Jun 2010
Posted by Federico Zuppa

But ... Isn't this a requirement for all approaches? Why do I have the impression that in Agile, the technical aspect is more important? When I was at University, I remember vividly having heard (and unfortunately later repeated) that the programmer had one of the less important tasks in the chain of producing software. The most important, more senior and smarter people are the architects/designers that analyse and design the application, giving the programmers some diagrams that they 'just' have to translate into code. That seemed like an easy task!!

What means Clean Code?

Ron Jeffries says that we should write "clean code that works". Uncle Bob then made an entire book of Clean Code. What is clean code? Following uncle Bob's explanation, at the high level, it is code that is well structured and which follows SOLID design principles. At a lower level, it's code that has meaningful names, good and useful comments, elegant functions and formatted code. It's code that expresses the intent of the developer in a clear way. It's code that contains no duplication. And is code that is easy to understand.
And how do you know it works? You don't know because you code it and you are a great programmer :-). You know it because there are automated tests that certify it. Agile development relies on test automation to know that features work and to know thay they do all the time. Beck says "Software features that can't be demonstrated by automated tests simply don't exist". Test automation is one of the pillars of clean code because it allows to continually refactor, it allows to redesign and ultimately it allows to deliver a continuous flow of value.

Why is so important in Agile?

These are a few reasons to justify the 'pain' of writing better code:

  1. Cost of Change is lowered: Agile welcomes change, even late in the development process. However, to be able to change, it should be cheap to change. Code that is well designed, easy to read and thoroughfully tested is easy to change and can be changed without fear of introducing new bugs. On the contrary, code that is hard to understand, is not well modularized and has no automated tests is difficult to change and the risks of introducing bugs while modifying it are many.

  2. Iterative & Incremental Development means more time coding: In sequential development, there are big phases of analysis and design before the coding phase starts. When the code phase starts, most of the design decisions should have been taken, so development proceeds fast. In Agile, development starts earlier so this phase takes more time. As the code base needs to be manipulated for a longer time, it pays to have it clean. Reading Clean Code, one of the a-ha moments that I had was in the chapter where Martin explains that in the life of a system, the majority of the time is spent reading code, not writing it. I started thinking about my experience, and that was entirely true. I have worked in many systems that already have a lot of features in it. In those systems, each time that I needed to add a new feature, I spent most of my time finding where to put the feature and how to accomodate it to the existing features than actually coding it.

  3. Iterative & Incremental Development means the code is changed frequently: Design is performed incrementally in Agile. Every iteration, new features are included in the code base. The design of the system should be one that cares about all features included until that moment. Therefore, this means that a lot of times the existing design needs to be modified. If the code is not maintained clean, understanding and modifying it may become difficult in a short period of time.

  4. Working software is more important than Comprehensive Documentation: This is what the Agile Manifesto says. But having the code base in good state, with good comments, good names, well modularized and with a comprehensive set of automated tests is the best documentation that a developer may have. Been able to understand a piece of code easily because it's simple and it expresses its purpose with clarity is much better than going through UML design documents. Being able to run and debug a set of unit tests for certain functionality provides the best tool to understand that functionality. After all, what really happens is what is in the code, not what is in the documentation. Code is always up to date while documentation is hard to maintain.

Why don't we do it then?

Lack of skill? Sometimes... This is a craft. A difficult one and we need to spend a lot of time getting better on it. Commitments.. Yes. Our own commitments and the commitments of our managers. Being under pressure and with the deadlines on sight, make it 'work' takes another meaning (I mean, you just want to throw some code that at least compiles and shows something on the screen). I won't follow. There are always plenty of reasons to do things worse!!

Humm.... but Scrum doesn't include a topic on technical practices

I've seen great debates last year over the lack of technical practices in Scrum. Some people believe it is a shortcoming of Scrum while others say Scrum is a product development framework and therefore it is out of scope to include a technical section. Without entering into this debate, something that I've seen a number of times is software development teams starting 'just' with Scrum, leaving the introduction of some XP practices like TDD for the future (if you take a look the popularity of XP and Scrum, I guess many people observed the same!). This may be justified by an approach where changes are introduced gradually (after all, Scrum per se represents a major change at the organizational and process level).
But what happens if you start with 'just' Scrum?. Basically, I believe Scrum alone is not sustainable. At least, in software development it is not. As iterations go by and the code base grows, there is more and more work to finish each feature. By consequence, teams cannot keep their commitments, the business people get mad and you hit the Scrum Wall (ouch, hit it many times)
This is still a topic I think about often. Scrum, per se, is not sustainable in software development. However, it is the most succesful process in the Agile world. It seems that leaving technical aspects out of it is both something that has help Scrum become more popular and the reason of many of its failures in the development world.

Conclusion

Clean Code is one of the pillars of Agile. Being able to deliver clean code that works greatly enhances the chances of deliver value sustainably and it makes our life as programmers better. Clean code provides the best ROI and the best documentation. Of course it is difficult to do it and it certainly takes a lot more time than writing crappy code. However, by not doing it, we lose a lot of the advantages that Agile claims.

Posted by Hernan Wilkinson

When talking about Smalltalk, there is definitively an over use on the possibility to add messages to Object class. It is so easy to do it, that people usually do it just to get something working fast, even if the coding is poor. There are a lot of messages (mainly #isXXX messages) that do not belong to Object and represent a bad design decision. Most of them are implemented there because they are "handy" and easily "reused". For example #-> or #assert: implemented in Squeak. Definitively not all objects should respond to them. Many of these messages are only used from "inside" the object, like the #assert: message.I would never write something like this: 1 assert: xxxx. Instead I would write: self assert: xxx witch clearly shows that #assert: is not a message that should be respond by every object, but only for those that represent assertions.

From my point of view, this issue is not an inheritance's problem per se, it is a miss-used of inheritance. If I try to use a hammer as a screwdriver, it is not the hammer's fault, but mine.
How is inheritance related with reuse? Well, that is an important question. Inheritance from the "pure theory" point of view, should not be use as a means of reuse. Reuse comes from good models, not from inheritance. Inheritance should be used as a tool to organize the knowledge that as a programmer, you are acquiring from the business domain. Classes should be use to represent the concepts and ideas you see in the business domain and inheritance should be used to organize how this concepts are related in an ontological way. So for example, an abstract class should represent an abstract concept wich defines the essential behavior that all the objects instances of its concrete subclasses should respond. Due to this relationship reuse comes aside, but reuse is not the means of inheritance. Subclassing just to reuse the implementation of a superclass is not a good design decision; it will bring problems sooner or later.

There has been an attempt to minimize the methods implemented in the Object class. For example on the Squeak distribution the class ProtoObject has been created. ProtoObject has only 35 methods whereas Object has 436! (on the basic image of Squeak version 3.10.x). Although ProtoObject has fewer methods, I do not agree with some of them. For example #ifNil: (and the like) and #tryNamedPrimitive: (and the like). Clearly these two messages (and their mutations) are implemented in ProtoObject as a means of reuse and not because every object should understand them. For example, why an account should respond #tryNamedPrimitive:? What does it mean to an account? A better design should have an object that represents the VM (for example) to which I can send the message #tryNamedPrimitive:. Of course the problem with this is how to access to this object and that is why that message is implemented in ProtoObject, because every object will inherit that method! And it will be so easy to use it as to write self tryNamedPrimitive: xxx. But thinking a little bit we can see that it is very easy to solve this problem. For example declaring this VM object in a global scope, so any object could send the message #tryNamedPrimitive: (and therefore reuse it), but not understand it. This brings me to the "rule" that says composition is a better tool to reuse.
The problem of using inheritance to reuse is that inheritance generates a strong coupling between the classes, its subclasses and its instances, where composition does not.

Inheritance generates an implementation and structural coupling between the classes and its subclasses (affecting directly their instances) where composition only couples an object with the composed one through the messages the former sends to the later. No implementation coupling, no structural coupling, just coupled by the message names one object send to the other, therefore a better design (the lower the coupling the better). This is the reason why good frameworks, black-box frameworks use composition over inheritance, where white-box frameworks being more immature, use inheritance to configure them.
This brings me to the idea of how hard should we stick to this, should we never "break" this rule? Well, I would not called it a rule but an heuristic, therefore it should be follow as much as possible, but we should also be pragmatic too. Sometimes subclassing to reuse while we are still learning about the problem is ok, is like making a white-box framework at the beginning. But we should never forget that our goal is a "black-box framework".
Other languages like Java do not suffer this problem and good designs can be implemented with it (although not so easily :-)). Java does not have this problem because Object class cannot be modified. This clearly shows that having too many methods in Object is not a problem of inheritance, but on how use it.

To summarize:

  • Inheritance should not be used to reuse, therefore having a lot of methods in Object class just because it is "handy" is a clear example of inheritance being used incorrectly. It generates unnecessary coupling which will make the system harder to change or refactor later.
  • Inheritance is just a tool; you can use it right or wrong.
  • There are other tools like composition and design techniques which solve the same problems and generate less coupling.
  • Reuse comes from good models, not from inheritance.