The speaker at the Austin JUG last week was Venkat Subramaniam (https://twitter.com/venkat_s, http://www.agiledeveloper.com/). He is to Java what Sandi Metz is to Ruby: If he is speaking in your town, you don’t care what the specific topic is. You know you need to go because you will learn something.
The topic was Twelve Ways to Make Code Suck Less. I will post some notes I took at the presentation. He gave the list counting down from twelve to one.
Why should we care about code quality? You cannot be agile if your code sucks. Code we write is how we tell our colleagues how we feel about them. Quality code is code that can be understood quickly.
- 12. Schedule time to lower technical debt. Technical debt can take the form of not upgrading a framework (or JDK) to a more current version. Bad code is not technical debt; it is sabotage. You should make a list of your technical debt and ask yourself if you are adding to it, or reducing it. Do not schedule 100% of your developers’ time for adding new features. You need some slack time; you need to spend some time learning.
- 11. Favor high cohesion. Highly cohesive code does one thing and does it well. It reduces the frequency/reasons for change. High cohesion leads to low complexity. He mentioned Cyclomatic complexity. TL/DR: Be a dessert topping OR a floor wax.
(I once again point you to the github page for Boot, with this little gem: “The Modern CLJS tutorials are an excellent introduction to Boot and ClojureScript. Pretty much everything you need to know about Boot to get a project off the ground is covered there. Check it out!” What if I want to learn about Boot but do not want to spend any time on ClojureScript? Why not have a tutorial that is just about Boot?)
- 10. Favor loose coupling. Remove coupling if possible. Code with high coupling is hard to test, hard to extend. Having a lot of mocks in a test is a bad sign.
- 9. Program with intention. He mentioned Kent Beck’s Design Rules:
– Passes the tests
– Reveals intention
– No duplication
– Fewest elements
See also http://wiki.c2.com/?XpSimplicityRules They are in order, so passing the tests takes priority over intention. (So why does Dr Subramaniam go in reverse order?) We should program deliberately.
- 8. Avoid primitive obsession. Do not write general code. Imperative code has accidental complexity.
If you write a for-loop, you might wonder if you need less-than, or less-than-or-equal-to. That is a sign. We do imperative because it is familiar. He gave an example of a method with a for-loop. He noted you would need extra variables to hold the result and the count. He contrasted it with an example of functional code using Java lambdas. Functional code is about a series of transformations, not mutations. You can read the functional example in one pass, while with the imperative you need to go up and down while reading it.
Imperative code is easy to write, but harder to read, while functional is easier to read. Good code is a story, not a puzzle. Lambdas are glue code, and sometimes two lines might be too many.
Lambdas still take some work to understand. You need to learn the semantics. The functional style is a combination of declarative code and higher-order functions.
- 7. Prefer clear code to clever code. Clever code makes you feel smart. Do not focus on performance too early.
- 6. Apply William Zinsser‘s principles on writing: Simplicity, clarity, brevity, humanity.
He recommended “On Writing Well” by William Zinsser. He said when his son asked to borrow his copy, he refused. Instead he bought his some another copy, and his son has re-read it at least three times.
- 5. Comment the why, not the what. A lot of developers make a lot of comments because a lot of companies and professors mandate comments. Do not comment to cover up bad code. Write expressive code that is self-documenting. Many times a comment is like explaining a joke. He recommended the book The Design of Everyday Things.
- 4. Avoid long methods – Apply SLAP (Single Level of Abstraction Principle) Long methods are hard to understand, lack cohesion, tend to become longer, and are harder to test. How to break up methods? Think about what layer of abstraction you are at.
He said it could be a bad idea to have a hard and fast rule about how many lines a method should be. I know Sandi Metz has a rule that methods should not be any longer than five lines. But is putting a rigid number such a good idea? I am probably not as smart as Sandi Metz, and frankly, you probably are not either. If you dogmatically insist on a hard number, you will probably just get a lot of methods that call methods that call methods, etc. Maybe we should stop using the word “Rule” when “Guideline” would be more appropriate.
One example he gave is that if someone asks you what you did over the weekend, you might say, “I went to such-and-such city on Saturday, and stayed home and did laundry on Sunday”. If they ask for more details, you could give them. Or you would talk about how you woke up, had breakfast, got in the car, got gas, with route you took, etc, etc, etc. They might not want that level of detail off the bat.
You might have a method that gets info from a database table, gets info from a web service, combines/transforms them somehow, and writes the result to another table. And has all the try/catch blocks for all the exceptions. That would be a good candidate for breaking it up.
He related a story about someone who attended one of his lectures who was hired by a company to refactor a method that was 40,000 lines long. After a year, this person got it down to 30,000 lines.
I stopped to talk to someone after the presentation, and then walked to my car. I think I overheard at least three conversations about this particular point.
- 3. Give good, meaningful names. Your variable names represent your level of abstraction.
He gave an example of bad variable names from a company that he could not name. It was software used to control oil refineries. One license is $1 million. He would not name the company, but if one license is $1 million, that narrows it down.
- 2. Do tactical code reviews. Do code reviews often, and make them incremental. If you put them off, people will just allow code to pass without understanding it or really looking at it.
- 1. Reduce state and state mutation. Do not make a field in a class (or method) unless you really need it. He related a story about an exhibit of Picasso with twenty versions of a painting, instead of just showing the final result. If Picasso could iterate, so can we. (I have a note asking how do functional languages deal with databases.)
Here are some pages on his site that I found to be informative:
- How to convince your fellow developer to write short methods? –
- Article: DSL in Java and Groovy –
- Functional Programming in Java is quite Approachable –
- Lambdas are Glue Code –
- Keep Lambdas Cohesive –
- The Imperative Style –
- The Declarative Style –
- The Functional Style –
- Prefer a Functional Style over an Imperative Style –
- Functional Programming Favors Expressions over Statements –
- The Power and Practicality of Immutability –
- Functional Specialized Functions and Immutability –
- What are Pure Functions? –
- Two Rules for Purity –
- Benefits of Pure Functions –
- A Mutability Trap –
- Lazy Evaluation: may make code more expressive without losing on efficiency: Part I –
- Lazy Evaluation: may make code more expressive without losing on efficiency: Part II –
- Lazy Evaluation make infinite streams possible –
- Infinite Streams May Remove Accidental Complexity –
- What is a Closure? – closures get info from surrounding state, lambdas do not
- Closures don’t Mean Mutability – get info from surrounding state, but do not change it