I'm a fan of Open Source and have a growing interest in serverless and edge computing. I'm not a big fan of spiders, but they're doing good work eating bugs. I also stream on Twitch.
Readability over being clever and the level of confidence for tests I write for a feature/bug fix over having 100% test coverage.
And if I’m being honest, I’ve never worked on a project that had 100% test coverage aside from a take home test I did for an interview a few years ago. 🙃
I've found that you can't really start out trying to make code clean. There are simply too many unknowns, too many variables at the beginning of a project to be able to start out clean. No matter how many "good abstractions" and "clean patterns" you put in place, things will start to get muddled and messy over time.
Thus, what makes code clean is going back after a while, looking at what's messy and deciding then what you actually need to do to make the code maintainable. Code doesn't start out clean. Cleaning it up makes it clean.
You start with the best intentions but you're absolutely right, there are too many directions to choose, unknowns, etc. and you end up chopping about to get to where you want to be and to actually get something built.
I wrote an article on this a while back, namely continuous refactoring, which helps as you go.
In my early years I had no idea that clean code was even a thing.
Then I started thinking about good variable names and short functions and those things that clean code prescribes without thinking too much about the reasons behind it.
Nowadays, I think more about keeping cognitive complexity low than clean code. Cognitive complexity tries to approximate how many things the reader has to keep in mind to understand a given piece of code. The fewer things to keep track of, the easier to follow the code.
A few things that I've found to reduce cognitive complexity:
Few intermediate variables: I have developed a distaste for intermediate variables, as they just clutter the scope and I need to keep track of them. I try to skip intermediate variables by composing functions instead of saving the output of a function to send it into another function. Also, when declaring a variable, I always put it in the smallest scope possible, and avoid mutating it as much as possible.
Composability: composable code is code that is made of smaller, independent parts that are composed together. Therefore, to understand a bigger part, I can look at the smaller parts independently and take it bit by bit. Much easier to understand a composition of ten pure functions than a 100-lines long function with a dozen and a half intermediate variables and several blocks of indentation.
Purity and referential transparency: there are two big dimensions that we need to keep track of when reading code. First, what is the code doing, and second, how have previous executions changed the internal state. A pure function will always yield the same output with the same input, so I don't need to keep track of what has happened before to see how it will affect the current execution.
Avoid/abstract conditional flow: ideally, there should be a single flow to follow. Multiple conditional statements, especially when nested, make it harder to understand what will be the output of a function. A conditional flow can be easily abstracted with Maybe and Either monads.
Keeping cognitive complexity low is part of clean code. Clean code isn't just about naming and formatting. It's also about sizes (method arguments, bodies, ...), control flow depth, etc.
Although proper naming also keeps cognitive comexity low. So both terms are highly co-related.
I break it down into a couple different layers, but I generally think about it in terms of the audience more than just the code itself.
There are some baselines like: bad/inconsistent names make it difficult to understand intent, dead code makes it difficult to understand what is relevant, flaky tests make it difficult to understand what it working.
The next layer is community. You can tell when someone writes Java in Python or vice versa. This isn't always super clear cut and might have layers to itself. For example, JavaScript is definitely #NotAMonolith.
Next is your team. Every team had slightly different norms.
Lastly, I'm starting to think about codebase size as a significant factor. If you are working on something tiny, I think abstraction probably more weight than it is worth. Just writing code as plainly as possible in as few places as possible is a great thing. I think this is particularly valuable while learning a tool. If haven't developed specific habits and for specific problems with that tool, you are not increasing your productivity by adding a lot of poorly understood layers of indirection. Just write the code as plainly as possible in the place you understand as much as possible and develop those solutions as you feel pain. I belabor the point because I used to value "using the framework well" too highly.
Yes, but I can not define what it was before, or what it is now. It seems I'm currently in transition. It seems that it does not matter how the code is written it is unreasonable. This means it becomes most important to learn to read code in many styles, rather than learning a style.
In the past, I used to think that you always had to produce clean code, no matter what.
Now, I realized that it's not possible and even bad for the business because it slows you down. You have to make trade-offs and focus both on the technical and business value of your code. Having technical debt is fine.
It feels like I'm becoming more and more radical about this because of the experiences I had in several different projects. It always seems to come down to the same thing: an experienced developer bored by trivial tasks introducing some unnecessary new abstraction layer or unecessarily clever solution. And the result is always the same: the code works, it's utterly complicated and / or unreadable for others, nobody else wants to touch it until they have to.
In my opinion clean code is vital for an application. The simpler the code the cleaner it is.
I think it's best to rely on tooling and communication, both proactive, in review, and retroactive, to communicate what's desired as a team. Further, it's best to lean on your code review tools, like pull requests and merge requests, which not only preserve a record of decisions made, but also allows stakeholders to chime in.
As for 'clean code', it all depends on your approach, and your goals. If you're an OOP or FP maximalist, especially when the team is working in a multi-paradigm language like JS, TS, Rust, Ruby, Python, etc., there's absolutely many other factors to consider, than what's "idiomatic". Sure, there's an easy way and a hard way to solve a problem, and sometimes you can contribute your own thoughts and experiences. But this isn't just your codebase. It's your team's, other team's, future teams, and past teams.
Often, I find performance to be underappreciated, but performance is also subjective until it can be measured. Your tools and your review process, your communication process and perhaps even documentation process (but use sparingly, just to communicate objectives and areas of concern, rather than winding up as a maintenance sink, in addition to tests and review), are the major things you'll come to lean on.
I might also say, your approach matters, and using the term 'cleanliness' as a metric to describe code quality is highly subjective, to say the least, and not always that helpful. Linters, code coverage, unit tests and integration tests, end to end tests / smoke tests, regression tests, and most of all, team communication, are your number one guiding factors here.
I will admit, to answer the question originally stated, I regret the approach I once took in my career. I subscribed to FP and/or OOP, "idiomatic" religions, and often was adversarial when challenging the decisions of others. I've had to learn to reign it in, and provide constructive feedback and discussing higher-order goals than can just be addressed through a cursory glance at a PR/MR.
Further, it's always a balance. No single prescriptive approach works everywhere. "It depends" is better than solutioning right out of the gate. Ask more questions, and don't just jump in and do the work. Take a healthy, balanced, measured approach. To some, it might come easy. To others, like me, I've had to learn that software development is more than just writing code, however clean it may be. Much more.
Software consultant. Bestselling Author. Loves rum, alt culture, games & metal.
Formerly Head of engineering, chief technical architect, head principal engineer, lead dev, etc.
Location
London, UK
Work
Independent Software Consultant at Electric Head Software
"Clean code" as a discipline has only become increasingly important, software craftmanship as a movement, and it's raw commerical, politicised focus, can go get in the sea.
One of the most profound changes in my approach to software was understanding it to be literature. Functional literature, but literature regardless. Intent, characterisation, description, text, subtext, flow, rhythm, style, all effect software like they do prose.
It's a constrained form of communication, with grammar, and that's why we work in "programming languages". They are languages. With rules, idioms and quirks. These aren't analogies, it's what software is. It's storytelling. Constrained creative writing with purpose.
Basically, Donald Knuth was right, and called it a bajillion years ago - with the idea of literate programming. Today's languages are that thing. You will never be a great programmer unless you become an excellent communicator, and an excellent writer. The skillset is the same.
Critical thinking, expression of concept, reducing repetiton, form for impact, signposting, intent and subtext. If you want to understand great software, understand great literature.
Communication skills are not optional
If you want to teach a junior programmer to be a better programmer, teach them to write. Language is our tool for organising our thoughts. It's powerful. It has meaning. It has power.
I believe clean code (the book series) does have some great points, however, in real life a lot of these are hard to adhere 100%. The things that I have personally taken from these book series are writing code with meaningful variable names, function names that make semantic sense with the action they're performing, while I don't believe in 100% coverage, I do believe in testing the important parts of your application, the Pareto principle applies here in my case. Avoiding comments has been easy once I have everything making semantic sense. Taking function splitting to an extreme is one thing that I don't find particularly useful, if I can do it comfortably I will, otherwise I'm not gonna force it.
I've found great value in writing code with the mindset of a book author, I can come back to my code months later and pick up where I left without much head-scratching.
In the tech world where we have a constant struggle of egos, I'd be unrealistic to deny the value of clean code, as with many things, if you take it to the extreme its no longer worth the effort, but for developing countries that don't have as big of a tech community its a great resource to learn good principles by yourself
Top comments (22)
Readability over being clever and the level of confidence for tests I write for a feature/bug fix over having 100% test coverage.
And if I’m being honest, I’ve never worked on a project that had 100% test coverage aside from a take home test I did for an interview a few years ago. 🙃
I've found that you can't really start out trying to make code clean. There are simply too many unknowns, too many variables at the beginning of a project to be able to start out clean. No matter how many "good abstractions" and "clean patterns" you put in place, things will start to get muddled and messy over time.
Thus, what makes code clean is going back after a while, looking at what's messy and deciding then what you actually need to do to make the code maintainable. Code doesn't start out clean. Cleaning it up makes it clean.
This ^^^ absolutely.
You start with the best intentions but you're absolutely right, there are too many directions to choose, unknowns, etc. and you end up chopping about to get to where you want to be and to actually get something built.
I wrote an article on this a while back, namely continuous refactoring, which helps as you go.
In my early years I had no idea that clean code was even a thing.
Then I started thinking about good variable names and short functions and those things that clean code prescribes without thinking too much about the reasons behind it.
Nowadays, I think more about keeping cognitive complexity low than clean code. Cognitive complexity tries to approximate how many things the reader has to keep in mind to understand a given piece of code. The fewer things to keep track of, the easier to follow the code.
A few things that I've found to reduce cognitive complexity:
Maybe
andEither
monads.Keeping cognitive complexity low is part of clean code. Clean code isn't just about naming and formatting. It's also about sizes (method arguments, bodies, ...), control flow depth, etc.
Although proper naming also keeps cognitive comexity low. So both terms are highly co-related.
I break it down into a couple different layers, but I generally think about it in terms of the audience more than just the code itself.
There are some baselines like: bad/inconsistent names make it difficult to understand intent, dead code makes it difficult to understand what is relevant, flaky tests make it difficult to understand what it working.
The next layer is community. You can tell when someone writes Java in Python or vice versa. This isn't always super clear cut and might have layers to itself. For example, JavaScript is definitely #NotAMonolith.
Next is your team. Every team had slightly different norms.
Lastly, I'm starting to think about codebase size as a significant factor. If you are working on something tiny, I think abstraction probably more weight than it is worth. Just writing code as plainly as possible in as few places as possible is a great thing. I think this is particularly valuable while learning a tool. If haven't developed specific habits and for specific problems with that tool, you are not increasing your productivity by adding a lot of poorly understood layers of indirection. Just write the code as plainly as possible in the place you understand as much as possible and develop those solutions as you feel pain. I belabor the point because I used to value "using the framework well" too highly.
Yes, but I can not define what it was before, or what it is now. It seems I'm currently in transition. It seems that it does not matter how the code is written it is unreasonable. This means it becomes most important to learn to read code in many styles, rather than learning a style.
In the past, I used to think that you always had to produce clean code, no matter what.
Now, I realized that it's not possible and even bad for the business because it slows you down. You have to make trade-offs and focus both on the technical and business value of your code. Having technical debt is fine.
It feels like I'm becoming more and more radical about this because of the experiences I had in several different projects. It always seems to come down to the same thing: an experienced developer bored by trivial tasks introducing some unnecessary new abstraction layer or unecessarily clever solution. And the result is always the same: the code works, it's utterly complicated and / or unreadable for others, nobody else wants to touch it until they have to.
In my opinion clean code is vital for an application. The simpler the code the cleaner it is.
I think it's best to rely on tooling and communication, both proactive, in review, and retroactive, to communicate what's desired as a team. Further, it's best to lean on your code review tools, like pull requests and merge requests, which not only preserve a record of decisions made, but also allows stakeholders to chime in.
As for 'clean code', it all depends on your approach, and your goals. If you're an OOP or FP maximalist, especially when the team is working in a multi-paradigm language like JS, TS, Rust, Ruby, Python, etc., there's absolutely many other factors to consider, than what's "idiomatic". Sure, there's an easy way and a hard way to solve a problem, and sometimes you can contribute your own thoughts and experiences. But this isn't just your codebase. It's your team's, other team's, future teams, and past teams.
Often, I find performance to be underappreciated, but performance is also subjective until it can be measured. Your tools and your review process, your communication process and perhaps even documentation process (but use sparingly, just to communicate objectives and areas of concern, rather than winding up as a maintenance sink, in addition to tests and review), are the major things you'll come to lean on.
I might also say, your approach matters, and using the term 'cleanliness' as a metric to describe code quality is highly subjective, to say the least, and not always that helpful. Linters, code coverage, unit tests and integration tests, end to end tests / smoke tests, regression tests, and most of all, team communication, are your number one guiding factors here.
I will admit, to answer the question originally stated, I regret the approach I once took in my career. I subscribed to FP and/or OOP, "idiomatic" religions, and often was adversarial when challenging the decisions of others. I've had to learn to reign it in, and provide constructive feedback and discussing higher-order goals than can just be addressed through a cursory glance at a PR/MR.
Further, it's always a balance. No single prescriptive approach works everywhere. "It depends" is better than solutioning right out of the gate. Ask more questions, and don't just jump in and do the work. Take a healthy, balanced, measured approach. To some, it might come easy. To others, like me, I've had to learn that software development is more than just writing code, however clean it may be. Much more.
"Clean code" as a discipline has only become increasingly important, software craftmanship as a movement, and it's raw commerical, politicised focus, can go get in the sea.
One of the most profound changes in my approach to software was understanding it to be literature. Functional literature, but literature regardless. Intent, characterisation, description, text, subtext, flow, rhythm, style, all effect software like they do prose.
It's a constrained form of communication, with grammar, and that's why we work in "programming languages". They are languages. With rules, idioms and quirks. These aren't analogies, it's what software is. It's storytelling. Constrained creative writing with purpose.
Basically, Donald Knuth was right, and called it a bajillion years ago - with the idea of literate programming. Today's languages are that thing. You will never be a great programmer unless you become an excellent communicator, and an excellent writer. The skillset is the same.
Critical thinking, expression of concept, reducing repetiton, form for impact, signposting, intent and subtext. If you want to understand great software, understand great literature.
Communication skills are not optional
If you want to teach a junior programmer to be a better programmer, teach them to write. Language is our tool for organising our thoughts. It's powerful. It has meaning. It has power.
It's a gift, it's for everyone. 🖤
I believe clean code (the book series) does have some great points, however, in real life a lot of these are hard to adhere 100%. The things that I have personally taken from these book series are writing code with meaningful variable names, function names that make semantic sense with the action they're performing, while I don't believe in 100% coverage, I do believe in testing the important parts of your application, the Pareto principle applies here in my case. Avoiding comments has been easy once I have everything making semantic sense. Taking function splitting to an extreme is one thing that I don't find particularly useful, if I can do it comfortably I will, otherwise I'm not gonna force it.
I've found great value in writing code with the mindset of a book author, I can come back to my code months later and pick up where I left without much head-scratching.
In the tech world where we have a constant struggle of egos, I'd be unrealistic to deny the value of clean code, as with many things, if you take it to the extreme its no longer worth the effort, but for developing countries that don't have as big of a tech community its a great resource to learn good principles by yourself