- It's not about whether code works
- A vast majority of time is spent reading and understanding code.
- Should be readable and meaningful
- Should reducce cognotive load
- Should be concise and the point
- Should abcoud unintuitive names, complex nesting and big code blocks
- Should follow common best practices and patterns
- Should be fun to write and to maintain
Clean code is easy to understand --- Dirty code is not
-
Names
- Variales
- Functions
- Classes
-
Structure & comments
- Code formatting
- Good & Bad comments
-
Functions
- Length
- Parameters
-
Conditionals & Error Handling
- Deep nesting
- Missing error handling
-
Classes & Data Structures
- Missing Distinction
- Bloated(big) classes
-
Solutions
- Rules & Concepts
- Patterns & Principles
- Test-Driven development
- Write code which is readable & easy to understand
- Patterns & principles
- Write code which is maantainable and extensible
- How to write the code
- Focus on singles problems / files
- Where to write which code
- Focus on the project as a whole
- Refactoring today is work you save tomorrow
- A codebase can only survive and stay maintanable it it's continuously improved and redactored
- Pro tip: Whenevver you add something new, try to improve existing code along the way
- Parameters
- function Length, abstraction levels & Splitting funcions
What makes up a function?
function add(n1, n2){
return n1 + n2;
}
// call the function
add(1, 2);
Keep the number of Parameters low
None parameters = user.save(), esasy to understand. easy to call, best possible option
1 parameter = log(message), easy to understand, easy to call, very good options
2 parameters = Point(10, 20), decent to understand, acceptable to call, use with caution
3 parameters = cal(5, 10, 'add') Challenging to understand, challenging to call, avoid if possible
more 3 parameters - coords(12, 2, 34, 5), difficult to read & understand. difficul to call, avoid
Try to avoid output arguments - especially if they are unexpected
Note important: Functions should be small, functions should dp exactly one thing One thing
Functions should do work one level of abstraction below their name
Try not to mix levels of Abstraction
Extract code that works on the same functionality
Extract code that requires more interpretation that the surrounding code
Don't write the same code more than once
- Split function reasonbly
- Being as granular as possible won't automatically improve readability
- The same input always yields the same output
- No side effects
- A side effect is an operation which does not just act on functions inputs and change the function output but which instead changes the overall system / program state.
- Side effects are not automatically bad - we do need them in our programs. But unexpected side effects should be avoided
- Your functions should not have any unexpected side effects
- If you have / need a side effect
- Choose a function name which implies it
- Move the side effect into another function / place
// pure function
function generatId(iser){
return 'id' + user
}
// No pure funtion
function generatId(iser){
return 'id' + user + Math.randon()
}
can you easily test a function?
- Yes = great
- No = Consider spliting it
- Avoid deep nesting
- Using factory functions and polymorphism
- Prefer posity checks
- Utilize errors
Throwing + handling errors can replace if statements and lead to more focused functions
Simple rulte: If something is an error -> Make it an error
There's one additional tweak you might want to make to the code.
At the moment, we always check for hard-coded string identifiers like "PAYPAL" or "CREDIT_CARD".
You typically want to avoid this - not primarily for readability reasons but to avoid errors (e.g. due to typos).
Hence it is a better practice to use globally defined enums - if your programming language supports that - or globally defined constants.
const TYPE_CREDIT_CARD = 'CREDIT_CARD';
// ...
if (transaction.type === TYPE_CREDIT_CARD) { ... }
By doing that, you define a value once (in a place which is easily found and editable) and you then reuse the constant / variable. Hence you can't introduce accidental typos in parts of your code and you also avoid repeating the hard-coded value over and over again. That in turn is helpful if you ever want to change the identifier => You then only need to change it once.
Clean code: Write code which is readable and easy to understand
Patterns and Principles: Write code which is maintainable and extensible
Object
- Private internal, properties, public API(methods)
- Contain your business logic in OOP
- Abstractions over concretins
Data Container / Data Structure
- Public internals/ properties alsmot not API
- Store and transport data
- Concretions only
The ability of an object to take on many forms.
Typically should prefer manu small classes over a few large classes.
- Classes should have a single responsibility SRP
How much are your class methods using the class properties?
-
Maximum Cohesion
- All methos each use all properties
- A highly cohesive objects
-
No Cohesion
- All methods don't use any class properties
- Data structure container with utility methods
Don't depend on the internal of "strangers" (other objects which you don't directly now).
Code in a method may only access direct internal (properties and methods) of:
- The object it belongs to.
- Object that are stored in properties of that object.
- Object which are received as method parameters.
- Object which are created in the method.
S = simple responsibility
- Classes should have a single responsibility - a class should not cchange for more that one reasion.
- Restricting classes to one ccore responsibility leads to smaller classes.
- Smaller classes tend to be easier to read.
O = Open / Close
- A class should be open for extension but close for modification.
- Extensibility ensures small class and help prevent code duplication(DRY)
- Smaller classes and DRY ccoe increase readability and maintainability
L = Liskov susbtitution
- Objects should be replaceable with instances of their subclasses without altering the behaior
I = Interface segregation
- Many client-specific interfaces are better than one general purpose interface
D = Dependency Inversion
- You should depend upon abstractions, not concretions.