{"id":891,"date":"2019-01-14T21:53:41","date_gmt":"2019-01-14T20:53:41","guid":{"rendered":"https:\/\/craftcoders.app\/?p=891"},"modified":"2024-08-14T15:29:10","modified_gmt":"2024-08-14T13:29:10","slug":"clean-code-the-hard-facts-and-figures","status":"publish","type":"post","link":"https:\/\/craftcoders.app\/clean-code-the-hard-facts-and-figures\/","title":{"rendered":"Clean Code: The hard facts and figures"},"content":{"rendered":"\r\n
A couple of weeks ago I began to read Uncle Bob’s old developer bible: Clean Code. I was excited about the presented concepts and put a lot of thought into applying them into code. One day I was lying on the couch of our office, reading Chapter 2: Meaningful Names.<\/em> Meanwhile, I overheard our team discussing the best troll name for a new database. Another day, I was reading the next chapter. Chapter 3: Functions should be small and do one thing only.<\/em> Back at my desk, I found myself scrolling through functions with hundreds of lines of code.<\/p>\r\n\r\n\r\n\r\n Although most teams I know try to produce clean code, it seems to be a hard thing to keep a project clean while it grows. I began to wonder: How much clean code is really out there in the wild? Followed by: How can a project even be considered as clean? So I picked some famous open source projects and analyzed them!<\/p>\r\n\r\n\r\n\r\n First, let’s summarize what I did: My first intent was to check a static code analysis tool like SonarQube, but I could hardly find an open-source project which also published the results of such tooling. This is when Metrilyzer<\/a> was born. An analysis tool of mine (private projects again<\/a>^^) which can read almost every Java-based project to do some data analysis on it. At first, I focused on the following metrics:<\/p>\r\n\r\n\r\n\r\n Of course, they are not enough to consider a project as “cleanly coded” but in my opinion they give a good indication on code modularity and compliance with single responsibility principle. This is one of the hardest things to accomplish from my point of view. So using these metrics you can at least see clearly when a project is not clean coded. \ud83d\ude09 Here are the results.<\/p>\r\n\r\n\r\n\r\n The four tested projects are Cassandra 3.11<\/a>, ElasticSearch 6.5<\/a>, Spring Boot 2.1<\/a> and Neuronizer Notes<\/a> (an Android app of mine). In the boxplots you can see the number of lines per class<\/strong> (y-axis) per project (x-axis). N is the number of classes in the project (which could be analyzed by Metrilyzer). The maximum values are somehow obscured so that the rest of the plot looks better, but you can still read them in the table. If you don’t know how boxplots work look here: What a Boxplot Can Tell You about a Statistical Data Set<\/a><\/p>\r\n\r\n\r\n\r\n You can see that most of the classes are very small and more than 75% of all classes are smaller than 100 lines of code. Despite every project having a couple of huge classes. It seems like the bigger the project, the longer the longest class is. Not very surprising, but things get more interesting when you compare different metrics. Let’s take a look at lines per method<\/strong> for example:<\/p>\r\n\r\n\r\n\r\n Like the classes, most of the methods are very small and more than 75% are smaller than 15 lines per method. Despite a large number of methods Spring Boot does a very good job at keeping them small. With a maximum of 54 lines per method. Also interesting is the correlation between N in the two metrics (which is the average number of methods per class<\/strong>):<\/p>\r\n\r\n\r\n\r\n I have to mention that getter and setter methods are excluded so in reality, the numbers are slightly higher (see metrics at the end). Neuronizer which is a small application has an easy time at keeping classes and methods small. As you can see Cassandra and Elastic Search do have a harder time. But Spring Boot is doing very well in comparison to the others. They have even smaller methods than my little android app. Okay, now let’s take a look at the most problematic classes.<\/p>\r\n\r\n\r\n\r\n What you can see here are the five most biggest classes for each project.<\/p>\r\n\r\n\r\n\r\n What I recognized at first were the test classes. Since the teams out there (at least those I have been part of) care less about test code quality vs. productive code quality it makes sense they can get very long. You can also see that long classes lead to long test classes. Elastics InternalEngine <\/em>and InternalEngineTests <\/em>for example. As test classes grow it gets harder and harder to keep them maintainable, so a well thought-out model for test classes should be applied. Regarding large test classes, I can recommend the article Writing Clean Tests \u2013 Small Is Beautiful.<\/a><\/p>\r\n\r\n\r\n\r\n Another important thing you can learn from this table is where the application has not been modeled carefully. Cassandras StorageService<\/em>, for example, has a very generic name and became the biggest god class in the project. Elastics Engine<\/em> and InternalEngine<\/em> had a similar destiny. These classes could easily be separated in a couple of subclasses, but as they are now they just cannot be clean.<\/p>\r\n\r\n\r\n\r\n For the interested guys out there, here are the other metrics in an uncommented form. They will be mentioned in the Conclusion though. All visualizations have been done using goodcalculators.com.<\/a><\/p>\r\n\r\n\r\n\r\nWhat makes a project clean?<\/h2>\r\n\r\n\r\n\r\n
\r\n
Cassandra, ElasticSearch, Spring Boot – The hard figures<\/h2>\r\n\r\n\r\n\r\n
\r\n
Pinning down problems<\/h2>\r\n\r\n\r\n\r\n
\r\n\r\n
\r\n \u00a0<\/td>\r\n Lines per class<\/strong><\/td>\r\n<\/tr>\r\n \r\n Cassandra<\/strong><\/td>\r\n org.apache.cassandra.service.StorageService: \u00a0\u00a0\u00a0\u00a04300
org.apache.cassandra.cql3.validation.operations.SelectTest: \u00a0\u00a0\u00a0\u00a02427
org.apache.cassandra.service.StorageProxy: \u00a0\u00a0\u00a0\u00a02244
org.apache.cassandra.db.LegacyLayout: \u00a0\u00a0\u00a0\u00a02160
org.apache.cassandra.db.ColumnFamilyStore: \u00a0\u00a0\u00a0\u00a02136<\/td>\r\n<\/tr>\r\n\r\n Elastic Search<\/strong><\/td>\r\n org.elasticsearch.index.engine.InternalEngineTests: \u00a0\u00a0\u00a0\u00a04653
org.elasticsearch.index.translog.TranslogTests: \u00a0\u00a0\u00a0\u00a02804
org.elasticsearch.index.shard.IndexShardTests: \u00a0\u00a0\u00a0\u00a02652
org.elasticsearch.index.engine.InternalEngine: \u00a0\u00a0\u00a0\u00a02631
org.elasticsearch.index.shard.IndexShard: \u00a0\u00a0\u00a0\u00a02566<\/td>\r\n<\/tr>\r\n\r\n Spring Boot<\/strong><\/td>\r\n org.springframework.boot.context.properties.ConfigurationPropertiesTests: \u00a0\u00a0\u00a0\u00a01509
org.springframework.boot.test.json.JsonContentAssert: \u00a0\u00a0\u00a0\u00a01277
org.springframework.boot.SpringApplicationTests: \u00a0\u00a0\u00a0\u00a01269
org.springframework.boot.SpringApplication: \u00a0\u00a0\u00a0\u00a01267
org.springframework.boot.test.web.client.TestRestTemplate: \u00a0\u00a0\u00a0\u00a01234<\/td>\r\n<\/tr>\r\n\r\n Neuronizer<\/strong><\/td>\r\n de.djuelg.neuronizer.presentation.ui.fragments.TodoListFragment: \u00a0\u00a0\u00a0\u00a0458
de.djuelg.neuronizer.presentation.ui.fragments.PreviewFragment: \u00a0\u00a0\u00a0\u00a0285
de.djuelg.neuronizer.presentation.ui.fragments.ItemFragment: \u00a0\u00a0\u00a0\u00a0251
de.djuelg.neuronizer.storage.TodoListRepositoryImpl: \u00a0\u00a0\u00a0\u00a0248
de.djuelg.neuronizer.storage.TodoListRepositoryImplTest: \u00a0\u00a0\u00a0\u00a0214<\/td>\r\n<\/tr>\r\n<\/tbody>\r\n<\/table>\r\n\r\n\r\n\r\n\r\n