Base rules: a function should do only one thing, use descriptive variable names that make sense, document your code (explain WHY the code is there, not WHAT it does).
Write your code so it can easily be expanded and refactored. Loosely coupled modules come to mind. You could use a boolean for a function parameter, but maybe a string/enum might be better if you foresee a binary parameter won't be enough in the near future.
Don't try to be "smart" by overengineering the solution. This often leads to pointless abstractions for edge cases that may never happen. Premature optimization is a cardinal sin. "You Aren't Gonna Need It" (YAGNI)
The bigger the project/feature you're building, the more upfront design you should do. If you intend to maintain the project for a very long time, spending some time on architecture design is worth it. If it is just a quick proof-of-concept kinda thing, why bother?
An app that is never shipped does not exist. You can spend ages writing beautiful code, but your users won't care how the app looks on the inside. I don't mean you should rush writing code and take shortcuts all the time. At some point your code is just "good enough".
Sometimes you create a suboptimal implementation and decide to take on the technical debt (probably because of a deadline), while the software works as intended. If you don't intend on expanding the software, why would you touch the code again? Maybe you can leave it as is. Sometimes, a change in requirements can make the tech debt go away and the problem basically solved itself!
Coding for 20 years | Working for startups for 10 years | Team leader and mentor | More information about me: https://thevaluable.dev/page/about/
Twitter: @Cneude_Matthieu
And I'm pretty sure many understand it differently.
I prefer speaking about maintainability, scalability, readability, performance, security. Not every project need all of that, and not in the same proportions.
Always trying to leave code in better shape than how you found it is really a great idea.
You can't always have the entire big picture in mind, but having the mindset that you can continuously improve things locally as you go is a great way to ensure things stay clean.
If the goal is clean code, remember that there is no such thing as "later".
Make it work, and then commit that working code first so you have it, but then immediately follow that with a cleanup commit (so you can compare or revert if the cleanup breaks something).
"Gather together those things that change for the same reason, and separate those things that change for different reasons ... a subsystem, module, class, or even a function, should not have more than one reason to change ... separating things that change for different reasons, is one of the keys to creating designs that have an independently deployable component structure."
Three-time entrepreneur. Co-founder of Algoritma, a data science academy; https://supertype.ai, a full-cycle data science agency; GrowthBot (chatbot on Slack). Building: Learnblockchain.academy
Location
Indonesia / Singapore
Work
Co-founder, https://Supertype.ai and https://Algorit.ma
Make it work first. Clean up later.
This is so true
I did the same
Make sure it has excellent test coverage. Clean it up later. Without good test coverage cleaning it up is fraught with peril.
Perhaps 97 Things Every Programmer Should Know has some ideas.
FYI: "Always leave the campground cleaner than you found it." is The Boy Scout Rule.
Sometimes you create a suboptimal implementation and decide to take on the technical debt (probably because of a deadline), while the software works as intended. If you don't intend on expanding the software, why would you touch the code again? Maybe you can leave it as is. Sometimes, a change in requirements can make the tech debt go away and the problem basically solved itself!
I'm not sure what clean code means.
And I'm pretty sure many understand it differently.
I prefer speaking about maintainability, scalability, readability, performance, security. Not every project need all of that, and not in the same proportions.
So, to answer the question: "it depends".
Always trying to leave code in better shape than how you found it is really a great idea.
You can't always have the entire big picture in mind, but having the mindset that you can continuously improve things locally as you go is a great way to ensure things stay clean.
If the goal is clean code, remember that there is no such thing as "later".
Make it work, and then commit that working code first so you have it, but then immediately follow that with a cleanup commit (so you can compare or revert if the cleanup breaks something).
Simple rule: Try not to break the "Single Responsibility Principle" (SRP), and most things will fall into place
SRP:
"Gather together those things that change for the same reason, and separate those things that change for different reasons ... a subsystem, module, class, or even a function, should not have more than one reason to change ... separating things that change for different reasons, is one of the keys to creating designs that have an independently deployable component structure."
Responsibilities and "one reason to change" are related but not equivalent.
"Single responsibility" is an oversimplification of cohesion.
Ultimately it's about discovering the boundaries where there is
something that is easier to recognize than it is to author (and it's the fundamental idea behind Bounded Contexts).
Gather together those things that change for the same reason is a good starting point (to avoid the shotgun surgery and divergent change code smells).
Kevlin Henney rant
Decide what "clean code" means to you. It's subjective
My advice to anyone who aspire to write maintainable software is to read the Twelve Factor App at least twice. Itβs better the second time.
12factor.net/
I recommend "Good code, bad code" book.