I have seen - and had to maintain - so many messed up, bad code in my carrier that it makes me wonder why I still work in this profession. In fact, I have rarely seen good, clean code. However, I can learn a ton going through open source code repos on Github.
The best definition I have found for clean code is by Michael Feathers captured in the book Clean Code: "Clean code always looks like it was written by someone who cares."
Do you really care about the code or the craft, when:
- you put 2844 lines of code in the model?
- you have 167 lines of code in one function?
- you have several deep nested if statements in for-each loops?
- you have 1354 lines of code in a js file that drives business logic?
- you have no tests at all?
I attended the fantastic Simple Design and Testing Conference a while ago. One of the topics we discussed there was the most important principle we'd ask a developer should follow. DRY was the absolute winner.
We all know about and follow the DRY principle but I question if that's enough?
Quite frankly I don't have the patience to analyze a 60 line function loaded with iterators and conditionals. Even the code's author can not understand it 5 minutes after it was written.
My coding style has changed in the last 4-5 years. I tend to write code in a functional style, class objects with a single function that are not longer than 5-10 lines.
Here is one of those:
module Services; module Utils class DecryptsData class << self def execute(service_result) return service_result unless service_result.success? encrypted_data = service_result.fetch(:encrypted_data) decrypted_data = ::Encryption.decrypt(encrypted_data) data_key = service_result.fetch(:data_key) service_result[data_key] = decrypted_data service_result end end end end; end
Look at this piece of code for a moment. Try to understand what I am doing here.
- A guard condition (line 7)
- Pulling the encrypted data from the context (line 9)
- Decrypting data (line 10)
- Pulling the key I save the decrypted data with (line 11)
- Saving the decrypted data in the context (line 13)
All I am doing is decrypting data. That's it. I am not querying the database, I am not validating data, I am not calling an external service and I am not looping through items and set properties based on some kind of predicate.
People might just shove this into the controller. I won't. I think about software as a collection of functions that's weaved together by organizer functions.
The benefits are enormous:
- One function
- -> which is short
- Easy to understand
- -> which is easy to test
You could say that I am doing something in Ruby that resembles to functional programming. I call functions on class objects, but the functions I am constructing are not immutable. And I new up an object and maintain state if I need to, but I try to avoid that for the sake of simplicity. I don't want anybody - who maintains the code I write - to spend a lot of time trying to understand what I am doing.
Is functional programming far for me? I don't think so, I believe I am just taking the first steps in that direction.